From 08ffefecb0e6668c33533a139753bce85d0e792c Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 11:06:53 +0100 Subject: [PATCH 1/6] fix(entry-point): rename hatch entry point from 'decorators' to 'reqstool' The entry point key must match the hook name used in pyproject.toml ([tool.hatch.build.hooks.reqstool]). The previous 'decorators' key caused 'Unknown build hook: reqstool' errors for all users following the README. Also: - Fix addopts: remove broken '-m not slow or not integration' filter - Drop unused 'flaky'/'slow' markers; add 'integration' and 'e2e' markers - Add tests/integration/ and tests/e2e/ directory structure - Add e2e test: runs hatchling directly in an isolated venv to verify the build hook generates annotations.yml and bundles reqstool_config.yml - Fix CI: correct cov package name, add --junitxml, build wheel before tests --- .github/workflows/build.yml | 6 +- pyproject.toml | 6 +- tests/e2e/__init__.py | 0 .../reqstool_python_hatch_plugin/__init__.py | 0 .../test_build_e2e.py | 73 +++++++++++++++++++ tests/integration/__init__.py | 0 .../reqstool_python_hatch_plugin/__init__.py | 0 7 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 tests/e2e/__init__.py create mode 100644 tests/e2e/reqstool_python_hatch_plugin/__init__.py create mode 100644 tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/reqstool_python_hatch_plugin/__init__.py 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/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..f227c56 --- /dev/null +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -0,0 +1,73 @@ +# Copyright © LFV +import shutil +import subprocess +import sys +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}" 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 From fe00432f6b62df558ed08fdcc736987862a4028e Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 11:10:21 +0100 Subject: [PATCH 2/6] test: verify annotations.yml content contains expected REQ/SVC ids --- tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py index f227c56..5d5a269 100644 --- a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -71,3 +71,6 @@ def test_hatch_build_sdist_contains_reqstool_artifacts(): # 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" From df0ef6490f20fc249ef811eeea3b3a6154c336cb Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 11:11:56 +0100 Subject: [PATCH 3/6] style: apply black formatting to e2e test --- tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py index 5d5a269..6096d28 100644 --- a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -64,9 +64,7 @@ def test_hatch_build_sdist_contains_reqstool_artifacts(): 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}" - ) + 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" From 13d7a4563a7f2572a7ea3c023cae79ee0ad52432 Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 11:13:47 +0100 Subject: [PATCH 4/6] fix: remove unused sys import --- tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py index 6096d28..dede2a8 100644 --- a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -1,7 +1,6 @@ # Copyright © LFV import shutil import subprocess -import sys import tarfile import tempfile import venv From 303f5c3b01fbd0e7dd6b8cc5fa96c0fdbd1ea9ee Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 13:40:56 +0100 Subject: [PATCH 5/6] fix(ci): remove pull_request_target trigger from semantic PR check --- .github/workflows/check-semantic-pr.yml | 2 -- 1 file changed, 2 deletions(-) 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: From 00608d74ece74452bb10b0802440b14c14a834d8 Mon Sep 17 00:00:00 2001 From: Jimisola Laursen Date: Wed, 4 Mar 2026 13:59:56 +0100 Subject: [PATCH 6/6] fix(tests): move tarfile assertions inside with block --- tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py index dede2a8..4e24512 100644 --- a/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py +++ b/tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py @@ -61,9 +61,10 @@ def test_hatch_build_sdist_contains_reqstool_artifacts(): 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}" + 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"