diff --git a/.github/workflows/attest.yml b/.github/workflows/attest.yml index 49b9fa1..8782a27 100644 --- a/.github/workflows/attest.yml +++ b/.github/workflows/attest.yml @@ -78,21 +78,27 @@ jobs: - name: Build attester image (digest-pinned base, pinned mcp-warden) run: docker build -q --build-arg "WARDEN_REF=$WARDEN_REF" -t attester:local . + # The matrix entry is only the coordinate; spawn/env come from the checked-out + # targets.yaml so dummy credentials never pass through a job output. - name: Attest one target (pre-fetch → sandboxed capture → sign) env: - TARGET_JSON: ${{ toJSON(matrix.target) }} + COORD: ${{ matrix.target }} IMAGE: attester:local SIGN: "1" OUT: ${{ github.workspace }}/out CACHE: ${{ github.workspace }}/cache - run: bash tools/attest_one.sh + run: | + pip install --quiet "pyyaml==6.0.3" + TARGET_JSON=$(python3 tools/plan.py --attester "$ATTESTER_ID" --one "$COORD") + export TARGET_JSON + bash tools/attest_one.sh - name: Artifact name id: name env: - TARGET_JSON: ${{ toJSON(matrix.target) }} + COORD: ${{ matrix.target }} run: | - n=$(printf '%s' "$TARGET_JSON" | python3 -c 'import json,sys,re;print(re.sub(r"[^A-Za-z0-9._-]","_",json.load(sys.stdin)["coordinate"]))') + n=$(printf '%s' "$COORD" | python3 -c 'import sys,re;print(re.sub(r"[^A-Za-z0-9._-]","_",sys.stdin.read()))') echo "name=lock-$n" >> "$GITHUB_OUTPUT" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 diff --git a/tools/plan.py b/tools/plan.py index bf157ef..d1d5313 100755 --- a/tools/plan.py +++ b/tools/plan.py @@ -5,7 +5,11 @@ a second observation of the same coordinate by the same attester is not new evidence. -Usage: plan.py [--attester ID] [--all] +Usage: plan.py [--attester ID] [--all] [--limit N] -> JSON list of coordinates (the matrix) + plan.py --one -> that target's full JSON (spawn/env/lock_rel) + +The matrix carries ONLY coordinates: GitHub refuses to emit a job output that looks +like it contains a secret, and the dummy credentials in `env` trip that heuristic. """ from __future__ import annotations @@ -35,6 +39,7 @@ def main() -> int: ap.add_argument("--attester", default="dse-nightly") ap.add_argument("--all", action="store_true", help="ignore existing locks (dry runs)") ap.add_argument("--limit", type=int, default=0, help="attest at most N targets (0 = all)") + ap.add_argument("--one", metavar="COORDINATE", help="print the full entry for one coordinate") ns = ap.parse_args() doc = yaml.safe_load((ROOT / "targets.yaml").read_text(encoding="utf-8")) out = [] @@ -42,7 +47,7 @@ def main() -> int: if t.get("skip"): continue d = lock_dir(t["coordinate"]) - if not ns.all and (d / f"{ns.attester}.lock").exists(): + if not ns.all and not ns.one and (d / f"{ns.attester}.lock").exists(): continue out.append({ "coordinate": t["coordinate"], @@ -50,9 +55,15 @@ def main() -> int: "env": t.get("env", {}), "lock_rel": str((d / f"{ns.attester}.lock").relative_to(ROOT)), }) + if ns.one: + match = [t for t in out if t["coordinate"] == ns.one] + if not match: + raise SystemExit(f"no pending target {ns.one!r}") + json.dump(match[0], sys.stdout, separators=(",", ":")) + return 0 if ns.limit > 0: out = out[: ns.limit] - json.dump(out, sys.stdout, separators=(",", ":")) + json.dump([t["coordinate"] for t in out], sys.stdout, separators=(",", ":")) return 0