diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7b8cc4..ab3cc0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,10 @@ jobs: python-version: "3.13" - name: Install dependencies run: pip install hatch - - name: Run unit and integrations tests - run: hatch run dev:pytest --cov=reqstool-python-decorators --cov-report=xml --cov-report=html + - name: Build wheel (used by e2e tests via PIP_FIND_LINKS) + run: hatch build --target wheel + - name: Run tests + run: hatch run dev:pytest --junitxml=build/junit.xml --cov=reqstool_python_hatch_plugin --cov-report=xml:build/coverage.xml - name: Build project run: hatch build # Upload artifacts for later use diff --git a/.github/workflows/check-semantic-pr.yml b/.github/workflows/check-semantic-pr.yml index 5bacd57..459b868 100644 --- a/.github/workflows/check-semantic-pr.yml +++ b/.github/workflows/check-semantic-pr.yml @@ -2,8 +2,6 @@ name: Check Semantic PR on: pull_request: types: [opened, edited, synchronize, reopened] - pull_request_target: - types: [opened, edited, synchronize, reopened] jobs: check: diff --git a/pyproject.toml b/pyproject.toml index 6f7eada..a3357cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ Source = "https://github.com/reqstool/reqstool-python-hatch-plugin.git" Documentation = "https://github.com/reqstool/reqstool-python-hatch-plugin.git" [project.entry-points.hatch] -decorators = "reqstool_python_hatch_plugin.hooks" +reqstool = "reqstool_python_hatch_plugin.hooks" [tool.hatch.version] source = "vcs" @@ -52,15 +52,13 @@ addopts = [ "-s", "--import-mode=importlib", "--log-cli-level=DEBUG", - '-m not slow or not integration', ] pythonpath = [".", "src", "tests"] testpaths = ["tests"] norecursedirs = ["tests/fixtures"] markers = [ - "flaky: tests that can randomly fail through no change to the code", - "slow: marks tests as slow (deselect with '-m \"not slow\"')", "integration: tests that require external resources", + "e2e: end-to-end tests that run the full pipeline locally", ] [tool.black] diff --git a/tests/e2e/__init__.py b/tests/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/reqstool_python_hatch_plugin/__init__.py b/tests/e2e/reqstool_python_hatch_plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py new file mode 100644 index 0000000..4e24512 --- /dev/null +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -0,0 +1,74 @@ +# Copyright © LFV +import shutil +import subprocess +import tarfile +import tempfile +import venv +from pathlib import Path + +import pytest + +FIXTURE_DIR = Path(__file__).parents[2] / "fixtures" / "test_project" +DIST_DIR = Path(__file__).parents[3] / "dist" + +# The hatch plugin appends reqstool_config.yml to the tar.gz and generates +# annotations.yml on disk. requirements.yml and software_verification_cases.yml +# are included via the sdist include config (docs/reqstool/**). +EXPECTED_IN_TARBALL = [ + "reqstool_config.yml", + "requirements.yml", + "software_verification_cases.yml", +] + + +@pytest.mark.e2e +def test_hatch_build_sdist_contains_reqstool_artifacts(): + """hatch build (sdist) triggers the reqstool hook and bundles all artifacts. + + Runs hatchling directly inside an isolated venv that has the local plugin wheel + pre-installed, bypassing hatch's own build-env management (which can't resolve + @ file:// hook dependencies reliably across pip/uv versions). + """ + wheels = sorted(DIST_DIR.glob("reqstool_python_hatch_plugin-*.whl")) + if not wheels: + pytest.skip("No local wheel found — run `hatch build --target wheel` first") + + with tempfile.TemporaryDirectory() as tmpdir: + tmp_project = Path(tmpdir) / "test_project" + shutil.copytree(FIXTURE_DIR, tmp_project, ignore=shutil.ignore_patterns("dist", "build", "__pycache__")) + + # Build an isolated venv with hatchling + the local plugin wheel. + # We call hatchling directly so we fully control what's installed. + venv_dir = Path(tmpdir) / "build-venv" + venv.create(str(venv_dir), with_pip=True) + python = str(venv_dir / "bin" / "python") + + subprocess.run( + [python, "-m", "pip", "install", "--quiet", "hatchling", str(wheels[-1])], + check=True, + ) + + result = subprocess.run( + [python, "-m", "hatchling", "build", "--target", "sdist"], + cwd=tmp_project, + capture_output=True, + text=True, + ) + assert result.returncode == 0, f"hatchling build failed:\n{result.stderr}" + + tarballs = sorted((tmp_project / "dist").glob("mypackage-*.tar.gz")) + assert tarballs, "No tarball found in dist/" + + with tarfile.open(tarballs[-1]) as tf: + names = tf.getnames() + for expected in EXPECTED_IN_TARBALL: + assert any( + expected in n for n in names + ), f"{expected!r} missing from {tarballs[-1].name};\ngot: {names}" + + # annotations.yml is generated on disk (not bundled in the tarball) + annotations_file = tmp_project / "build" / "reqstool" / "annotations.yml" + assert annotations_file.exists(), f"annotations.yml not generated at {annotations_file}" + annotations_content = annotations_file.read_text() + assert "REQ_001" in annotations_content, "annotations.yml missing REQ_001" + assert "SVC_001" in annotations_content, "annotations.yml missing SVC_001" diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/reqstool_python_hatch_plugin/__init__.py b/tests/integration/reqstool_python_hatch_plugin/__init__.py new file mode 100644 index 0000000..e69de29