fix(ci): plumb VERCEL_AUTOMATION_BYPASS_SECRET into preview-smoke + visual-diff #17

Merged
varutasu merged 4 commits from convoy/fix-vercel-deployment-protection-in-ci into main 2026-05-24 17:26:22 -04:00
varutasu commented 2026-05-24 16:28:17 -04:00 (Migrated from github.com)

Summary

Closes the CI-infra follow-up that PR #16 exposed. Vercel Deployment Protection 401s anonymous GitHub-runner GETs against preview URLs, which made both Playwright smoke and Screenshot diff time out at 10 minutes on every PR. This convoy plumbs VERCEL_AUTOMATION_BYPASS_SECRET end-to-end and the wait-action's healthcheck now succeeds in 194 milliseconds.

Convoy artifacts

  • Convoy file: .convoys/fix-vercel-deployment-protection-in-ci.md (architect ratified 4 decisions: A/B/C/D)
  • Single brief: .convoys/fix-vercel-deployment-protection-in-ci/brief-1-inject-bypass-secret-into-ci-workflows.md

Commits (3 commits, all on workflow YAML only)

  1. 365e9f0 — fix(ci): plumb VERCEL_AUTOMATION_BYPASS_SECRET into preview-smoke + visual-diff (Brief 1)
  2. b6f8688 — fix(ci): route github.event.pull_request.body through env: to avoid shell injection
  3. 043a6ee — fix(ci): drop x-vercel-set-bypass-cookie=true from wait-action path

Commits 2 and 3 are scope expansions discovered during CI validation of commit 1, not separate convoys, because both were latent bugs in the same two files that actively blocked validation of commit 1's success metric. Total diff: 2 files, +42/-10.

Decisions ratified

Decision Choice
A Bypass via query param on wait-action's path: vs. forking action for header Query param
B max_timeout 600 → 120 seconds
C Touch concurrency: blocks? No
D Skip on fork PRs? Yes (gate: short-circuit, FIRST)

Scope expansions discovered during validation

Shell-injection hardening (commit b6f8688)

