Skip to content

Commit 87b8d70

Browse files
bgagentclaude
andcommitted
fix(screenshot): read environment_url from deployment_status, not deployment
GitHub's `deployment_status` webhook puts the deployed URL on the *status* object, not the deployment itself. The deployment object is immutable per (sha, environment); the status changes through the deploy lifecycle (`pending` → `success`) and carries the URL only once the deploy finishes. Symptom: receiver kept short-circuiting `success` events from Vercel with `{ok: true, skipped_no_url: true}` because we read the wrong field. Verified by inspecting the webhook delivery payload via `gh api .../deliveries/<id> --jq .request.payload.deployment_status` — URL was there all along. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f0587c5 commit 87b8d70

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

cdk/src/handlers/github-webhook-processor.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ interface GitHubDeploymentStatusPayload {
3939
readonly id?: number;
4040
readonly state?: string;
4141
readonly target_url?: string;
42+
/** The deployed URL — lives on the *status* object, not the deployment. */
43+
readonly environment_url?: string;
4244
};
4345
readonly deployment?: {
4446
readonly id?: number;
4547
readonly sha?: string;
4648
readonly environment?: string;
47-
readonly environment_url?: string;
4849
};
4950
readonly repository?: {
5051
readonly full_name?: string;
@@ -89,7 +90,9 @@ export async function handler(event: ProcessorEvent): Promise<void> {
8990

9091
const repo = payload.repository?.full_name;
9192
const sha = payload.deployment?.sha;
92-
const previewUrl = payload.deployment?.environment_url;
93+
// The URL lives on `deployment_status` (it changes per status update —
94+
// `pending` has no URL, `success` fills it in), not on `deployment`.
95+
const previewUrl = payload.deployment_status?.environment_url;
9396
const deploymentId = payload.deployment?.id;
9497

9598
if (!repo || !sha || !previewUrl) {

cdk/src/handlers/github-webhook.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,14 @@ const DEDUP_TTL_SECONDS = 60 * 60;
4343
* (and any GitHub-Deployments-API-aware deploy backend) posts this when
4444
* a preview / production deploy finishes. The interesting fields:
4545
* - `deployment_status.state`: `success` | `failure` | `error` | `pending` | `in_progress`
46+
* - `deployment_status.environment_url`: the deployed URL — lives on the
47+
* *status* object, not the deployment itself. (The deployment object
48+
* only has the immutable SHA + environment name; URL changes per
49+
* status update — first `pending` has no URL, then `success` fills
50+
* it in.)
4651
* - `deployment.environment`: `Preview` | `Production`
47-
* - `deployment.environment_url`: the deployed URL (used by the agent
48-
* as the screenshot target — no extra round-trip needed)
4952
* - `deployment.sha`: the commit SHA the deploy is for (used to map
50-
* back to an ABCA task via the RepoCommitIndex GSI)
53+
* back to a PR via the GitHub commit-pulls API)
5154
*
5255
* Full payload is forwarded to the processor without re-serialization
5356
* risk — the processor parses its own copy from the raw body.
@@ -57,12 +60,12 @@ interface GitHubDeploymentStatusEnvelope {
5760
readonly deployment_status?: {
5861
readonly id?: number;
5962
readonly state?: string;
63+
readonly environment_url?: string;
6064
};
6165
readonly deployment?: {
6266
readonly id?: number;
6367
readonly sha?: string;
6468
readonly environment?: string;
65-
readonly environment_url?: string;
6669
};
6770
readonly repository?: {
6871
readonly full_name?: string;
@@ -164,7 +167,7 @@ export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayPr
164167
return jsonResponse(400, { error: 'Missing repo, deployment id, or status id' });
165168
}
166169

167-
if (!payload.deployment?.environment_url) {
170+
if (!payload.deployment_status?.environment_url) {
168171
logger.warn('GitHub deployment_status webhook missing environment_url; cannot screenshot', {
169172
repo,
170173
deployment_id: deploymentId,

0 commit comments

Comments
 (0)