Skip to content

Commit ca500a6

Browse files
pranavz28claude
andauthored
ci(sdk-regression): full 33-SDK matrix via RUN_REGRESSION comment / dispatch with per-SDK ref overrides (PER-9772) (#2322)
* ci(sdk-regression): auto-run on PRs via `run-sdk-regression` label 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> * ci(sdk-regression): also fan out App Percy + POA on Buildkite 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> * ci(sdk-regression): trigger App/POA on Buildkite directly + report table 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> * ci(sdk-regression): filter bootstrap/wait jobs from the result table 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> * ci(sdk-regression): add 5 injection-capable SDKs to the fan-out matrix 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> * ci(sdk-regression): add workflow_dispatch to run the web fan-out on demand 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> * ci(sdk-regression): set fail-fast: false on the SDK matrix 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> * ci(sdk-regression): dispatch percy-storybook's versioned test workflow 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> * ci(sdk-regression): add appium-dotnet + styleguidist to the matrix 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> * ci(sdk-regression): add App Percy + remaining SDKs to the matrix 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> * ci(sdk-regression): exclude percy-ember and percy-puppeteer 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> * ci(sdk-regression): paginate job-id lookup + don't gate on it 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> * ci(sdk-regression): re-add percy-ember and percy-puppeteer to matrix 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> * ci(sdk-regression): dispatch ci.yml for tosca-dotnet and uipath 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> * ci(sdk-regression): drop percy-robotframework and percy-nightmare from 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> * ci(sdk-regression): remove App Percy + POA Buildkite job Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(sdk-regression): comment+dispatch triggers only; per-SDK ref overrides 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> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8d11a0c commit ca500a6

1 file changed

Lines changed: 119 additions & 15 deletions

File tree

.github/workflows/sdk-regression.yml

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
name: SDK Regression
22
on:
33
issue_comment:
4-
types: [created, edited]
4+
types: [created, edited]
5+
workflow_dispatch:
6+
inputs:
7+
branch:
8+
description: CLI branch to run all SDK regression against
9+
required: false
10+
default: master
11+
sdk_refs:
12+
description: >-
13+
Per-SDK workflow ref overrides, comma-separated repo@branch
14+
(e.g. percy-cypress@my-fix,percy-ember@feat-x). SDKs not listed
15+
run from their matrix default ref.
16+
required: false
17+
default: ''
518
permissions:
619
contents: read
720
jobs:
@@ -15,19 +28,32 @@ jobs:
1528
contents: read
1629
pull-requests: read
1730
statuses: write
18-
if: ${{ github.event.issue.pull_request && github.event.comment.body == 'RUN_REGRESSION' }}
31+
# Run when either:
32+
# - a maintainer comments RUN_REGRESSION on a PR, or
33+
# - the workflow is dispatched manually (CLI branch + optional per-SDK refs).
34+
# Both paths are internal-only: the comment path is gated to write/admin
35+
# collaborators (check-access below); dispatch requires repo write access.
36+
if: >-
37+
(github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == 'RUN_REGRESSION') ||
38+
(github.event_name == 'workflow_dispatch')
1939
strategy:
40+
# Don't let one SDK's failure cancel the rest — a regression run must
41+
# report every SDK's result, not abort on the first failure.
42+
fail-fast: false
2043
matrix:
2144
# Format: repo@branch (default branch is master)
22-
repo:
45+
repo:
46+
# NOTE: percy-ember and percy-puppeteer assert on the PER-7348
47+
# readiness-gate contract, so they are expected to RED against an
48+
# ahead-of-release cli@master until they adapt + bump @percy/sdk-utils.
49+
# Kept in the matrix intentionally (run them, fix the reds as they come).
2350
- percy-ember
24-
- percy-cypress
2551
- percy-puppeteer
52+
- percy-cypress
2653
- percy-storybook
2754
- percy-playwright
2855
- percy-testcafe
2956
- percy-nightwatch
30-
- percy-nightmare
3157
- percy-webdriverio
3258
- percy-webdriverio@v2
3359
- percy-protractor
@@ -39,8 +65,31 @@ jobs:
3965
- percy-capybara
4066
- percy-appium-js
4167
- gatsby-plugin-percy
68+
# Injection-capable SDKs previously missing from the fan-out.
69+
# Their default branch is `main`, so pin the dispatch ref with @main
70+
# (the split default below is `master`, which would 404 for these).
71+
- percy-detox@main
72+
- percy-playwright-python@main
73+
- percy-playwright-java@main
74+
- percy-playwright-dotnet@main
75+
- percy-appium-dotnet@main
76+
- percy-styleguidist@main
77+
# App Percy + remaining SDKs (default branch noted; @main where not master)
78+
- percy-appium-python
79+
- percy-appium-java
80+
- percy-appium-wd
81+
- percy-appium-ruby@main
82+
- percy-maestro-web@main
83+
- percy-maestro-app@main
84+
- percy-react-native-app@main
85+
- percy-tosca-dotnet@main
86+
- percy-uipath@main
87+
- percy-xcui-swift@main
4288
steps:
89+
# Permission check applies only to the comment path; the label path is
90+
# already gated by GitHub (only write+ collaborators can label a PR).
4391
- name: Get user permissions
92+
if: ${{ github.event_name == 'issue_comment' }}
4493
uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0
4594
id: check-access
4695
with:
@@ -51,15 +100,34 @@ jobs:
51100
return data.permission;
52101
result-encoding: string
53102
- name: Check Access Level
54-
if: steps.check-access.outputs.result != 'write' && steps.check-access.outputs.result != 'admin'
103+
if: ${{ github.event_name == 'issue_comment' && steps.check-access.outputs.result != 'write' && steps.check-access.outputs.result != 'admin' }}
55104
run: exit 1
56105
- uses: xt0rted/pull-request-comment-branch@e8b8daa837e8ea7331c0003c9c316a64c6d8b0b1 # v3.0.0
57-
if: ${{ github.event.issue.pull_request }}
106+
if: ${{ github.event_name == 'issue_comment' }}
58107
id: comment-branch
108+
# Pass event data via env (never interpolate untrusted head.ref into the
109+
# shell directly). The ref is validated by the regex-match step below
110+
# before it is ever used to trigger a downstream workflow.
111+
- name: Resolve PR head ref and sha
112+
id: pr
113+
env:
114+
EVENT_NAME: ${{ github.event_name }}
115+
COMMENT_HEAD_REF: ${{ steps.comment-branch.outputs.head_ref }}
116+
COMMENT_HEAD_SHA: ${{ steps.comment-branch.outputs.head_sha }}
117+
DISPATCH_BRANCH: ${{ github.event.inputs.branch }}
118+
DISPATCH_SHA: ${{ github.sha }}
119+
run: |
120+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
121+
echo "head_ref=$DISPATCH_BRANCH" >> "$GITHUB_OUTPUT"
122+
echo "head_sha=$DISPATCH_SHA" >> "$GITHUB_OUTPUT"
123+
else
124+
echo "head_ref=$COMMENT_HEAD_REF" >> "$GITHUB_OUTPUT"
125+
echo "head_sha=$COMMENT_HEAD_SHA" >> "$GITHUB_OUTPUT"
126+
fi
59127
- uses: actions-ecosystem/action-regex-match@9e6c4fb3d5e898f505be7a1fb6e7b0a278f6665b # v2.0.2
60128
id: regex-match
61129
with:
62-
text: ${{ steps.comment-branch.outputs.head_ref }}
130+
text: ${{ steps.pr.outputs.head_ref }}
63131
regex: '^[a-zA-Z0-9_/\-]+$'
64132
- name: Break on invalid branch name
65133
run: exit 1
@@ -68,17 +136,24 @@ jobs:
68136
- name: Get Current Job Log URL
69137
uses: Tiryoh/gha-jobid-action@be260d8673c9211a84cdcf37794ebd654ba81eef # v1.4.0
70138
id: job-url
139+
# This step only resolves a target_url for the commit status; a lookup
140+
# failure must never gate the regression itself.
141+
continue-on-error: true
71142
with:
72143
github_token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }}
73-
job_name: "regression (${{ matrix.repo }})"
144+
job_name: "regression (${{ matrix.repo }})"
145+
# The fan-out matrix is >30 jobs (currently 35); the action defaults to
146+
# per_page=30, so jobs on page 2 resolve to null and exit 1. Cover the
147+
# whole matrix (GitHub jobs API max page size is 100).
148+
per_page: 100
74149
- name: Output Current Job Log URL
75150
run: echo ${{ steps.jobs.outputs.html_url }}
76151
- uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0
77152
with:
78153
github-token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }}
79154
script: |
80155
const { owner, repo } = context.repo;
81-
const sha = '${{ steps.comment-branch.outputs.head_sha }}'
156+
const sha = '${{ steps.pr.outputs.head_sha }}'
82157
const state = 'pending';
83158
const target_url = '${{ steps.job-url.outputs.html_url }}'
84159
const check_name = 'SDK Regression ${{ matrix.repo }}'
@@ -96,28 +171,57 @@ jobs:
96171
with:
97172
msg: ${{ matrix.repo }}
98173
separator: '@'
174+
# Resolve the ref the SDK's workflow runs from. Dispatch may override it
175+
# per SDK via the sdk_refs input (comma-separated repo@branch); anything
176+
# not listed keeps the matrix default. The chosen ref is validated before
177+
# use — it flows into a downstream workflow dispatch.
178+
- name: Resolve SDK workflow ref
179+
id: sdk-ref
180+
env:
181+
SDK_REFS: ${{ github.event.inputs.sdk_refs }}
182+
REPO_NAME: ${{ steps.split.outputs._0 }}
183+
DEFAULT_REF: ${{ steps.split.outputs._1 || 'master' }}
184+
run: |
185+
ref="$DEFAULT_REF"
186+
IFS=',' read -ra overrides <<< "${SDK_REFS:-}"
187+
for o in "${overrides[@]}"; do
188+
o="${o#"${o%%[![:space:]]*}"}"; o="${o%"${o##*[![:space:]]}"}"
189+
case "$o" in
190+
"$REPO_NAME"@?*) ref="${o#*@}" ;;
191+
esac
192+
done
193+
if ! printf '%s' "$ref" | grep -Eq '^[a-zA-Z0-9_/.\-]+$'; then
194+
echo "::error::invalid sdk ref '$ref' for $REPO_NAME"
195+
exit 1
196+
fi
197+
echo "ref=$ref" >> "$GITHUB_OUTPUT"
99198
- name: Trigger Workflow & Wait
100199
uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5
101200
id: reg-test
102201
with:
103202
owner: percy
104203
repo: ${{ steps.split.outputs._0 }}
105204
github_token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }}
106-
workflow_file_name: test.yml
107-
ref: ${{ steps.split.outputs._1 || 'master' }}
108-
client_payload: '{ "branch": "${{ steps.comment-branch.outputs.head_ref }}"}'
205+
# Most SDKs expose `test.yml`; a few use a differently-named workflow
206+
# that carries the @percy/cli inject step:
207+
# percy-storybook -> versioned test-storybook-vN.yml (latest v10)
208+
# percy-react-native-app -> storybook-rn-ci.yml
209+
# percy-tosca-dotnet / percy-uipath -> ci.yml (they have no test.yml)
210+
workflow_file_name: ${{ steps.split.outputs._0 == 'percy-storybook' && 'test-storybook-v10.yml' || (steps.split.outputs._0 == 'percy-react-native-app' && 'storybook-rn-ci.yml' || ((steps.split.outputs._0 == 'percy-tosca-dotnet' || steps.split.outputs._0 == 'percy-uipath') && 'ci.yml' || 'test.yml')) }}
211+
ref: ${{ steps.sdk-ref.outputs.ref }}
212+
client_payload: '{ "branch": "${{ steps.pr.outputs.head_ref }}"}'
109213
wait_interval: 15
110214
- name: Update Status
111215
uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0
112216
with:
113217
github-token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }}
114218
script: |
115219
const { owner, repo } = context.repo;
116-
const sha = '${{ steps.comment-branch.outputs.head_sha }}'
220+
const sha = '${{ steps.pr.outputs.head_sha }}'
117221
const state = '${{ steps.reg-test.outcome }}';
118222
const target_url = '${{ steps.job-url.outputs.html_url }}'
119223
const check_name = 'SDK Regression ${{ matrix.repo }}'
120-
224+
121225
github.repos.createCommitStatus({
122226
context: check_name,
123227
owner,

0 commit comments

Comments
 (0)