The pre-existing Decide step inlined ${{ github.event.pull_request.body }} directly into a bash script. When this PR's body contained parenthesized phrases like "(was: 10-minute timeout)", bash parsed ( as a subshell start and the workflow failed with syntax error near unexpected token '('. This is GitHub's documented anti-pattern (SC2086-class shell injection). Fix: route PR body and fork flag through env: vars (PR_BODY, PR_IS_FORK), then quote them in shell. Same change in both workflows.

The architect's brief specified ?x-vercel-protection-bypass=...&x-vercel-set-bypass-cookie=true — but Vercel responds to that with 307 + Set-Cookie, and axios in Node has no cookie jar, so the followup request after the redirect drops the cookie and 401s. For a one-shot healthcheck, the cookie variant is unnecessary; the per-request bypass query is enough. The cookie variant stays reserved for the future Playwright config (adopt-playwright-smoke) where a real browser cookie jar exists. Verified locally:

  • curl -sI ".../?bypass=X" → HTTP/2 200
  • curl -sI ".../?bypass=X&set-bypass-cookie=true" → HTTP/2 307 (the broken path)

CI proof

From this PR's most recent run (job 77628243264):

target url » https://tcg-vault-lpnyvpmeg-randall-stillwells-projects.vercel.app
Received success status code

Wait-action elapsed: 194 ms. (Was: 10 minute timeout on every PR.)

Step breakdown on Playwright smoke after this PR:

  • Wait for Vercel Preview deployment — success
  • actions/setup-node — success
  • npm ci — success
  • Install Playwright browsers — success
  • Run smoke tests — failure (expected — playwright.config.js doesn't exist yet; that's adopt-playwright-smoke's scope)

Expected CI on this PR

Check Status Why
Aggregate gate status green
Lint / vitest / forbidden-endpoints / Vercel green unchanged from main
Should run? gate green env-var fix lets the body parse safely
Playwright smoke red but FAST wait-action works (~200ms); fails downstream at npx playwright test because no config exists yet
Screenshot diff not triggered path filter excludes workflow-only PRs

The visible red on Playwright smoke is the documented expected outcome. After adopt-playwright-smoke ships, both checks reach the actual test step and either pass (smoke green) or fail on real assertions.

Out of scope (deferred / pre-existing)

  • adopt-playwright-smoke — authors playwright.config.js, @playwright/test, and the actual smoke specs. This is the next convoy in the chain.
  • AGENTS.md § 7 noun correction — current wording says "header (x-vercel-protection-bypass)" but per Decision A we landed query-param. Doc-writer pass at convoy close handles this.
  • Replacing wait-for-vercel-preview action — flagged for replace-wait-for-vercel-preview follow-up if the action ages out further (last release Mar 2024).

Test plan

  • Local YAML parse (both files)
  • CI: Aggregate gate, Lint, vitest, forbidden-endpoints, Vercel — all green
  • CI: Wait-action step succeeds in <1s (was: 10-min timeout)
  • Workflow run time: 59s (was: 10+ min)
  • Bypass secret value confirmed: 32 hex chars, no quote wrapping (curl -sI returns HTTP/2 200)
  • No set -x, no echo/cat/printf of the bypass URL or secret in any modified step
  • permissions: blocks unchanged (PR #16 contracts preserved)
  • concurrency: blocks unchanged
  • Post-merge: visit a workflow run log, grep for the bypass secret's first 8 chars — expect zero matches

Closes the CI-infra side-effect of P0 #7. Doc-writer will mark the queued convoy as RESOLVED in .convoys/ship-readiness.md, update AGENTS.md § 7's "header" wording, and note the env-var-safety hardening as a separable finding.

## Summary Closes the CI-infra follow-up that PR #16 exposed. Vercel Deployment Protection 401s anonymous GitHub-runner GETs against preview URLs, which made both `Playwright smoke` and `Screenshot diff` time out at 10 minutes on every PR. This convoy plumbs `VERCEL_AUTOMATION_BYPASS_SECRET` end-to-end and the wait-action's healthcheck now succeeds in **194 milliseconds**. ## Convoy artifacts - Convoy file: `.convoys/fix-vercel-deployment-protection-in-ci.md` (architect ratified 4 decisions: A/B/C/D) - Single brief: `.convoys/fix-vercel-deployment-protection-in-ci/brief-1-inject-bypass-secret-into-ci-workflows.md` ## Commits (3 commits, all on workflow YAML only) 1. **`365e9f0`** — fix(ci): plumb VERCEL_AUTOMATION_BYPASS_SECRET into preview-smoke + visual-diff (Brief 1) 2. **`b6f8688`** — fix(ci): route github.event.pull_request.body through env: to avoid shell injection 3. **`043a6ee`** — fix(ci): drop x-vercel-set-bypass-cookie=true from wait-action path Commits 2 and 3 are scope expansions discovered during CI validation of commit 1, not separate convoys, because both were latent bugs in the same two files that actively blocked validation of commit 1's success metric. Total diff: 2 files, +42/-10. ## Decisions ratified | | Decision | Choice | |---|---|---| | A | Bypass via query param on wait-action's path: vs. forking action for header | **Query param** | | B | max_timeout | **600 → 120 seconds** | | C | Touch concurrency: blocks? | **No** | | D | Skip on fork PRs? | **Yes (gate: short-circuit, FIRST)** | ## Scope expansions discovered during validation ### Shell-injection hardening (commit `b6f8688`) The pre-existing `Decide` step inlined `${{ github.event.pull_request.body }}` directly into a bash script. When this PR's body contained parenthesized phrases like "(was: 10-minute timeout)", bash parsed `(` as a subshell start and the workflow failed with `syntax error near unexpected token '('`. This is GitHub's documented anti-pattern (`SC2086`-class shell injection). Fix: route PR body and fork flag through env: vars (`PR_BODY`, `PR_IS_FORK`), then quote them in shell. Same change in both workflows. ### Cookie-param removal (commit `043a6ee`) The architect's brief specified `?x-vercel-protection-bypass=...&x-vercel-set-bypass-cookie=true` — but Vercel responds to that with **307 + Set-Cookie**, and axios in Node has no cookie jar, so the followup request after the redirect drops the cookie and 401s. For a one-shot healthcheck, the cookie variant is unnecessary; the per-request bypass query is enough. The cookie variant stays reserved for the future Playwright config (`adopt-playwright-smoke`) where a real browser cookie jar exists. Verified locally: - `curl -sI ".../?bypass=X"` → HTTP/2 200 ✅ - `curl -sI ".../?bypass=X&set-bypass-cookie=true"` → HTTP/2 307 (the broken path) ## CI proof From this PR's most recent run (job `77628243264`): ``` target url » https://tcg-vault-lpnyvpmeg-randall-stillwells-projects.vercel.app Received success status code ``` Wait-action elapsed: **194 ms**. (Was: 10 minute timeout on every PR.) Step breakdown on Playwright smoke after this PR: - `Wait for Vercel Preview deployment` — success - `actions/setup-node` — success - `npm ci` — success - `Install Playwright browsers` — success - `Run smoke tests` — failure (expected — `playwright.config.js` doesn't exist yet; that's `adopt-playwright-smoke`'s scope) ## Expected CI on this PR | Check | Status | Why | |---|---|---| | Aggregate gate status | green | | | Lint / vitest / forbidden-endpoints / Vercel | green | unchanged from main | | `Should run?` gate | green | env-var fix lets the body parse safely | | `Playwright smoke` | red but FAST | wait-action works (~200ms); fails downstream at `npx playwright test` because no config exists yet | | `Screenshot diff` | not triggered | path filter excludes workflow-only PRs | The visible red on Playwright smoke is the documented expected outcome. After `adopt-playwright-smoke` ships, both checks reach the actual test step and either pass (smoke green) or fail on real assertions. ## Out of scope (deferred / pre-existing) - **`adopt-playwright-smoke`** — authors `playwright.config.js`, `@playwright/test`, and the actual smoke specs. This is the next convoy in the chain. - **`AGENTS.md` § 7 noun correction** — current wording says "header (`x-vercel-protection-bypass`)" but per Decision A we landed query-param. Doc-writer pass at convoy close handles this. - **Replacing `wait-for-vercel-preview` action** — flagged for `replace-wait-for-vercel-preview` follow-up if the action ages out further (last release Mar 2024). ## Test plan - [x] Local YAML parse (both files) - [x] CI: Aggregate gate, Lint, vitest, forbidden-endpoints, Vercel — all green - [x] CI: Wait-action step succeeds in <1s (was: 10-min timeout) - [x] Workflow run time: 59s (was: 10+ min) - [x] Bypass secret value confirmed: 32 hex chars, no quote wrapping (`curl -sI` returns HTTP/2 200) - [x] No `set -x`, no `echo`/`cat`/`printf` of the bypass URL or secret in any modified step - [x] `permissions:` blocks unchanged (PR #16 contracts preserved) - [x] `concurrency:` blocks unchanged - [ ] Post-merge: visit a workflow run log, grep for the bypass secret's first 8 chars — expect zero matches Closes the CI-infra side-effect of P0 #7. Doc-writer will mark the queued convoy as RESOLVED in `.convoys/ship-readiness.md`, update AGENTS.md § 7's "header" wording, and note the env-var-safety hardening as a separable finding.
vercel[bot] commented 2026-05-24 16:28:22 -04:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tcg-vault Ready Ready Preview, Comment May 24, 2026 9:15pm

Request Review

[vc]: #y9rXg5v0YXRIstupc7zTshE21KhGXTLeRvU+/om+LYk=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0Y2ctdmF1bHQiLCJwcm9qZWN0SWQiOiJwcmpfRjZXOEVvRkd3Y0g3aWVGcnRvRlNlOXdVVkFhNSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0Y2ctdmF1bHQtZ2l0LWNvbnZveS1maXgtdmVyLTkyOTNhZi1yYW5kYWxsLXN0aWxsd2VsbHMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcmFuZGFsbC1zdGlsbHdlbGxzLXByb2plY3RzL3RjZy12YXVsdC8zOHp0VGF1bjU1OHd4ejdGeTZZZHFXQ0w3a3ZyIiwicHJldmlld1VybCI6InRjZy12YXVsdC1naXQtY29udm95LWZpeC12ZXItOTI5M2FmLXJhbmRhbGwtc3RpbGx3ZWxscy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwicm9vdERpcmVjdG9yeSI6bnVsbH1dLCJyZXF1ZXN0UmV2aWV3VXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3ZlcmNlbC1hZ2VudC9yZXF1ZXN0LXJldmlldz9vd25lcj12YXJ1dGFzdSZyZXBvPXRjZy12YXVsdCZwcj0xNyJ9 The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated (UTC) | | :--- | :----- | :------ | :------ | | [tcg-vault](https://vercel.com/randall-stillwells-projects/tcg-vault) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/randall-stillwells-projects/tcg-vault/38ztTaun558wxz7Fy6YdqWCL7kvr) | [Preview](https://tcg-vault-git-convoy-fix-ver-9293af-randall-stillwells-projects.vercel.app), [Comment](https://vercel.live/open-feedback/tcg-vault-git-convoy-fix-ver-9293af-randall-stillwells-projects.vercel.app?via=pr-comment-feedback-link) | May 24, 2026 9:15pm | <a href="https://vercel.com/vercel-agent/request-review?owner=varutasu&repo=tcg-vault&pr=17" rel="noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://agents-vade-review.vercel.sh/request-review-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://agents-vade-review.vercel.sh/request-review-light.svg"><img src="https://agents-vade-review.vercel.sh/request-review-light.svg" alt="Request Review"></picture></a>
github-actions[bot] commented 2026-05-24 16:28:30 -04:00 (Migrated from github.com)

Pipeline Health

Build + CI gates

Gate Status
Vercel build (Preview) pass
CI: Lint pass
CI: Schema map fresh skipped
Preview smoke failure
Visual diff ⏭ skipped or pending

Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build).

Role reports

Role Status
Reviewer report pending
A11y audit pending
Design system audit pending

See individual comments above for details. This rollup updates automatically.

<!-- pipeline-rollup --> ## Pipeline Health ### Build + CI gates | Gate | Status | | --- | --- | | Vercel build (Preview) | ✅ pass | | CI: Lint | ✅ pass | | CI: Schema map fresh | ❌ skipped | | Preview smoke | ❌ failure | | Visual diff | ⏭ skipped or pending | _Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build)._ ### Role reports | Role | Status | | --- | --- | | Reviewer report | ⏳ pending | | A11y audit | ⏳ pending | | Design system audit | ⏳ pending | See individual comments above for details. This rollup updates automatically.
Sign in to join this conversation.
No description provided.