ci(sdk-regression): full 33-SDK matrix via RUN_REGRESSION comment / dispatch with per-SDK ref overrides (PER-9772)#2322
ci(sdk-regression): full 33-SDK matrix via RUN_REGRESSION comment / dispatch with per-SDK ref overrides (PER-9772)#2322pranavz28 wants to merge 18 commits into
Conversation
Make SDK regression runnable as an automatic PR check, not only via a manual `RUN_REGRESSION` comment. Adds a `pull_request` trigger gated by the `run-sdk-regression` label; resolves the PR head ref/sha from either event; keeps the comment path and its write/admin permission guard intact. Untrusted head.ref is passed via env (not interpolated into the shell) and is still validated by the existing regex-match step before any downstream workflow is triggered. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
RUN_regression |
|
RUN_REGRESSION |
The same regression trigger (RUN_REGRESSION comment or run-sdk-regression label) now also fires the App Percy + POA suites, which run on Buildkite (real BrowserStack devices/browsers). A new trigger-app-poa job repository_dispatches to percy/percy-automation, whose workflow creates the Buildkite builds against this CLI branch. percy-automation remains the single owner of App/POA-on-Buildkite; this is just the trigger. Internal-only guard (write/admin or label) and env-based, regex-validated branch handling mirror the web job. Requires a PERCY_AUTOMATION_DISPATCH_TOKEN secret. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the percy-automation repository_dispatch hop with a direct Buildkite REST call: the app-poa-regression job creates builds on the app-percy and poa SDK regression suites (CLI built from this branch), polls them to completion, and upserts a per-SDK pass/fail table comment on the PR. - Direct Buildkite trigger (BUILDKITE_API_TOKEN in this repo) — no extra repo hop. - Waits for the builds (bounded by MAX_WAIT_MIN), then posts/edits a marker comment with each suite's per-job result + build links; fails the job if any job failed/canceled. - Internal-only guard + env-based, regex-validated branch handling unchanged. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Grounded against real builds of app-percy-sdk-regression-suite: the matrix
jobs are named per SDK+device (e.g. 'Python-Android [...]'), but the build
also has the bootstrap upload step ('App-Percy-SDK-tests'/'POA-SDK-tests')
and an unnamed wait job. Exclude both from the per-SDK pass/fail table and the
failure check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
detox, playwright-python, robotframework, playwright-java, playwright-dotnet all support CLI-branch injection in their test.yml but were missing from the matrix, so a CLI change silently skipped them. Added as @main (their default branch) since the split default ref is master. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…emand Lets the SDK regression matrix be triggered manually (and on the PR's own branch) against a chosen CLI branch, without a comment/label. The Buildkite App/POA job stays comment/label-only, so a dispatch tests the web fan-out only. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A regression fan-out must report every SDK's result; with default fail-fast the first SDK failure cancels all other matrix jobs, hiding the rest. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
storybook has no test.yml — it uses test-storybook-vN.yml — so the fan-out silently failed to trigger it. Dispatch test-storybook-v10.yml for storybook. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both now support CLI-branch injection (appium-dotnet#403, styleguidist#25). Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds appium-python/java/wd/ruby, maestro-web/app, react-native-app, tosca-dotnet, uipath, xcui-swift. react-native-app uses storybook-rn-ci.yml (per-repo workflow filename). Skips puppeteer/ember (per decision) and katalon/espresso (infeasible / needs emulator). Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Their tests assert on the PER-7348 readiness-gate contract and fail against an ahead-of-release cli@master until they adapt + bump @percy/sdk-utils. Skip per decision. Part of PER-9772. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fan-out matrix has grown past 30 jobs (currently 35). The `Get Current Job Log URL` step (Tiryoh/gha-jobid-action) defaults to per_page=30, so every job on page 2 fails to find itself, resolves job_id to null, and exits 1 *before* dispatching the SDK workflow — producing false reds (appium-js, maestro-app, maestro-web, selenium-ruby) that never actually ran a regression. Set per_page=100 (jobs API max) to cover the whole matrix, and mark the step continue-on-error since it only feeds the commit-status target_url and must never gate the regression itself. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both were excluded because their tests assert on the PER-7348 readiness-gate contract and red against an ahead-of-release cli@master. Re-adding them as-is: both have a workflow_dispatch trigger on their default branch, so they dispatch and run. Expected to red on master until they adapt to the two-call readiness contract + bump @percy/sdk-utils; we'll fix the reds as they surface. Matrix is now 36 SDKs (job-id lookup already paginated to per_page=100). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
percy-tosca-dotnet and percy-uipath have no test.yml — their @percy/cli inject step lives in ci.yml (workflow_dispatch + branch input + "Set up @percy/cli from git" cloning the injected branch are all present there). The orchestrator was dispatching test.yml, so both 404'd at the trigger step — previously mis-attributed to a token-access gap. Map both to ci.yml in the workflow_file_name selector so the fan-out reaches their real (correct) inject workflow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…m matrix percy-robotframework is archived (read-only) so workflow dispatch always 404s; percy-nightmare's downstream run hangs until the 6h job limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ides Drop the run-sdk-regression label (pull_request) trigger — regression now runs only via a RUN_REGRESSION PR comment or workflow_dispatch. Dispatch gains an sdk_refs input (comma-separated repo@branch) to run individual SDKs' workflows from a specific branch (e.g. CLI master + one SDK's feature branch); unlisted SDKs keep their matrix default ref. Overridden refs are validated before flowing into the downstream dispatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
run-sdk-regression label
🤖 Claude Code Review —
|
| Priority | Category | Check | Status | Notes |
|---|---|---|---|---|
| High | Security | No hardcoded secrets | ✅ Pass | Only secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN references; no literals |
| High | Security | Auth/authz checks present | ✅ Pass | Comment path gated to write/admin via check-access; dispatch gated by GitHub's own write requirement |
| High | Security | Input validation & sanitization | ✅ Pass | head_ref regex-validated before use; new sdk_refs parsed from env (not shell-interpolated) and the resolved ref is regex-validated before flowing downstream |
| High | Security | No IDOR | N/A | — |
| High | Security | No SQL injection | N/A | — |
| High | Correctness | Logic correct, edge cases | ✅ Pass | Nested workflow_file_name ternary correct; sdk_refs parser handles whitespace-trim, last-wins, exact repo@ match (no prefix cross-match) |
| High | Correctness | Explicit error handling | ✅ Pass | Invalid refs exit 1; job-url intentionally continue-on-error with rationale |
| High | Correctness | No race conditions | N/A | fail-fast: false is the correct choice here |
| Medium | Testing | New code has tests | ✅ Pass | sdk_refs parsing unit-tested + end-to-end dispatch verified (run 28647243686) |
| Medium | Testing | Error paths tested | ✅ Pass | Empty input, no prefix cross-match, last-wins, dotted refs, shell-metachar rejection |
| Medium | Testing | Existing tests pass | ✅ Pass | Workflow-only change |
| Medium | Performance | No unbounded fetching | ✅ Pass | per_page: 100 fix covers the 35-job matrix (was silently truncating at 30) |
| Medium | Performance | Background jobs | N/A | — |
| Medium | Quality | Follows existing patterns | ✅ Pass | Consistent with the repo's env-passing + regex-gate hardening (PER-8611) |
| Medium | Quality | Focused changes | ✅ Pass | Single concern, single file |
| Low | Quality | Meaningful names, no dead code | ✅ Pass | — |
| Low | Quality | Comments explain why | One stale comment (see below) | |
| Low | Quality | No unnecessary deps | ✅ Pass | All actions SHA-pinned |
Findings
1. Stale comment references a removed "label path" (Low — quality)
- File:
.github/workflows/sdk-regression.yml:89-90 - Issue: The comment says "Permission check applies only to the comment path; the label path is already gated by GitHub (only write+ collaborators can label a PR)." But this PR deliberately removed the label trigger — there is no
labeledevent inon:anymore. The comment describes a code path that no longer exists. - Suggestion:
# Permission check applies only to the comment path; workflow_dispatch is already gated by GitHub (requires repo write access).
2. branch dispatch input can't contain dots, but sdk_refs can (Low — minor inconsistency, not a bug)
- File:
.github/workflows/sdk-regression.yml:131vs:193 - Issue:
head_ref(which the dispatchbranchinput flows into) is validated with^[a-zA-Z0-9_/\-]+$(no.), whilesdk_refsallows.via^[a-zA-Z0-9_/.\-]+$. So a dispatch withbranch=rel/1.2.xis rejected even though dotted refs are advertised as supported forsdk_refs. Harmless for real PR head branches, but the two validators diverging is a small trap. - Suggestion: If dotted CLI branches should be dispatchable, align the
head_refregex to also allow.. Otherwise leave it — just be aware of the asymmetry.
Pre-existing (not introduced by this PR): line 150 echo ${{ steps.jobs.outputs.html_url }} references step id jobs, but the id is job-url (line 138) — so it echoes empty. Dates to #1237; purely cosmetic debug echo. Out of scope; could be swept up as a one-char fix.
Security deep-dive
The classic dangerous issue_comment/workflow_dispatch combo is handled correctly:
- The workflow always executes from the default branch (master), so a malicious PR can't alter this file's logic.
- Attacker-controllable
head_ref(fork branch name) is passed via env var, regex-validated, and gated by "Break on invalid branch name" before use in the downstream trigger and JSONclient_payload— anchored^...$regex means no shell/JSON breakout. - New
SDK_REFSis env-passed (never spliced into the shell source), parsed safely, and the resolvedrefis regex-validated before reaching thetrigger-workflow-and-waitref:input.readstops at the first newline, soGITHUB_OUTPUTnewline-injection isn't reachable. - Both trigger paths are internal-only (write/admin).
Overall: Pass
Verdict: Approve — security-conscious, well-tested CI change verified green end-to-end. Only nit worth doing before merge is fixing the stale "label path" comment (finding #1); finding #2 is optional.
What (PER-9772)
Make the SDK regression a usable pre-merge gate for percy/cli changes:
RUN_REGRESSIONcomment on a PR (write/admin authors; tests that PR's CLI branch), orworkflow_dispatch(defaults to CLImaster).run-sdk-regressionlabel trigger from this PR's first iteration was dropped in review of the final design.percy-robotframework(archived) andpercy-nightmare(downstream hangs) are excluded.sdk_refsinput, comma-separatedrepo@branch. Example:branch=master, sdk_refs=percy-cypress@my-fixruns the whole matrix against cli master with percy-cypress's workflow taken frommy-fix; unlisted SDKs keep their matrix default ref. Overridden refs are regex-validated before flowing into the downstream dispatch. (Comment path always uses matrix defaults —issue_commentcarries no inputs.)percy-tosca-dotnet/percy-uipathdispatchci.yml(they have notest.yml);percy-storybook→test-storybook-v10.yml;percy-react-native-app→storybook-rn-ci.yml.Rollout fixes that made the matrix green
Getting to green surfaced 9 repos where
percy-bot(theWORKFLOW_DISPATCH_ACTIONS_TOKENidentity) lacked access (now granted), plus 7 SDK-side bugs, all fixed and merged:percy-selenium-ruby#39 + percy-appium-js#589 (CLI-from-git setup used
npx percywithoutyarn global binon PATH → pulled the unrelated publicpercypackage), percy-ember#1171 (canvas test asserted attribute order), percy-protractor#734 (local_iframe_shimfallbacks drifted from the canonical sdk-utils contract of #2319), percy-selenium-python#247 (pre-existing coverage debt), percy-maestro-app#17 (noworkflow_dispatchinputs declared → 422), percy-styleguidist#26 (ESM test loader mislabeled yarn-linked CJS dists).Verification
sdk_refsparsing unit-tested (empty input → defaults; per-repo exact match, no prefix cross-match; last-wins duplicates;rel/1.2.x-style refs allowed; shell metacharacters rejected).test.ymldispatched at a branch ref with the CLIbranchinput (run 28662964285).🤖 Generated with Claude Code