diff --git a/.github/workflows/reference-implementation-supply-chain.yml b/.github/workflows/reference-implementation-supply-chain.yml new file mode 100644 index 00000000..1142f864 --- /dev/null +++ b/.github/workflows/reference-implementation-supply-chain.yml @@ -0,0 +1,98 @@ +# Runs the AGT supply-chain checks that nothing else invokes. +# +# verify-pin.sh clones agt.lock's pinned commit and byte-diffs the vendored .rego +# files against it. A mismatch fails the job. It skips the other files in policy/lib, +# including data.json, which holds the egress allowlist. +# +# run-upstream-watch.sh diffs AGT's contract surfaces between the pinned commit and +# upstream main, and prints a report. Drift exits 0 because moving the pin is a human +# call. The job fails only when the watch could not run. The report goes to the job +# summary. +# +# verify:zero-diff is left out. It compares against a branch that does not exist in +# this repository and exits 1 by design here. +# +# Both scripts clone from GitHub, so this is its own workflow. The test job in +# reference-implementation.yml should not depend on the network. +# +# No workflow expression appears inside a `run:` block. +name: Reference implementation supply chain + +on: + pull_request: + paths: + - "reference-implementations/agt/agt.lock" + - "reference-implementations/agt/policy/**" + - "reference-implementations/agt/scripts/verify-pin.sh" + - "reference-implementations/agt/scripts/run-upstream-watch.sh" + - ".github/workflows/reference-implementation-supply-chain.yml" + push: + branches: ["main", "integration"] + paths: + - "reference-implementations/agt/agt.lock" + - "reference-implementations/agt/policy/**" + - "reference-implementations/agt/scripts/verify-pin.sh" + - "reference-implementations/agt/scripts/run-upstream-watch.sh" + - ".github/workflows/reference-implementation-supply-chain.yml" + schedule: + # Weekly, an hour after zizmor. Upstream moves whether or not this tree changes. + - cron: "0 7 * * 1" + workflow_dispatch: + +permissions: {} + +concurrency: + group: reference-implementation-supply-chain-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + agt-supply-chain: + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + defaults: + run: + working-directory: reference-implementations/agt + steps: + - name: Check out the repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # agt.lock comes from the pull request, and both scripts clone whatever agt_repo + # names. The expected value lives here because /.github/ needs a project lead's + # review. This checks the repository only. agt_ref still comes from the pull + # request, so a reviewer has to read every change to it. + - name: Check that agt.lock names the upstream repository + run: | + set -euo pipefail + expected="https://github.com/microsoft/agent-governance-toolkit" + actual="$(jq -r '.agt_repo' agt.lock)" + if [ "$actual" != "$expected" ]; then + echo "agt.lock names $actual, expected $expected" >&2 + exit 1 + fi + + # Same pin as reference-implementation.yml. + - name: Install Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + with: + bun-version: "1.3.11" + + - name: Install dependencies from the lockfile + run: bun install --frozen-lockfile + + # Both scripts clean up with `trash` and refuse to start without it. Same pin + # as reference-implementation.yml. + - name: Install the trash CLI the scripts require + run: bun add --global trash-cli@7.2.0 + + - name: Verify the vendored bundle against the pinned commit + run: bash scripts/verify-pin.sh + + # pipefail so a failed watch fails the step instead of tee's exit status. + - name: Report drift against upstream main + run: | + set -euo pipefail + bash scripts/run-upstream-watch.sh | tee -a "$GITHUB_STEP_SUMMARY" diff --git a/reference-implementations/agt/packages/conformance/README.md b/reference-implementations/agt/packages/conformance/README.md index 62087828..9081b9ec 100644 --- a/reference-implementations/agt/packages/conformance/README.md +++ b/reference-implementations/agt/packages/conformance/README.md @@ -48,7 +48,7 @@ bun run watch:upstream This runs `scripts/run-upstream-watch.sh`, which clones AGT twice, once at the pinned commit and once at `main`, and hands the two paths to the runner as `PINNED_AGT_CLONE` and `UPSTREAM_AGT_CLONE`. The runner diffs AGT's eight declared contract surfaces field by field, validates the Guardian's policy input against both schema versions, and reports which tools the two hookmaps declare against the manifest's registry. -The watch reports. It never fails a build. Nothing reaches this tree until a human moves the pin in `agt.lock`. +The watch reports drift, and drift never fails a build. A run that could not finish does fail the job. Nothing reaches this tree until a human moves the pin in `agt.lock`. ## Use it in-process