feat(ci): opt-in pre-merge testgrid for core changes via label + RC-tagged staging build - #6085
feat(ci): opt-in pre-merge testgrid for core changes via label + RC-tagged staging build#6085kriscoleman wants to merge 3 commits into
Conversation
…taging build (ku-adc) Add .github/workflows/testgrid-pr.yaml: an opt-in, pre-merge Testgrid run for core kURL changes, triggered by the 'run-testgrid' label or workflow_dispatch. Builds the PR's kURL with a core-only strategy, publishes under a unique RC tag to a per-PR versioned staging path, queues tgrun against it, and comments the run URL on the PR — without ever touching staging/VERSION. Recovers the deleted deploy-branch-staging.yaml as the base and adapts it: - pull_request[labeled] + workflow_dispatch triggers (no pull_request_target) - label-gating (write access) + testgrid-pr GitHub Environment for secrets - os-firstlast subset by default, os-full via 'testgrid-full' label - build-and-upload ordered after copy-packages so the fresh PR build always wins over stale copies on overlapping .tmpl/extra packages - reuses mshick/add-pr-comment for the PR run-URL comment Refs #6084. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BLOCKING: - Stop using AWS_PROD_* in testgrid-pr.yaml. Use a dedicated least-privilege secret (AWS_STAGING_PR_ACCESS_KEY_ID/SECRET) that must be IAM-scoped to s3://kurl-sh/staging/* with DENY on dist/* and staging/VERSION. Since the credentialed jobs run PR-authored make targets, the IAM policy is the real blast-radius boundary. Documented as a hard provisioning prerequisite. - Add VERSIONED_ONLY mode to bin/generate-addons.js and set it in testgrid-pr; drop the non-atomic restore step. The RC build now never writes the shared unversioned staging metadata, so there is no write-then-restore race under cancel-in-progress and no fragile prev-version rollback. LOW: - Document testgrid-pr GitHub Environment required reviewers as a HARD prerequisite (not optional) in the workflow header and README. - SHA-pin token/secret-receiving third-party actions: mshick/add-pr-comment@v3 and docker/login-action@v4. - Document that label runs reuse kurl-util:alpha and rebuild only the core batch; extra-packages / build-kurl-util-image require workflow_dispatch. - Wrap S3 copy/upload calls in exponential-backoff retry (mirrors bin/upload-dist-staging.sh); note the AWS CLI list-objects-v2 auto-pagination in bin/list-packages-s3.sh so the core-only copy set stays complete. Verified: actionlint PASS (integrated shellcheck), shellcheck list-packages-s3 PASS, node --check generate-addons.js PASS, workflow YAML parses. Refs #6084. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…testgrid-pr (ku-adc) Round 2 review (2 LOW): LOW-A: tighten the documented least-privilege IAM prerequisite so an exfiltrated staging credential cannot clobber shared staging state. The workflow only writes under staging/<rc-tag>/ (staging/*-rc-*), so the documented policy now: allow ListBucket+GetObject on staging/* (read for the copy-from-previous-release step); allow PutObject/DeleteObject ONLY on staging/*-rc-*; explicit DENY on dist/*, staging/VERSION, and the shared unversioned staging/addons-gen.json + staging/supported-versions-gen.json. Notes not to deny staging/*-gen.json broadly (would block the versioned staging/<rc-tag>/addons-gen.json this workflow must write). LOW-B: fix doc contradiction — the security-model bullet no longer claims the run-testgrid label 'requires write access / only maintainers can start a run'. It now states the label is Triage-addable and the testgrid-pr Environment's required reviewers is the real gate on secret exposure. Verified every workflow write targets staging/<rc-tag>/. actionlint PASS, node --check PASS, YAML parses. Refs #6084. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review record (con-voyage escort)Reviewed across 3 rounds by independent GitHub-Actions/Bash code and security reviewers. The security review was the primary gate (this workflow builds and runs PR-authored code with S3 + Testgrid credentials). Security review — found and closed 2 BLOCKING issues, then tightened the IAM contract; final verdict PASS ("airtight"):
Code review — PASS: triggers/concurrency, RC-tag uniqueness, core-only build completeness (copy prev + rebuild core = complete installable version), and the tgrun/PR-comment wiring all verified.
|
|
Reviews (1): Last reviewed commit: "docs(ci): tighten documented IAM policy ..." | Re-trigger Greptile |
| prev_version: ${{ steps.set-tag.outputs.prev_version }} | ||
| kurl_util_image: ${{ steps.set-tag.outputs.kurl_util_image }} | ||
| testgrid_ref: ${{ steps.set-tag.outputs.testgrid_ref }} | ||
| testgrid_os_spec: ${{ steps.set-tag.outputs.testgrid_os_spec }} |
There was a problem hiding this comment.
The
os-spec step emits testgrid_os_spec, but this job output reads it from set-tag. Every label-triggered or manually dispatched run therefore passes an empty --os-spec value to tgrun, preventing the intended Testgrid matrix from being queued.
| testgrid_os_spec: ${{ steps.set-tag.outputs.testgrid_os_spec }} | |
| testgrid_os_spec: ${{ steps.os-spec.outputs.testgrid_os_spec }} |
Knowledge Base Used: Build, package, and release automation
Core kURL changes (
scripts/,packages/, staging metadata) only get exercised in Testgrid after they land onmain, whendeploy-staging.yamlruns on push. So a core regression stays invisible until it's already merged. That's exactly how the Ubuntu 26.04kubelet: command not foundbug (#6072) slipped through and needed a follow-up fix (#6082).This brings back a pre-merge Testgrid path for core changes. It existed once as
deploy-branch-staging.yamland got deleted right after 26.04 shipped, so I recovered it from git history and reworked it into a label-driven, PR-safe workflow.This workflow hands repository secrets to jobs that build and run PR-authored code, so two things must be provisioned first. Without them it's a security risk, not a feature.
1. A dedicated least-privilege IAM user for
AWS_STAGING_PR_ACCESS_KEY_ID/AWS_STAGING_PR_SECRET_ACCESS_KEY. This must NOT be the prod credential (AWS_PROD_*). The workflow only ever writes under the per-PR RC prefix, so scope the policy to exactly that:s3:ListBucket+s3:GetObjectonstaging/*(read, to copy packages from the previous staging release).s3:PutObject/s3:DeleteObjectonly onstaging/*-rc-*(the RC prefix, not all ofstaging/*).dist/*,staging/VERSION,staging/addons-gen.json, andstaging/supported-versions-gen.json. (Don't denystaging/*-gen.jsonbroadly, or you'll also block the versionedstaging/<rc-tag>/addons-gen.jsonthis workflow legitimately writes.)Since the credentialed jobs run PR code, that IAM policy (not the workflow text) is the real blast-radius boundary. An exfiltrated credential is only as powerful as the policy lets it be.
2. The
testgrid-prGitHub Environment configured with required reviewers. Therun-testgridlabel is addable at Triage-level access, so it's not a strong gate on its own. The Environment's required reviewers is what actually gates who can start a credentialed run.How it works
Trigger: add the
run-testgridlabel to a PR, or run it manually viaworkflow_dispatchwith a branch input.*.tmpl,common.tar.gz,kurl-bin-utils) from the PR's source and copies everything else from the last staging release. A label run reuseskurl-util:alpha; to rebuild extra packages or a branch-specific util image, useworkflow_dispatchwith theextra-packages/build-kurl-util-imageinputs.<latest-tag>-rc-pr<num>-<sha>, to a per-PR paths3://kurl-sh/staging/<rc-tag>/. It never runsset-current-versionand never touches the sharedstaging/VERSIONpointer or the unversioned staging metadata (aVERSIONED_ONLYmode ingenerate-addons.jskeeps the shared*-gen.jsonuntouched).tgrunagainst that RC version. OS pool defaults to theos-firstlastsubset to keep cost down; add thetestgrid-fulllabel for the full matrix. Concurrency is capped to one run per PR.RC builds are never promoted to prod. Artifacts land under
staging/v20...-rc-.../and get swept by the existingbin/cleanup-staging-s3.sh(30-day cutoff); a tighter S3 lifecycle rule onstaging/*-rc-*is a nice-to-have.Security model
Plain
pull_request, notpull_request_target, so fork code never runs with secrets. Fork PRs are skipped; a maintainer pushes the branch and usesworkflow_dispatchto test a fork. Third-party actions that receive tokens are SHA-pinned. Privileged jobs run in thetestgrid-prEnvironment (see prereq 2 above).What I verified
A GitHub Actions workflow can't really run locally, so I validated what I could:
actionlint(which runs shellcheck on every embedded script),shellcheckon the edited shell,node --checkongenerate-addons.js, and a YAML parse. I traced the build, copy, publish, and queue chain against the existing scripts by hand.What still needs a live run with CI + S3 + Testgrid creds: the actual build/upload to the versioned path, the
tgrunqueue, the PR comment, and the acceptance criterion of demonstrating it on a real core-change PR. That's why this stays a draft.Fixes #6084.
🤖 Generated with Claude Code