Evidence Dashboard Publish (GP5)
Evidence Dashboard Publish (GP5)
The dashboard-publish pipeline is the repo’s first GitHub Pages surface.
On every merge to main, on demand, and after every successful
evidence-ingest (GP2) run (its trigger-dashboard job
dispatches this workflow), it regenerates the interim-evidence dashboard from
the source-keyed evidence tree in GCS and deploys the static site to GitHub
Pages. It is the consumer end of the chain whose producer is evidence-ingest:
ingest writes the tree, publish renders and serves it. (For the full GPn
stage map of the evidence-corroboration pipeline, see
/aicr/contributor-guide/evidence-ingest.)
The pages concurrency group (cancel-in-progress: false) serializes to one
running build plus one queued build, the newest dispatch superseding the
queued one — so the burst of ingests from a multi-cell nightly UAT batch
coalesces into a single rebuild instead of a backlog.
Fern publishes the product docs to docs.nvidia.com via publish-fern-docs.yml;
that is a separate surface. This workflow is the only one that deploys to
GitHub Pages.
Pipeline
The byte-deterministic generator (no clock, no random — see
tools/corroborate) makes the publish a
straight deploy: there is no drift-PR for the site. The in-job determinism
gate runs the generator twice and diffs the output, so a non-reproducible
build fails loudly instead of shipping.
Identity and fork safety
Two jobs, neither holding the other’s scope:
buildauthenticates to GCS with a read-only (objectViewer) identity impersonated through the existinggithub-actions-poolWIF federation, syncs the tree, runs the generator, and uploads the Pages artifact. It holdsid-token: write(for GCS WIF) but notpages: write. The read SA (evidence-read@eidosx, provisioned byinfra/uat-gcp-account/evidence-dashboard.tf) is deliberately not the shared project-widegithub-actions@eidosxSA and not the GP2evidence-publishwrite SA; a Pages publish must never run with GCS write or admin scope.deployholdspages: write+id-token: writeand deploys the artifact to thegithub-pagesenvironment. It holds no GCS credentials, and additionally runs only onrefs/heads/main— aworkflow_dispatchrun on a branch still builds (a preview) but never publishes a branch build over the single live site.
Every job is gated if: github.repository == 'nvidia/aicr', so a fork never
obtains GCS credentials or Pages write.
Tests
tools/corroborate/publish_workflow_test.go pins the contract that
actionlint cannot express: the deploy job declares pages: write +
id-token: write and targets the github-pages environment; the canonical
publish chain (configure-pages → upload-pages-artifact → deploy-pages)
is present; the build job uses the read-only identity (never the shared or
write SA); the determinism gate is wired; and every job carries the
canonical-repo guard. Workflow syntax is covered by the repo actionlint gate
in merge-gate.yaml.
Forward limitations
- The read SA’s impersonation is repository-scoped (the shared
github-actions-poolprovider, owned byinfra/demo-api-server, maps only the repository attribute). It is least-privilege on the resource side (objectVieweron one bucket); GP3’sinfra/evidence-dashboardmay tighten the subject condition further. - The build passes
-allowlist recipes/evidence/allowlist.yamlto the generator, re-deriving each source’s class from its verified signer against the in-tree GP1 allowlist — defense in depth on top of the class GP2 baked intometa.jsonat ingest time.pkg/corroborate’s loader delegates to the sharedpkg/evidence/allowlistparser, so it reads the canonicalidentityPattern/sourceschema directly (#1505). - The Pages site is served at the custom domain
validation.aicr.run: the publish workflow reasserts it on every run by writing aCNAMEfile into the site artifact (a deploy whose artifact omitsCNAMEwould otherwise silently clear the setting).