Skip to content

Commit 87f2f37

Browse files
committed
Add CI timeouts and diagnostics
1 parent c30ca55 commit 87f2f37

5 files changed

Lines changed: 84 additions & 13 deletions

File tree

.github/workflows/ci.yml

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ jobs:
8080
github.event_name != 'pull_request' ||
8181
github.event.pull_request.head.repo.full_name == github.repository
8282
runs-on: ubuntu-latest
83+
timeout-minutes: 20
8384
steps:
8485
- uses: actions/checkout@v4
8586
with:
@@ -117,8 +118,17 @@ jobs:
117118
R2_HOST: ${{ secrets.R2_HOST }}
118119
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
119120
R2_PUBLIC_URL: ${{ secrets.R2_PUBLIC_URL }}
120-
- run: yarn exec playwright install --with-deps chromium
121-
- run: yarn verify:local
121+
- name: Install Playwright Chromium
122+
run: |
123+
set -euo pipefail
124+
echo "::group::Browser test diagnostics"
125+
node --version
126+
yarn --version
127+
df -h
128+
echo "::endgroup::"
129+
timeout --foreground 10m yarn exec playwright install --with-deps chromium
130+
- name: Run local browser verification
131+
run: timeout --foreground 12m yarn verify:local
122132
env:
123133
PLAYWRIGHT_SKIP_INSTALL: "1"
124134
TRANSLOADIT_KEY: ${{ secrets.TRANSLOADIT_KEY }}
@@ -137,6 +147,7 @@ jobs:
137147
github.event_name != 'pull_request' ||
138148
github.event.pull_request.head.repo.full_name == github.repository
139149
runs-on: ubuntu-latest
150+
timeout-minutes: 25
140151
steps:
141152
- uses: actions/checkout@v4
142153
with:
@@ -154,7 +165,7 @@ jobs:
154165
- name: Deploy Convex preview (PR)
155166
if: github.event_name == 'pull_request'
156167
run: |
157-
node scripts/deploy-cloud.ts >> "$GITHUB_ENV"
168+
timeout --foreground 12m node scripts/deploy-cloud.ts >> "$GITHUB_ENV"
158169
env:
159170
CI_OUTPUT: "1"
160171
CONVEX_PREVIEW_NAME: ${{ steps.preview_name.outputs.name }}
@@ -171,7 +182,7 @@ jobs:
171182
- name: Deploy Convex stable (main)
172183
if: github.ref == 'refs/heads/main'
173184
run: |
174-
node scripts/deploy-cloud.ts >> "$GITHUB_ENV"
185+
timeout --foreground 12m node scripts/deploy-cloud.ts >> "$GITHUB_ENV"
175186
env:
176187
CI_OUTPUT: "1"
177188
CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
@@ -197,7 +208,9 @@ jobs:
197208
- name: Resolve Vercel preview URL
198209
if: env.E2E_REMOTE_APP_URL == ''
199210
run: |
200-
echo "E2E_REMOTE_APP_URL=$(node scripts/resolve-vercel-preview.ts)" >> "$GITHUB_ENV"
211+
preview_url="$(timeout --foreground 7m node scripts/resolve-vercel-preview.ts)"
212+
echo "Resolved Vercel preview URL: ${preview_url}"
213+
echo "E2E_REMOTE_APP_URL=${preview_url}" >> "$GITHUB_ENV"
201214
env:
202215
VERCEL_PREVIEW_DEPLOY_HOOK: ${{ secrets.VERCEL_PREVIEW_DEPLOY_HOOK }}
203216
VERCEL_PROJECT_SLUG: convex
@@ -234,7 +247,8 @@ jobs:
234247
VERCEL_PROTECTION_BYPASS: ${{ secrets.VERCEL_PROTECTION_BYPASS }}
235248
CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
236249
CONVEX_PREVIEW_DEPLOY_KEY: ${{ secrets.CONVEX_PREVIEW_DEPLOY_KEY }}
237-
- run: yarn verify:cloud
250+
- name: Run cloud verification
251+
run: timeout --foreground 12m yarn verify:cloud
238252
env:
239253
E2E_REMOTE_APP_URL: ${{ env.E2E_REMOTE_APP_URL }}
240254
E2E_REMOTE_CONVEX_URL: ${{ env.E2E_REMOTE_CONVEX_URL }}

