diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d95d26..f50f4f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,11 +47,11 @@ jobs: # edge can't wedge the required context.) needs: changes # Every leg runs on GitHub-HOSTED runners now — the self-hosted NucBox Windows runners are RETIRED. - # The PRIVATE repo (wshallwshall) runs the UBUNTU leg only (its remaining Actions cost); the mirror - # (MEFORORG) runs the full ubuntu + windows-2022 + windows-2025 matrix, all FREE (hosted minutes are - # free on the public mirror). Hosted-everywhere is also safer: a self-hosted runner must never be - # reachable by a public repo (fork PR = RCE on the owner's LAN). `matrix.hosted` is the runner label - # set; the per-repo matrix (which drops the Windows legs on the private repo) is built in `changes`. + # This repo (MEFORORG) runs the full ubuntu + windows-2022 + windows-2025 matrix, all FREE (hosted + # minutes are free on a public repo); a FORK building its own pushes gets the UBUNTU leg only, to + # keep a contributor's personal CI cheap. Hosted-everywhere is also safer: a self-hosted runner must + # never be reachable by a public repo (fork PR = RCE on the owner's LAN). `matrix.hosted` is the + # runner label set; the per-repo matrix is built in `changes`. runs-on: ${{ matrix.hosted }} # Wall-clock backstop: bound each leg so a hung test fails in minutes, not the 6h default. Three # nested watchdogs catch a hang at increasing scope (#55): the pytest-timeout per-test cap (60s, @@ -61,11 +61,11 @@ jobs: timeout-minutes: ${{ matrix.job_timeout }} strategy: fail-fast: false - # Per-repo matrix from the `changes` job: ubuntu-only on the private repo (Windows runners retired), - # full ubuntu+windows matrix on the mirror. Each leg carries os / python-version / hosted (the - # runner labels, an array) + the timeout knobs. NB: the Windows legs no longer EXIST on the private - # repo, so drop `test (windows-2022/2025, py3.14)` from wshallwshall's REQUIRED checks — a - # required-but-absent context blocks the PR forever. + # Per-repo matrix from the `changes` job: the full ubuntu+windows matrix here, ubuntu-only on a + # fork's own pushes. Each leg carries os / python-version / hosted (the runner labels, an array) + + # the timeout knobs. NB: a fork *PR* against this repo still runs in THIS repo's context, so it + # gets the full matrix — the required `test (windows-2022/2025, py3.14)` contexts are always + # present on a PR here, and a required-but-absent context (which blocks a PR forever) can't occur. matrix: ${{ fromJSON(needs.changes.outputs.matrix) }} defaults: run: diff --git a/messagefoundry/scaffold.py b/messagefoundry/scaffold.py index aad1c83..d2ead02 100644 --- a/messagefoundry/scaffold.py +++ b/messagefoundry/scaffold.py @@ -162,7 +162,7 @@ def archive(msg): # type: ignore[no-untyped-def] - name: Verify SLSA build provenance before install env: GH_TOKEN: ${{ github.token }} - run: gh attestation verify dist-verify/messagefoundry-*.whl --repo wshallwshall/MessageFoundry + run: gh attestation verify dist-verify/messagefoundry-*.whl --repo MEFORORG/MessageFoundry check: needs: verify-engine diff --git a/scripts/security/vuln_metrics.py b/scripts/security/vuln_metrics.py index 80fbf4b..41cd94a 100644 --- a/scripts/security/vuln_metrics.py +++ b/scripts/security/vuln_metrics.py @@ -27,6 +27,7 @@ import base64 import csv import json +import os import re import statistics import subprocess @@ -264,7 +265,14 @@ def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) - p.add_argument("--repo", default="wshallwshall/MessageFoundry") + # Measure the repo the workflow actually runs in. vuln-metrics.yml invokes this with no --repo, so a + # hardcoded default silently decides what gets measured: it named the private vault, whose Dependabot + # PRs a public GITHUB_TOKEN cannot read — every KPI would have been computed over an empty window. + # $GITHUB_REPOSITORY makes it self-correcting; the literal is only for a local run outside Actions. + p.add_argument( + "--repo", + default=os.environ.get("GITHUB_REPOSITORY") or "MEFORORG/MessageFoundry", + ) p.add_argument("--limit", type=int, default=200, help="Dependabot PRs to scan (the window)") p.add_argument("--timeout", type=float, default=20.0) p.add_argument("--csv", default="docs/security/metrics/vuln-metrics.csv") diff --git a/tests/test_scaffold.py b/tests/test_scaffold.py index 396a2dd..5270161 100644 --- a/tests/test_scaffold.py +++ b/tests/test_scaffold.py @@ -65,9 +65,13 @@ def test_scaffold_writes_the_skeleton(tmp_path: Path) -> None: # repo variable for indexes that strip attestations; the check job gates on it (never on verify failure) assert "verify-engine:" in ci assert ( - "gh attestation verify dist-verify/messagefoundry-*.whl --repo wshallwshall/MessageFoundry" + "gh attestation verify dist-verify/messagefoundry-*.whl --repo MEFORORG/MessageFoundry" in ci ) + # The scaffolded gate must name the repo that BUILDS the release — attestations are minted by the + # public repo's release workflow, so a private-vault slug here verifies against something no + # adopter can read. Pin the negative too: the retired slug must never creep back in. + assert "wshallwshall" not in ci assert "vars.MEFOR_VERIFY_ENGINE != 'off'" in ci assert "needs: verify-engine" in ci assert "needs.verify-engine.result != 'failure'" in ci