diff --git a/.github/workflows/pr_code_changes.yaml b/.github/workflows/pr_code_changes.yaml index c63cd69b..eb825e24 100644 --- a/.github/workflows/pr_code_changes.yaml +++ b/.github/workflows/pr_code_changes.yaml @@ -81,7 +81,7 @@ jobs: - name: Smoke-import core modules run: python -c "import policyengine; from policyengine.core import Dataset, Policy, Simulation; from policyengine.outputs import aggregate, poverty, inequality; print('import OK')" BundleVerification: - name: Verify bundle metadata + name: Verify release inputs and bundle packages # Private manifest credentials are available only to trusted repository PRs. if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest @@ -97,8 +97,19 @@ jobs: uses: actions/setup-python@v6 with: python-version: '3.13' - - name: Check derived bundle metadata - run: python scripts/bundle.py check + - name: Install package for TRACE verification + run: uv pip install -e . h5py --system + - name: Check reviewed release inputs + env: + HUGGING_FACE_TOKEN: ${{ secrets.HUGGING_FACE_TOKEN }} + run: | + if [[ -z "${HUGGING_FACE_TOKEN:-}" ]]; then + echo "::error::Full private release verification requires HUGGING_FACE_TOKEN" + exit 1 + fi + python scripts/check_release_credentials.py + python scripts/bundle.py check --published-spm --include-tros --strict-tros + python scripts/release_lock.py - name: Install bundle package scaffold run: | uv pip install -e ".[models]" --system @@ -121,13 +132,6 @@ jobs: PY - name: Check installed package consistency run: python -m pip check - # Only the reviewed registry lock is a pull-request concern. The - # read-only credential, published-measurement and TRACE sidecar gates - # are release gates: the release workflow regenerates the sidecars and - # publishes the wheels immediately before checking them, so they cannot - # be satisfied from a pull request. They run in push.yaml instead. - - name: Check the reviewed registry lock - run: python scripts/release_lock.py - name: Verify bundle packages run: policyengine bundle verify --country us --country uk --packages-only --json Test: diff --git a/changelog.d/547.fixed.md b/changelog.d/547.fixed.md new file mode 100644 index 00000000..f848df50 --- /dev/null +++ b/changelog.d/547.fixed.md @@ -0,0 +1 @@ +Correct the US and UK TRACE composition fingerprints after the PolicyEngine Core 3.32.10 bundle update, and run the authenticated TRACE comparison on same-repository pull requests. diff --git a/src/policyengine/data/bundle/uk.trace.tro.jsonld b/src/policyengine/data/bundle/uk.trace.tro.jsonld index 8cbe5b58..66d72e14 100644 --- a/src/policyengine/data/bundle/uk.trace.tro.jsonld +++ b/src/policyengine/data/bundle/uk.trace.tro.jsonld @@ -102,7 +102,7 @@ "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "323677508f58ce65912d6b90f485952e948f0050817be2f8fa093ede0df746ff" + "trov:sha256": "33eb1f57385df5ed4526990a14563029aabe1e57c1900fd1bd4c4e0238ae1e5a" } }, "trov:hasPerformance": { diff --git a/src/policyengine/data/bundle/us.trace.tro.jsonld b/src/policyengine/data/bundle/us.trace.tro.jsonld index 7e32fd21..1b7bf451 100644 --- a/src/policyengine/data/bundle/us.trace.tro.jsonld +++ b/src/policyengine/data/bundle/us.trace.tro.jsonld @@ -101,7 +101,7 @@ "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "0120221ad564a13e0576636f9e81da6163d578ca7cf6fce1923efeaff6e1d7cd" + "trov:sha256": "1a978ebb6d8843fda08a599d33d38139dfe2b1544f937af2b7b5031332548bd1" } }, "trov:hasPerformance": { diff --git a/tests/test_release_tro_generation.py b/tests/test_release_tro_generation.py index 05f8277e..69ffd6a8 100644 --- a/tests/test_release_tro_generation.py +++ b/tests/test_release_tro_generation.py @@ -13,6 +13,7 @@ import requests from policyengine.provenance import manifest +from policyengine.provenance.trace import compute_trace_composition_fingerprint ROOT = Path(__file__).resolve().parents[1] FIXTURES = ROOT / "tests" / "fixtures" / "release_tros" @@ -21,6 +22,27 @@ import generate_trace_tros as generator # noqa: E402 +@pytest.mark.parametrize("country", ["us", "uk"]) +def test_bundled_tro_composition_fingerprint_matches_artifacts(country): + tro_path = ( + ROOT + / "src" + / "policyengine" + / "data" + / "bundle" + / f"{country}.trace.tro.jsonld" + ) + tro = json.loads(tro_path.read_text())["@graph"][0] + composition = tro["trov:hasComposition"] + artifact_hashes = [ + artifact["trov:sha256"] for artifact in composition["trov:hasArtifact"] + ] + + assert composition["trov:hasFingerprint"]["trov:sha256"] == ( + compute_trace_composition_fingerprint(artifact_hashes) + ) + + @pytest.fixture def release(monkeypatch, tmp_path): package = tmp_path / "policyengine" @@ -300,15 +322,18 @@ def test_release_workflows_gate_complete_inputs_and_lock(): assert commands.index( "python scripts/check_release_credentials.py" ) < commands.index("check --published-spm") - # The pull-request job checks only the reviewed registry lock. The - # read-only credential, published-measurement and TRACE sidecar gates - # depend on the release workflow regenerating sidecars and publishing - # wheels first, so a pull request can never satisfy them. + # Same-repository pull requests have the read-only credential required to + # run the exact pre-versioning release check before merge. pr_commands = "\n".join( step.get("run", "") for step in pr["jobs"]["BundleVerification"]["steps"] ) assert "python scripts/release_lock.py" in pr_commands - assert "--published-spm" not in pr_commands + assert "check --published-spm --include-tros --strict-tros" in pr_commands + assert 'if [[ -z "${HUGGING_FACE_TOKEN:-}" ]]' in pr_commands + assert "python scripts/check_release_credentials.py" in pr_commands + assert pr_commands.index( + "python scripts/check_release_credentials.py" + ) < pr_commands.index("check --published-spm") commands = "\n".join( step.get("run", "") for step in push["jobs"]["Versioning"]["steps"] )