scripts/deploy-cloud.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const log = (...args: Parameters<typeof console.log>) => {
2020
const runStdio = ciOutput ? 'pipe' : 'inherit'
2121

2222
const rootDir = resolve(fileURLToPath(new URL('..', import.meta.url)))
23+
const minutes = (value: number) => value * 60 * 1000
2324
const sleep = (ms: number) =>
2425
new Promise((resolve) => {
2526
setTimeout(resolve, ms)
@@ -40,18 +41,26 @@ const deployCloud = async () => {
4041
await mkdir(projectDir, { recursive: true })
4142

4243
log(`Packing @transloadit/convex into ${tgzPath}...`)
43-
run('yarn', ['pack', '-o', tgzPath], { cwd: rootDir, stdio: runStdio })
44+
run('yarn', ['pack', '-o', tgzPath], {
45+
cwd: rootDir,
46+
stdio: runStdio,
47+
timeoutMs: minutes(2),
48+
})
4449

4550
await writeAppFiles({ projectDir, tgzPath })
4651

4752
log('Installing dependencies...')
4853
run('npm', ['install', '--no-fund', '--no-audit'], {
4954
cwd: projectDir,
5055
stdio: runStdio,
56+
timeoutMs: minutes(5),
5157
})
5258

5359
log('Deploying Convex app...')
5460
const previewName = process.env.CONVEX_PREVIEW_NAME
61+
if (previewName) {
62+
log(`Preview deployment name: ${previewName}`)
63+
}
5564
const deployArgs = ['convex', 'deploy', '--typecheck', 'disable', '--yes']
5665
if (previewName) {
5766
deployArgs.push('--preview-create', previewName)
@@ -64,6 +73,7 @@ const deployCloud = async () => {
6473
CONVEX_DEPLOY_KEY: requireEnv('CONVEX_DEPLOY_KEY'),
6574
},
6675
stdio: 'pipe',
76+
timeoutMs: minutes(8),
6777
})
6878

6979
const { deploymentName, deploymentUrl } = parseDeployOutput(deployOutput)
@@ -89,6 +99,7 @@ const deployCloud = async () => {
8999
env: deployEnv,
90100
stdio: runStdio === 'inherit' ? 'pipe' : runStdio,
91101
input: value,
102+
timeoutMs: minutes(2),
92103
})
93104
return
94105
} catch (error) {

scripts/qa/run.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type RunOptions = {
55
env?: NodeJS.ProcessEnv
66
stdio?: 'inherit' | 'pipe'
77
input?: string
8+
timeoutMs?: number
89
}
910

1011
export const requireEnv = (name: string) => {
@@ -17,20 +18,42 @@ export const requireEnv = (name: string) => {
1718

1819
export const run = (command: string, args: string[], options: RunOptions = {}) => {
1920
const stdio = options.input !== undefined ? 'pipe' : (options.stdio ?? 'inherit')
21+
if (options.timeoutMs) {
22+
console.error(
23+
`Running with timeout (${Math.round(options.timeoutMs / 1000)}s): ${command} ${args.join(
24+
' ',
25+
)}`,
26+
)
27+
}
2028
const result = spawnSync(command, args, {
2129
cwd: options.cwd,
2230
env: options.env,
2331
stdio,
2432
input: options.input,
2533
encoding: 'utf8',
34+
timeout: options.timeoutMs,
35+
killSignal: 'SIGTERM',
2636
})
2737

38+
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`
39+
40+
if (result.error) {
41+
throw new Error(`Command failed: ${command} ${args.join(' ')}
42+
${result.error.message}
43+
${output}`)
44+
}
45+
46+
if (result.signal) {
47+
throw new Error(`Command failed: ${command} ${args.join(' ')}
48+
Exited via signal ${result.signal}${options.timeoutMs ? ` after ${options.timeoutMs}ms` : ''}
49+
${output}`)
50+
}
51+
2852
if (result.status !== 0) {
29-
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`
3053
throw new Error(`Command failed: ${command} ${args.join(' ')}\n${output}`)
3154
}
3255

33-
return `${result.stdout ?? ''}${result.stderr ?? ''}`
56+
return output
3457
}
3558

3659
export const parseJson = <T>(output: string): T => {

scripts/resolve-vercel-preview.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const githubRepo = process.env.GITHUB_REPOSITORY ?? ''
1212
const githubSha = process.env.GITHUB_SHA ?? ''
1313
const githubEventPath = process.env.GITHUB_EVENT_PATH ?? ''
1414
const githubHeadRef = process.env.GITHUB_HEAD_REF ?? process.env.GITHUB_REF_NAME ?? ''
15+
const log = (...args: Parameters<typeof console.error>) => {
16+
console.error('[resolve-vercel-preview]', ...args)
17+
}
1518

1619
if (!githubToken) {
1720
throw new Error('Missing GITHUB_TOKEN')
@@ -29,6 +32,7 @@ const headers = {
2932

3033
const triggerPreviewDeploy = async () => {
3134
if (!deployHook) return
35+
log('Triggering Vercel preview deploy hook')
3236
const response = await fetch(deployHook, { method: 'POST' })
3337
if (!response.ok) {
3438
throw new Error(`Vercel deploy hook failed: ${response.status}`)
@@ -61,7 +65,10 @@ const fetchDeploymentUrl = async (): Promise<string | null> => {
6165
target_url?: string
6266
}>
6367
const success = statuses.find((status) => status.state === 'success')
64-
if (success?.target_url) return success.target_url
68+
if (success?.target_url) {
69+
log('Found successful GitHub deployment URL')
70+
return success.target_url
71+
}
6572
}
6673

6774
return null
@@ -86,7 +93,11 @@ const fetchCheckRunUrl = async (): Promise<string | null> => {
8693
const vercelCheck = checks.find(
8794
(check) => check.app?.slug === 'vercel' && check.conclusion === 'success' && check.details_url,
8895
)
89-
return vercelCheck?.details_url ?? null
96+
if (vercelCheck?.details_url) {
97+
log('Found successful Vercel check-run URL')
98+
return vercelCheck.details_url
99+
}
100+
return null
90101
}
91102

92103
const fetchPreviewUrlFromComments = async (): Promise<string | null> => {
@@ -115,6 +126,9 @@ const fetchPreviewUrlFromComments = async (): Promise<string | null> => {
115126
projects?: Array<{ previewUrl?: string }>
116127
}
117128
const previewUrl = payload.projects?.find((project) => project.previewUrl)?.previewUrl
129+
if (previewUrl) {
130+
log('Found Vercel preview URL in PR comments')
131+
}
118132
return previewUrl ?? null
119133
} catch {
120134
return null
@@ -161,7 +175,9 @@ const isPreviewReady = async (url: string) => {
161175
await triggerPreviewDeploy()
162176

163177
const deadline = Date.now() + 6 * 60 * 1000
178+
let attempts = 0
164179
while (Date.now() < deadline) {
180+
attempts += 1
165181
const deploymentUrl = await fetchDeploymentUrl()
166182
if (deploymentUrl) {
167183
process.stdout.write(normalizeUrl(deploymentUrl))
@@ -183,11 +199,16 @@ while (Date.now() < deadline) {
183199
if (fallbackUrl) {
184200
const ready = await isPreviewReady(fallbackUrl)
185201
if (ready) {
202+
log('Using ready fallback Vercel preview URL')
186203
process.stdout.write(normalizeUrl(fallbackUrl))
187204
process.exit(0)
188205
}
189206
}
190207

208+
if (attempts === 1 || attempts % 6 === 0) {
209+
log(`Still waiting for preview URL after ${attempts} checks`)
210+
}
211+
191212
await sleep(5000)
192213
}
193214

scripts/verify.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const parseArgs = (args: string[]) => {
2121
}
2222

2323
const logger = createDebugLogger({ namespace: 'verify' })
24+
const minutes = (value: number) => value * 60 * 1000
2425

2526
const runBrowser = async (options: {
2627
mode: Mode
@@ -33,11 +34,11 @@ const runBrowser = async (options: {
3334

3435
if (!skipInstall) {
3536
logger.event('playwright-install', { browser: 'chromium' })
36-
run('yarn', ['exec', 'playwright', 'install', 'chromium'])
37+
run('yarn', ['exec', 'playwright', 'install', 'chromium'], { timeoutMs: minutes(5) })
3738
}
3839

3940
logger.event('build')
40-
run('yarn', ['build'])
41+
run('yarn', ['build'], { timeoutMs: minutes(5) })
4142

4243
const testEnv: NodeJS.ProcessEnv = {
4344
...process.env,
@@ -53,6 +54,7 @@ const runBrowser = async (options: {
5354

5455
run('yarn', ['exec', 'vitest', 'run', '--config', 'vitest.e2e.config.ts'], {
5556
env: testEnv,
57+
timeoutMs: minutes(8),
5658
})
5759
}
5860

0 commit comments

Comments
 (0)