Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/check-semantic-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down
Empty file added tests/e2e/__init__.py
Empty file.
Empty file.
74 changes: 74 additions & 0 deletions tests/e2e/reqstool_python_hatch_plugin/test_build_e2e.py
Original file line number Diff line number Diff line change
@@ -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"
Empty file added tests/integration/__init__.py
Empty file.
Empty file.