feat(ci): GP5 GitHub Pages publish for evidence dashboard#1504
Conversation
📝 WalkthroughWalkthroughAdds a GitHub Actions workflow that publishes the evidence dashboard to GitHub Pages. The build job uses a dedicated read-only GCP service account to sync evidence data from GCS, runs the generator twice, compares outputs, and uploads the site artifact. A deploy job publishes the artifact to the Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 markdownlint-cli2 (0.22.1)docs/contributor/evidence-dashboard-publish.mdmarkdownlint-cli2 wrapper config was not available before execution docs/contributor/evidence-ingest.mdmarkdownlint-cli2 wrapper config was not available before execution infra/uat-gcp-account/README.mdmarkdownlint-cli2 wrapper config was not available before execution Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/evidence-dashboard-publish.yaml:
- Around line 40-43: The Pages publish workflow currently allows manual runs on
non-main refs, so the build/deploy path can authenticate and publish a branch
build. Update the evidence-dashboard-publish workflow by adding a github.ref
check for refs/heads/main to the build/deploy jobs (or restrict the deploy job
only) while keeping workflow_dispatch for build-only previews if needed, using
the existing job definitions that currently only gate on github.repository.
In `@docs/contributor/evidence-dashboard-publish.md`:
- Around line 16-33: The Markdown diagram fence in the contributor publish doc
is missing a language tag, which triggers MD040. Update the fenced block around
the evidence-dashboard publish flow to use an explicit language label such as
text, keeping the diagram content unchanged. Locate the fenced diagram by the
evidence-dashboard publish steps and apply the language-tagged opening fence
there.
- Around line 78-83: Remove the outdated allowlist-schema note in the
contributor evidence dashboard publish docs and rewrite the paragraph so it
matches the shared allowlist format described in evidence ingest. Update the
wording around the generator and `pkg/corroborate` loader to reflect the same
`issuer` + `identity` anchored-regex schema used elsewhere, and avoid mentioning
the conflicting `identityPattern`/`source` fields so the docs stay consistent.
In `@tools/corroborate/publish_workflow_test.go`:
- Around line 109-125: The action contract test in publish_workflow_test.go is
stripping the `@sha` pin by keying only on the repo path, so it won’t catch
unpinned tags or branches. Update the test around the wf.Jobs/step.Uses loop to
assert both the expected action repo paths and that each required uses value is
pinned to a full commit SHA, rather than normalizing with strings.SplitN. Ensure
the checks still cover actions/configure-pages, actions/upload-pages-artifact,
and actions/deploy-pages.
- Around line 88-101: The permission assertions in publish_workflow_test are too
narrow and can miss future extra scopes, so tighten the checks in the workflow
test by comparing the full permissions maps for the top level, build job, and
deploy job instead of only selected keys. Update the assertions around
wf.Permissions, wf.job(t, "build"), and wf.job(t, "deploy") to validate exact
maps and keep the least-privilege split intact, including the duplicate check
site referenced in the later test block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 87c609d9-3fc6-43c0-afbd-fdb62b3737c7
📒 Files selected for processing (8)
.github/workflows/evidence-dashboard-publish.yamldocs/contributor/evidence-dashboard-publish.mddocs/contributor/evidence-ingest.mdinfra/uat-gcp-account/README.mdinfra/uat-gcp-account/evidence-dashboard.tfinfra/uat-gcp-account/outputs.tfinfra/uat-gcp-account/variables.tftools/corroborate/publish_workflow_test.go
| ``` | ||
| GCS source-keyed tree (read-only) | ||
| │ | ||
| ▼ | ||
| rsync gs://<bucket>/results → evidence/results | ||
| │ | ||
| ▼ | ||
| generate corroborate -in evidence -out _site (×2, byte-identical) | ||
| │ | ||
| ▼ | ||
| determinism gate diff -r _site _site_check (fails closed) | ||
| │ | ||
| ▼ | ||
| upload-pages-artifact (_site) | ||
| │ | ||
| ▼ | ||
| deploy-pages → github-pages environment | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Label the diagram fence so markdownlint passes.
The opening fence needs a language tag (text is fine) or MD040 will keep firing.
Proposed fix
-```
+```text📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| GCS source-keyed tree (read-only) | |
| │ | |
| ▼ | |
| rsync gs://<bucket>/results → evidence/results | |
| │ | |
| ▼ | |
| generate corroborate -in evidence -out _site (×2, byte-identical) | |
| │ | |
| ▼ | |
| determinism gate diff -r _site _site_check (fails closed) | |
| │ | |
| ▼ | |
| upload-pages-artifact (_site) | |
| │ | |
| ▼ | |
| deploy-pages → github-pages environment | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/contributor/evidence-dashboard-publish.md` around lines 16 - 33, The
Markdown diagram fence in the contributor publish doc is missing a language tag,
which triggers MD040. Update the fenced block around the evidence-dashboard
publish flow to use an explicit language label such as text, keeping the diagram
content unchanged. Locate the fenced diagram by the evidence-dashboard publish
steps and apply the language-tagged opening fence there.
Source: Linters/SAST tools
| - The build does not pass `-allowlist` to the generator. Each source's class | ||
| is already re-derived from its verified signer at GP2 ingest time and baked | ||
| into `meta.json` (the trust gate); the generator's own allowlist | ||
| re-derivation is deferred until `pkg/corroborate`'s loader is reconciled with | ||
| the GP1 allowlist schema (`recipes/evidence/allowlist.yaml` uses | ||
| `identityPattern`/`source`; the loader still expects an `identity` field). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the stale allowlist-schema note.
This paragraph now contradicts docs/contributor/evidence-ingest.md, which already describes the shared allowlist as issuer + identity entries with anchored regexes. Please update the wording so both docs describe the same schema.
Proposed fix
- re-derivation is deferred until `pkg/corroborate`'s loader is reconciled with
- the GP1 allowlist schema (`recipes/evidence/allowlist.yaml` uses
- `identityPattern`/`source`; the loader still expects an `identity` field).
+ re-derivation is deferred until `pkg/corroborate`'s loader matches the
+ shared `recipes/evidence/allowlist.yaml` schema described in
+ `evidence-ingest.md`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - The build does not pass `-allowlist` to the generator. Each source's class | |
| is already re-derived from its verified signer at GP2 ingest time and baked | |
| into `meta.json` (the trust gate); the generator's own allowlist | |
| re-derivation is deferred until `pkg/corroborate`'s loader is reconciled with | |
| the GP1 allowlist schema (`recipes/evidence/allowlist.yaml` uses | |
| `identityPattern`/`source`; the loader still expects an `identity` field). | |
| - The build does not pass `-allowlist` to the generator. Each source's class | |
| is already re-derived from its verified signer at GP2 ingest time and baked | |
| into `meta.json` (the trust gate); the generator's own allowlist | |
| re-derivation is deferred until `pkg/corroborate`'s loader matches the | |
| shared `recipes/evidence/allowlist.yaml` schema described in | |
| `evidence-ingest.md`. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/contributor/evidence-dashboard-publish.md` around lines 78 - 83, Remove
the outdated allowlist-schema note in the contributor evidence dashboard publish
docs and rewrite the paragraph so it matches the shared allowlist format
described in evidence ingest. Update the wording around the generator and
`pkg/corroborate` loader to reflect the same `issuer` + `identity`
anchored-regex schema used elsewhere, and avoid mentioning the conflicting
`identityPattern`/`source` fields so the docs stay consistent.
Source: Linked repositories
Add the repo's first GitHub Pages pipeline: on merge to main (and workflow_dispatch) it syncs the source-keyed corroboration evidence tree from GCS read-only, runs the GP4 corroborate generator to emit the deterministic static site, verifies the build is reproducible, and publishes via configure-pages -> upload-pages-artifact -> deploy-pages. - New .github/workflows/evidence-dashboard-publish.yaml: build job holds only id-token (GCS WIF, read-only SA) and runs an in-job determinism gate (two builds, diff); deploy job holds pages: write on the github-pages environment. Every job gated to the canonical repo for fork safety. The generator runs without -allowlist for now: class is already re-derived from the verified signer at GP2 ingest and baked into meta.json (re-derivation deferred until the corroborate loader is reconciled with the GP1 allowlist schema). - New tools/corroborate/publish_workflow_test.go: pins the security contract actionlint cannot express (Pages perms + environment, the publish chain, the read-only identity, the determinism gate, per-job repo guard). - New infra/uat-gcp-account/evidence-dashboard.tf: dedicated read-only evidence-read SA (objectViewer on the evidence bucket only, repository-scoped WIF impersonation), kept separate from the shared github-actions SA and the GP2 writer. - Contributor doc + cross-link from evidence-ingest.md. Signed-off-by: Nathan Hensley <[email protected]>
ea4b74c to
5e99c72
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/evidence-dashboard-publish.yaml:
- Around line 72-77: The workflow hardcodes a different evidence bucket than the
Terraform-managed contract, so the Pages publish job can drift from infra
settings. Update the evidence-dashboard publish workflow to source the bucket
from the same workflow inputs/variables used for the service account, or remove
the Terraform override so both `GCS_READ_SERVICE_ACCOUNT` and `BUCKET` stay
aligned. Use the existing `evidence-dashboard` workflow contract symbols around
`GCP_WIF_PROVIDER`, `GCS_READ_SERVICE_ACCOUNT`, and `BUCKET` to keep the rsync
target consistent with `infra/uat-gcp-account/evidence-dashboard.tf`.
- Around line 63-77: The build job is still using the GCP auth and gcloud sync
path for all manual runs, which breaks the documented preview behavior for
feature-branch dispatches. Update the workflow around the build job and the
preview-related steps so that either build is restricted to refs/heads/main
alongside the existing repository check, or a true credential-free preview path
is added that skips the GCP auth and gcloud storage rsync logic. Use the build
job and the preview section near the documented manual-run behavior to keep the
conditions consistent.
In `@tools/corroborate/publish_workflow_test.go`:
- Around line 247-257: The fork-safety contract test only checks the
canonical-repo clause and misses the main-branch deploy guard. Update
TestPublishWorkflowForkSafety to explicitly verify wf.job(t, "deploy").If also
includes github.ref == 'refs/heads/main', while keeping the existing repository
guard checks on wf.Jobs so deploy remains restricted to main on the canonical
repo.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 80b73594-7237-4186-97c3-fcb659275080
📒 Files selected for processing (8)
.github/workflows/evidence-dashboard-publish.yamldocs/contributor/evidence-dashboard-publish.mddocs/contributor/evidence-ingest.mdinfra/uat-gcp-account/README.mdinfra/uat-gcp-account/evidence-dashboard.tfinfra/uat-gcp-account/outputs.tfinfra/uat-gcp-account/variables.tftools/corroborate/publish_workflow_test.go
| build: | ||
| name: Build site | ||
| if: github.repository == 'nvidia/aicr' | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
| permissions: | ||
| contents: read | ||
| id-token: write # GCS Workload Identity Federation only | ||
| env: | ||
| GCP_WIF_PROVIDER: "projects/116689922666/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" | ||
| # Read-only (objectViewer) SA, scoped to the evidence bucket by | ||
| # infra/uat-gcp-account/evidence-dashboard.tf. Deliberately NOT the shared | ||
| # github-actions@eidosx SA and NOT the GP2 evidence-publish writer. | ||
| GCS_READ_SERVICE_ACCOUNT: "[email protected]" | ||
| BUCKET: "aicr-testgrid-staging" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Non-main manual runs are not actually the documented preview path.
This job still does GCP auth and gcloud storage rsync unconditionally, while the shared github-actions-pool provider referenced here is main/tag-scoped in infra/demo-api-server/federation.tf:60-75. A workflow_dispatch run from a feature branch will therefore fail in build instead of producing the “credential-free preview” described on Lines 153-155. Either gate build to refs/heads/main too, or add a real preview path that skips auth/sync.
Also applies to: 99-115, 153-156
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/evidence-dashboard-publish.yaml around lines 63 - 77, The
build job is still using the GCP auth and gcloud sync path for all manual runs,
which breaks the documented preview behavior for feature-branch dispatches.
Update the workflow around the build job and the preview-related steps so that
either build is restricted to refs/heads/main alongside the existing repository
check, or a true credential-free preview path is added that skips the GCP auth
and gcloud storage rsync logic. Use the build job and the preview section near
the documented manual-run behavior to keep the conditions consistent.
| GCP_WIF_PROVIDER: "projects/116689922666/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" | ||
| # Read-only (objectViewer) SA, scoped to the evidence bucket by | ||
| # infra/uat-gcp-account/evidence-dashboard.tf. Deliberately NOT the shared | ||
| # github-actions@eidosx SA and NOT the GP2 evidence-publish writer. | ||
| GCS_READ_SERVICE_ACCOUNT: "[email protected]" | ||
| BUCKET: "aicr-testgrid-staging" |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Keep the evidence bucket on one contract.
infra/uat-gcp-account/evidence-dashboard.tf grants roles/storage.objectViewer on var.evidence_bucket, but this workflow always rsyncs aicr-testgrid-staging. A non-default Terraform apply will leave the Pages build pointed at the wrong bucket and fail at runtime or read the wrong data. Either surface the bucket through the same workflow contract as the service account, or remove the Terraform override.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/evidence-dashboard-publish.yaml around lines 72 - 77, The
workflow hardcodes a different evidence bucket than the Terraform-managed
contract, so the Pages publish job can drift from infra settings. Update the
evidence-dashboard publish workflow to source the bucket from the same workflow
inputs/variables used for the service account, or remove the Terraform override
so both `GCS_READ_SERVICE_ACCOUNT` and `BUCKET` stay aligned. Use the existing
`evidence-dashboard` workflow contract symbols around `GCP_WIF_PROVIDER`,
`GCS_READ_SERVICE_ACCOUNT`, and `BUCKET` to keep the rsync target consistent
with `infra/uat-gcp-account/evidence-dashboard.tf`.
| // TestPublishWorkflowForkSafety asserts every job is gated to the canonical | ||
| // repo so a fork never obtains GCS creds or Pages write. | ||
| func TestPublishWorkflowForkSafety(t *testing.T) { | ||
| wf := loadPublishWorkflow(t) | ||
|
|
||
| for name, job := range wf.Jobs { | ||
| if !strings.Contains(job.If, "github.repository == 'nvidia/aicr'") { | ||
| t.Errorf("job %q missing canonical-repo guard (fork safety)", name) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Also lock the main-only deploy guard in the contract test.
This only asserts the canonical-repo clause. If a future edit removes github.ref == 'refs/heads/main' from deploy.if, the test suite still passes and the live Pages site can be overwritten from a branch dispatch again. Add an explicit assertion on wf.job(t, "deploy").If for the main-ref guard.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tools/corroborate/publish_workflow_test.go` around lines 247 - 257, The
fork-safety contract test only checks the canonical-repo clause and misses the
main-branch deploy guard. Update TestPublishWorkflowForkSafety to explicitly
verify wf.job(t, "deploy").If also includes github.ref == 'refs/heads/main',
while keeping the existing repository guard checks on wf.Jobs so deploy remains
restricted to main on the canonical repo.
Summary
Add the repo's first GitHub Pages pipeline: on merge to
main(andworkflow_dispatch) it syncs the source-keyed corroboration evidence tree from GCS read-only, runs the GP4corroborategenerator to emit the deterministic static site, verifies reproducibility, and publishes viaconfigure-pages→upload-pages-artifact→deploy-pages.Motivation / Context
GP5 of the interim evidence-dashboard epic: stand up the public surface for the corroboration dashboard. Fern publishes product docs to docs.nvidia.com via
publish-fern-docs.yml; this is the only workflow that deploys to GitHub Pages.Fixes: #1405
Related: #1400 (epic), #1404 (GP4 generator), #1402 (GP2 ingest), #1403 (GP3 infra)
Type of Change
Component(s) Affected
docs/).github/workflows), infra (infra/uat-gcp-account), generator test (tools/corroborate)Implementation Notes
buildholds onlyid-token: write(GCS WIF, read-only SA) and runs an in-job determinism gate (two generator builds,diff -r);deployholdspages: writeon thegithub-pagesenvironment. Neither grants the other's scope. Every job is gatedif: github.repository == 'nvidia/aicr'for fork safety.evidence-read@eidosxSA (objectViewer on the evidence bucket only, repository-scoped WIF impersonation), added ininfra/uat-gcp-account/evidence-dashboard.tf— deliberately not the sharedgithub-actionsSA and not the GP2 writer. Already applied ineidosx(terraform plan= 3 add / 0 change / 0 destroy; SA + bindings verified live).-allowlistfor now. Each source's class is already re-derived from its verified signer at GP2 ingest and baked intometa.json(the trust gate). The generator's own allowlist re-derivation is deferred untilpkg/corroborate's loader is reconciled with the GP1 allowlist schema (recipes/evidence/allowlist.yamlusesidentityPattern/source; the loader still expectsidentity). Documented in the contributor doc.tools/corroborate/publish_workflow_test.gopins the security contract actionlint cannot express (Pages perms + environment, the publish chain, the read-only identity, the determinism gate, per-job repo guard).timeout-minutes.Testing
Local end-to-end of the build half (build generator →
gcloud storage rsyncevidence → generate) ran clean and byte-deterministic.Risk Assessment
Rollout notes: Before the first main-merge run, enable Pages in repo settings (Settings → Pages → source "GitHub Actions"); the
github-pagesenvironment is created on first deploy. The read-only SA is already provisioned. No data migration.Checklist
make testwith-race) — rango test -race ./tools/corroborate/...make lint) —golangci-lintclean on the changed package;actionlint+ docs checks passpublish_workflow_test.gogit commit -S)