diff --git a/.github/workflows/maint-coverage-guard.yml b/.github/workflows/maint-coverage-guard.yml index 5e42e15..224c1d6 100644 --- a/.github/workflows/maint-coverage-guard.yml +++ b/.github/workflows/maint-coverage-guard.yml @@ -181,11 +181,10 @@ jobs: ['success', 'neutral'].includes(run.conclusion || ''), ); const runsToProbe = successfulRuns.slice(0, 10); - const requiredCoverageArtifactNames = new Set([ - 'gate-coverage-trend', - 'gate-coverage-trend-history', - 'gate-coverage', - ]); + // The payload is the only input every successful Gate run produces. Trend artifacts + // are best-effort enrichment: their download steps continue on error and the guard + // already omits their CLI arguments when they are absent. + const requiredCoverageArtifactNames = new Set(['gate-coverage']); let candidate = null; for (const run of runsToProbe) { let artifacts = []; @@ -240,8 +239,7 @@ jobs: core.warning( [ 'Unable to locate a recent successful Gate workflow run with required', - 'coverage artifacts: gate-coverage-trend, gate-coverage-trend-history,', - 'gate-coverage.', + 'coverage payload artifact: gate-coverage.', ].join(' '), ); core.setOutput('run_id', ''); diff --git a/.gitignore b/.gitignore index 718dbac..dabc0a6 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,7 @@ Audits/ # --- python / tooling noise --- __pycache__/ *.py[cod] +*.egg-info/ .venv/ venv/ .pytest_cache/ diff --git a/tests/test_coverage_guard_config.py b/tests/test_coverage_guard_config.py new file mode 100644 index 0000000..e435d34 --- /dev/null +++ b/tests/test_coverage_guard_config.py @@ -0,0 +1,34 @@ +"""Regression checks for the Gate-to-coverage-guard artifact contract.""" + +from __future__ import annotations + +import re + +import paths + +WORKFLOW = paths.REPO_ROOT / ".github" / "workflows" / "maint-coverage-guard.yml" + + +def test_coverage_guard_requires_only_the_gate_payload(): + """Optional trend downloads must not disqualify an otherwise usable Gate payload.""" + workflow = WORKFLOW.read_text(encoding="utf-8") + match = re.search( + r"const requiredCoverageArtifactNames = new Set\((.*?)\);", + workflow, + re.S, + ) + assert match, "coverage-guard artifact discovery set is missing" + + required = set(re.findall(r"'([^']+)'", match.group(1))) + assert required == {"gate-coverage"}, ( + "the coverage payload is mandatory; trend/history artifacts are best-effort inputs " + "handled by the guard runner when present" + ) + + for step_name, artifact in ( + ("Download coverage trend artifact", "gate-coverage-trend"), + ("Download coverage trend history artifact", "gate-coverage-trend-history"), + ): + block = workflow.split(f"- name: {step_name}", 1)[1].split("\n - name:", 1)[0] + assert f"name: {artifact}" in block, f"{step_name} no longer requests {artifact}" + assert "continue-on-error: true" in block, f"{artifact} must remain optional" diff --git a/tests/test_repo_artifact_hygiene.py b/tests/test_repo_artifact_hygiene.py index e8f5db5..218fe31 100644 --- a/tests/test_repo_artifact_hygiene.py +++ b/tests/test_repo_artifact_hygiene.py @@ -135,6 +135,19 @@ def test_no_emitted_artifact_is_tracked(): ) +def test_generated_package_metadata_is_ignored_and_untracked(): + """Packaging metadata generated by autofix must not become a source-tree change.""" + require_git() + artifact = "src/UNKNOWN.egg-info/PKG-INFO" + assert is_ignored(artifact), f"{artifact} is not ignored" + proc = git("ls-files", "--", "*.egg-info/*") + assert proc.returncode == 0, f"git ls-files failed: {proc.stderr.strip()}" + assert not proc.stdout.strip(), ( + "generated package metadata is tracked; remove it from the index and keep *.egg-info/ " + "in .gitignore so autofix cannot add it to unrelated pull requests" + ) + + @pytest.mark.parametrize("path", MUST_STAY_COMMITTABLE) def test_sources_and_docs_are_not_swallowed(path: str): require_git()