Files
supabase/scripts/waitForVercelPreview.js
Miranda Limonczenko 187120a718 test(e2e): share the a11y warn-mode helpers across suites
Docs owned the only warn-mode implementation, so WWW and Studio would each have
copied it. Move the reusable half into e2e/shared: the scan-result envelope, the
annotate and blocking helpers, an exclude-capable scan for covering everything
outside a region, and the viewport helpers responsive chrome needs.

Also gate the Vercel bypass secret on a Supabase host, and stop resolving a
preview URL from a skipped Vercel build.

Docs behavior is unchanged: the emitted axe-results.json is byte-identical.
2026-08-17 11:21:44 -07:00

122 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Vercel's GitHub App has stopped writing GitHub Deployment objects since
// 2026-02-17 (broken app auth), so polling the Deployments API (as
// vercel/wait-for-deployment-action does) times out even though the preview
// builds fine. Poll the project's Vercel commit status instead, then resolve
// the actual preview URL via Vercel's own deployments API.
//
// Set VERCEL_STATUS_CONTEXT to the project's commit status name, e.g.
// "Vercel – docs" or "Vercel – zone-www-dot-com".
const { appendFileSync } = require('fs')
const TIMEOUT_MS = 900_000
const POLL_INTERVAL_MS = 15_000
async function fetchLatestStatus(repository, sha, githubToken, statusContext) {
const url = `https://api.github.com/repos/${repository}/commits/${sha}/statuses`
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${githubToken}`,
Accept: 'application/vnd.github+json',
},
})
if (!response.ok) {
throw new Error(`Failed to fetch commit statuses: ${response.status} ${response.statusText}`)
}
const statuses = await response.json()
return statuses
.filter((status) => status.context === statusContext)
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0]
}
async function resolveDeploymentUrl(targetUrl, vercelToken, teamId) {
const rawId = targetUrl.split('/').filter(Boolean).pop()
if (!rawId) {
throw new Error(`Could not parse a deployment ID from target_url: ${targetUrl}`)
}
const deploymentId = rawId.startsWith('dpl_') ? rawId : `dpl_${rawId}`
const url = teamId
? `https://api.vercel.com/v13/deployments/${deploymentId}?teamId=${teamId}`
: `https://api.vercel.com/v13/deployments/${deploymentId}`
const response = await fetch(url, {
headers: { Authorization: `Bearer ${vercelToken}` },
})
if (!response.ok) {
throw new Error(
`Failed to resolve Vercel deployment ${deploymentId}: ${response.status} ${response.statusText}`
)
}
const deployment = await response.json()
return `https://${deployment.url}`
}
function writeOutput(name, value) {
const outputFile = process.env.GITHUB_OUTPUT
if (!outputFile) {
throw new Error('GITHUB_OUTPUT environment variable is required')
}
appendFileSync(outputFile, `${name}=${value}\n`)
}
async function main() {
const repository = process.env.GITHUB_REPOSITORY
const sha = process.env.HEAD_SHA
const githubToken = process.env.GITHUB_TOKEN
const vercelToken = process.env.VERCEL_TOKEN
const teamId = process.env.VERCEL_TEAM_ID
const statusContext = process.env.VERCEL_STATUS_CONTEXT
if (!repository) throw new Error('GITHUB_REPOSITORY environment variable is required')
if (!sha) throw new Error('HEAD_SHA environment variable is required')
if (!githubToken) throw new Error('GITHUB_TOKEN environment variable is required')
if (!vercelToken) throw new Error('VERCEL_TOKEN environment variable is required')
if (!statusContext) throw new Error('VERCEL_STATUS_CONTEXT environment variable is required')
const start = Date.now()
for (;;) {
const latest = await fetchLatestStatus(repository, sha, githubToken, statusContext)
if (latest?.state === 'success') {
// A skipped build posts success, but its target_url points at an older
// deployment. Resolve no URL rather than test the wrong markup.
if (/^skipped/i.test(latest.description ?? '')) {
console.error(
`Vercel skipped the "${statusContext}" build for ${sha}: ${latest.description}`
)
return
}
if (!latest.target_url) {
throw new Error(
`"${statusContext}" commit status succeeded but had no target_url to resolve a deployment from`
)
}
const deploymentUrl = await resolveDeploymentUrl(latest.target_url, vercelToken, teamId)
writeOutput('deployment-url', deploymentUrl)
return
}
if (latest?.state === 'failure' || latest?.state === 'error') {
throw new Error(`"${statusContext}" deployment failed (commit status: ${latest.state})`)
}
if (Date.now() - start > TIMEOUT_MS) {
throw new Error(`Timed out after 900s waiting for the "${statusContext}" preview deployment`)
}
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS))
}
}
main().catch((error) => {
console.error('Fatal error:', error)
process.exit(1)
})