mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
Both recent staging 500s (module-scope createClient with an unset env var; libpg-query wasm missing from the function bundle) were boot-time crashes: the TanStack server imports the whole route tree at init, so one bad route module 500s every route. They only surfaced at runtime on Vercel. Add two guards so they fail earlier: - scripts/smoke-server.mjs: post-build smoke test that boots the built server with a platform-like env (self-hosted secrets removed) and requests a route. Any boot crash returns 5xx and exits non-zero. Wired into build:tanstack so the Vercel build fails. Accepts a custom entry path so CI can reuse it against the 'vercel build' function output to also catch missing-asset bugs (libpg-query.wasm), which local node_modules masks. - eslint: no-restricted-syntax banning module-scope createClient in pages/api/** and routes/** (analytics-SQL selectors factored into a shared const so the new block doesn't drop that guard for overlapping files).
74 lines
3.1 KiB
JavaScript
74 lines
3.1 KiB
JavaScript
#!/usr/bin/env node
|
|
// Post-build smoke test: boot the built TanStack server handler and make a
|
|
// real request with a platform-like environment, so module-scope boot
|
|
// crashes fail the BUILD instead of the deployed function at runtime.
|
|
//
|
|
// Why this exists: the TanStack server entry eagerly imports the entire route
|
|
// tree (`loadEntries`), so every route module is evaluated the first time the
|
|
// single function handler serves a request. A bad top-level side effect in any
|
|
// one route — e.g. `createClient(process.env.SUPABASE_URL!, ...)` at module
|
|
// scope, where SUPABASE_URL is unset on platform — throws during that import
|
|
// and 500s every route, including trivial ones like /api/get-utc-time. Those
|
|
// failures only showed up at runtime on Vercel; this catches them at build.
|
|
//
|
|
// What this catches: anything that throws while the route tree is imported
|
|
// (the most common class — missing env vars read at module scope).
|
|
// What this does NOT catch: assets missing from the *Vercel function bundle*
|
|
// (e.g. libpg-query.wasm). Local node_modules still has those, so booting the
|
|
// plain `dist/server` build won't surface them — that needs booting the
|
|
// `vercel build` output. Pass that function's entry as argv[1] to reuse this
|
|
// script in a deploy-gating CI step.
|
|
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const studioRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
const serverEntry = process.argv[2]
|
|
? path.resolve(process.argv[2])
|
|
: path.join(studioRoot, 'dist/server/server.js')
|
|
|
|
// Simulate the platform function runtime: these are only set on self-hosted,
|
|
// so removing them surfaces any route that needs them at module-load time.
|
|
for (const key of ['SUPABASE_URL', 'SUPABASE_SERVICE_KEY', 'SUPABASE_SERVICE_ROLE_KEY']) {
|
|
delete process.env[key]
|
|
}
|
|
|
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ''
|
|
|
|
// A request to any route forces `loadEntries` to import the full route tree,
|
|
// so a single cheap, dependency-free endpoint is enough to exercise the boot.
|
|
const ROUTES = ['/api/get-utc-time']
|
|
|
|
console.log(`[smoke] booting ${path.relative(studioRoot, serverEntry)}`)
|
|
const { default: handler } = await import(serverEntry)
|
|
|
|
let failed = false
|
|
for (const route of ROUTES) {
|
|
const url = `http://localhost${basePath}${route}`
|
|
try {
|
|
const res = await handler.fetch(new Request(url))
|
|
if (res.status >= 500) {
|
|
failed = true
|
|
const body = await res.text().catch(() => '')
|
|
console.error(`[smoke] ✗ ${route} → ${res.status}\n${body.slice(0, 800)}`)
|
|
} else {
|
|
console.log(`[smoke] ✓ ${route} → ${res.status}`)
|
|
}
|
|
} catch (err) {
|
|
failed = true
|
|
console.error(`[smoke] ✗ ${route} threw while booting the server:\n`, err)
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
console.error(
|
|
'\n[smoke] FAILED — the server bundle does not boot cleanly. This usually means a\n' +
|
|
'route module has a top-level side effect (e.g. createClient at module scope with a\n' +
|
|
'missing env var). Make it lazy so it only runs inside the handler.'
|
|
)
|
|
process.exit(1)
|
|
}
|
|
|
|
console.log('\n[smoke] passed — server boots and routes respond without a 5xx.')
|
|
process.exit(0)
|