Commit 4c8fdd6
ci: Comment the Vercel build log on PRs whose build fails (#1946)
## Problem
- When a PR's Vercel build fails, the Vercel bot comment and the
`Vercel` check link to the Vercel inspector, which only Vercel team
members can open
- Contributors who aren't Vercel team members see a red X and a login
wall, with no hint what broke
- Example: #1913
## Solution
- On `vercel.deployment.error` (the same `repository_dispatch` channel
`preview-links.yml` uses), comment the tail of the build log on the PR
Vercel built the deployment for (`meta.githubPrId`), like the links /
redirects / CSpell checks do. Each PR gets its own deployment, so two
PRs at the same commit each get their own comment
- On the next `vercel.deployment.success`, update that comment to ✅ on
every open PR at the commit that has one. The success path only reads PR
comments; it does not touch Vercel
- Lines that look like credentials (JWTs, `vcp_` / `ghp_` /
`github_pat_` / `AKIA…` tokens, `Bearer …`, `*TOKEN=` / `*SECRET=` /
`*KEY=` assignments) are redacted before the log is written, since the
comment and artifact are public and the build gets `VERCEL_OIDC_TOKEN`,
`VERCEL_DEPLOYMENT_KEY` and `VERCEL_ENV_ENC_KEY`. The code fence is
sized longer than any backtick run in the log, so a log line cannot
close it and inject Markdown
## Token
- `VERCEL_TOKEN` repo secret is a [project-scoped
token](https://vercel.com/docs/accounts/access-tokens) for
`sourcegraph-docs` only, expiring 2026-12-10. Vercel has no read-only
scope; project scope is the narrowest it offers, and it can read and
write everything in that one project
- Enumerated with read-only calls:
- Allowed: project settings, environment variables (`/env`, including
`?decrypt=true`; the project has none), deployments list, deployment
metadata, build log events, domains, custom environments, project list
(returns only this project)
- Denied: team, team members, user, log drains, webhooks, Edge Config,
other projects, minting tokens
- Writes within the project (env vars, deployments, domains, settings)
are allowed per Vercel's docs; not exercised
- `fetch-log` asks GitHub for an open PR from this repository at the
commit before it contacts Vercel, so a dispatch for a fork PR or a stale
commit never uses the token. `repository_dispatch` runs the workflow and
script from `main`, so a PR can't change the code the token is handed to
- Anyone with write access can read any repo secret by pushing a
workflow; this token limits what that buys them to one Vercel project
## Slack
- The Vercel Slack app already posts ":red_circle: … failed to deploy …
`<short sha>` | sourcegraph-docs" to `#alerts-vercel-doc-site`. On a
failure, a last step finds that post (looking back 30 minutes, then
polling for up to 5 more since Vercel and this workflow are triggered by
the same event) and uploads the full redacted log into its thread,
linking the PR comment. `continue-on-error`, so a Slack problem can't
hide the PR comment
- Needs the `SLACK_BOT_TOKEN` repo secret and `SLACK_CHANNEL_ID` repo
variable; skips quietly without them. The bot is the app in
`dev/slack-app-vercel-build-report.json` (`channels:history` to find the
post, `files:write` to reply); it must be `/invite`d to the channel
- Not yet run end to end; the app and secret are still to be created
## Tested
- Run locally against the two example PRs, which is how the comments
there got posted (from my account, since GitHub neither delivers
`repository_dispatch` nor resolves `workflow_dispatch` for a workflow
that isn't on `main` yet):
- #1948: ❌ [build log
comment](#1948 (comment))
- #1949: ❌ posted on a broken revision, then updated to ✅ [after the
fixed revision
built](#1949 (comment))
- A PR with no failed build exits with `has no failed build to resolve`
- That test found a bug: with two open PRs at the same head SHA, only
the first PR the `commits/{sha}/pulls` API listed got the comment.
Vercel records the PR a deployment was built for (`meta.githubPrId`), so
`fetch-log` now reads it and the comment lands on that PR only. The
success path stays off Vercel and keeps the commit lookup, since it only
updates comments that already exist
- Re-ran `fetch-log` after the reorder: #1948's head →
`pull_request=1948`; a commit with no open PR stops at `No open PR with
head …` with `VERCEL_TOKEN=invalid`, proving Vercel was not contacted.
Redaction and fence sizing checked against JWT, `vcp_`, `ghp_`, `AKIA…`,
`Bearer`, `KEY: value` lines and a log line of six backticks
- After merge, re-run on a PR by hand: `gh workflow run
vercel-build-report.yml -f id=dpl_... -f state=error -f sha=<pr head
sha>`
<details><summary>Dry-run output (abridged)</summary>
> ### ❌ The Vercel build failed for this PR
>
> Vercel only shows build logs to members of its team, so here is the
end of the log.
> Run `npm run build` locally to reproduce.
>
> **Build log**
> ```
> Running build in Cleveland, USA (East) – cle1
> ...
> ❌ Found 2 dead link(s) in 1 file(s):
> 📄 docs/code-search/features.mdx
> Line 154: /code-search/no-such-page
> ...
> ❌ Failed checks: links, filenames
> ELIFECYCLE Command failed with exit code 1.
> Error: Command "pnpm run build" exited with 1
> ```
</details>
## Amp thread
- [Vercel build failure
report](https://ampcode.com/threads/T-01a09014-dfa8-740c-95b4-9e28c43cae51)
<!-- pr-stack-merge-order -->
## Merge order for the PR-check stack
Trial-merged onto `main` in this order with no conflicts:
1. #1946 Vercel build log comment — independent; first so the other PRs'
Vercel failures get a readable log
2. #1916 check-links report format — adds `dev/sync-review-comments.sh`,
which #1935 calls
3. #1935 redirect check — needs #1916 merged first
4. #1947 spell check comment updates — independent
5. #1944 check-links, generated-docs sync PR — conflicts with #1916 on
`dev/check-links.mjs`; rebase after #1916 merges
Squash-merge each, then rebase the next onto `main`.
#1948 (broken) and #1949 (fixed) are the example PRs that exercise every
check; never merge, close them once the stack has landed.
---------
Co-authored-by: Amp <amp@ampcode.com>1 parent bfd1e61 commit 4c8fdd6
4 files changed
Lines changed: 614 additions & 0 deletions
File tree
- .github/workflows
- dev
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
0 commit comments