ci: harden paid runner browser and lock repair (#12829)

## Thinking Path

Paid cells now reuse the AWS image's system Chrome, but Playwright video
recording still resolves its revision-pinned FFmpeg helper from the
Playwright cache. Run 33875618534 proved Chrome qualification succeeds
and then failed before provider startup because that helper was absent.
The same run also exposed that generic lock repair can churn unrelated
package platform metadata, so the automated repair paths need
resolution-only regeneration rather than lockfile-only metadata refresh.

## What Changed

- install Playwright FFmpeg only on the AWS/system-Chrome path
- retry the small helper installation up to three times before provider
secrets are exposed
- keep the GitHub-hosted Chromium fallback unchanged
- bind static coverage to the exact FFmpeg step block and its pre-secret
ordering
- add pnpm `--resolution-only` to all four automated lock-repair paths
while retaining full transitive resolution
- require resolution-only repair in the shared workflow regression

The actual generated lockfile correction remains bot-owned by PR #12828
and is intentionally not committed here.

## Verification

- `node --test
.github/scripts/tests/lockfile-refresh-workflows.test.mjs`
- `actionlint -ignore SC2012` on all modified workflows
- focused Prettier checks
- `git diff --check`
- prior run 33875618534: system Chrome 151 qualified; missing Playwright
FFmpeg was the sole cell startup failure

## Risks

Low. The new network operation is limited to Playwright's pinned FFmpeg
payload, happens before paid credentials are exposed, and leaves the
hosted-runner path unchanged. Resolution-only is still a full
dependency-resolution pass, unlike lockfile-only, while avoiding
unrelated current-platform metadata churn.

## Model Used

GPT-5
This commit is contained in:
Dotta 2026-09-04 08:58:44 -05:00 committed by GitHub
parent af3023f1e3
commit 4ef6155aae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 42 additions and 6 deletions

View File

@ -17,6 +17,7 @@ test('lockfile repair workflows resolve dependencies instead of updating metadat
assert.ok(repairCommands.length > 0, `${workflow} must contain a lockfile repair command`);
for (const command of repairCommands) {
assert.match(command, /--resolution-only/);
assert.match(command, /--ignore-scripts/);
assert.doesNotMatch(command, /--lockfile-only/);
}

View File

@ -87,7 +87,7 @@ jobs:
- name: Refresh lockfile for Docker build context
run: |
set -euo pipefail
pnpm install --ignore-scripts --no-frozen-lockfile
pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
changed="$(git status --porcelain)"
if [ -z "$changed" ]; then
@ -281,7 +281,7 @@ jobs:
- name: Refresh lockfile for Docker build context
run: |
set -euo pipefail
pnpm install --ignore-scripts --no-frozen-lockfile
pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
changed="$(git status --porcelain)"
if [ -z "$changed" ]; then

View File

@ -346,7 +346,7 @@ jobs:
id: regen_lockfile
run: |
cp pnpm-lock.yaml "$RUNNER_TEMP/pnpm-lock.before.yaml"
pnpm install --ignore-scripts --no-frozen-lockfile
pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
if cmp -s "$RUNNER_TEMP/pnpm-lock.before.yaml" pnpm-lock.yaml; then
echo "regenerated=0" >> "$GITHUB_OUTPUT"
else

View File

@ -35,7 +35,7 @@ jobs:
cache: pnpm
- name: Refresh pnpm lockfile
run: pnpm install --ignore-scripts --no-frozen-lockfile
run: pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
- name: Fail on unexpected file changes
run: |

View File

@ -923,6 +923,21 @@ jobs:
test -x "$chrome_path"
google-chrome --version
- name: Install Playwright FFmpeg on AWS runner
if: needs.authorize.outputs.playwright_channel == 'chrome'
run: |
set -euo pipefail
for attempt in 1 2 3; do
if pnpm exec playwright install ffmpeg; then
exit 0
fi
if [ "$attempt" -eq 3 ]; then
echo "Playwright FFmpeg installation failed after $attempt attempts." >&2
exit 1
fi
sleep "$((attempt * 10))"
done
- name: Install Chromium headless shell on GitHub-hosted fallback
if: needs.authorize.outputs.playwright_channel != 'chrome'
run: |

View File

@ -306,8 +306,8 @@ test("the trusted PR workflow regenerates stale stacked lockfiles", () => {
);
assert.match(
workflow,
/pnpm install --ignore-scripts --no-frozen-lockfile/,
"the policy job must resolve the complete merge tree instead of only updating lockfile metadata",
/pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile/,
"the policy job must resolve the complete merge tree without rewriting platform metadata",
);
assert.match(
workflow,

View File

@ -266,10 +266,30 @@ describe("public repository paid workflow security", () => {
const daytonaPluginPreparation = paidJob.indexOf(
"Prepare bundled Daytona plugin without dependency lifecycle scripts",
);
const awsFfmpegInstall = paidJob.indexOf(
"- name: Install Playwright FFmpeg on AWS runner",
);
const hostedChromiumInstall = paidJob.indexOf(
"- name: Install Chromium headless shell on GitHub-hosted fallback",
);
const paidExecution = paidJob.indexOf("- name: Run paid cell");
expect(paidInstall).toBeGreaterThan(0);
expect(daytonaPluginPreparation).toBeGreaterThan(paidInstall);
expect(awsFfmpegInstall).toBeGreaterThan(daytonaPluginPreparation);
expect(hostedChromiumInstall).toBeGreaterThan(awsFfmpegInstall);
expect(paidExecution).toBeGreaterThan(awsFfmpegInstall);
expect(paidExecution).toBeGreaterThan(daytonaPluginPreparation);
const awsFfmpegStep = paidJob.slice(
awsFfmpegInstall,
hostedChromiumInstall,
);
expect(awsFfmpegStep).toContain(
"if: needs.authorize.outputs.playwright_channel == 'chrome'",
);
expect(awsFfmpegStep).toContain("pnpm exec playwright install ffmpeg");
expect(awsFfmpegStep).not.toMatch(
/(?:OPENAI|ANTHROPIC|OPENROUTER|DAYTONA)_API_KEY/,
);
const preparedBeforeProviderAccess = paidJob.slice(
daytonaPluginPreparation,
paidExecution,