scripts/supabase-worktree-config.ts
Rama main · réplica de lectura del repositorio original.
import { createHash } from 'node:crypto'
import { spawnSync } from 'node:child_process'
import { realpathSync } from 'node:fs'
import { resolve } from 'node:path'
export interface SupabaseWorktreePorts {
api: number
db: number
dbShadow: number
dbPooler: number
studio: number
inbucket: number
analytics: number
edgeInspector: number
}
export interface SupabaseWorktreeConfig {
repoRoot: string
worktreeHash: string
projectId: string
ports: SupabaseWorktreePorts
}
/**
* Best-effort repo root lookup for the current worktree.
*
* Falls back to `cwd` when git is unavailable.
*/
function getGitRepoRoot(cwd: string): string {
const res = spawnSync('git', ['rev-parse', '--show-toplevel'], {
cwd,
encoding: 'utf8',
})
if (res.status === 0) {
const out = (res.stdout || '').trim()
if (out)
return out
}
return cwd
}
/**
* Resolve a deliberate CI port band when jobs share a runner.
*
* The default remains path-derived for local worktrees; CI supplies a reserved,
* ten-port-aligned band so its jobs never depend on the runner's default ports.
*/
function getConfiguredPortOffset(): number | undefined {
const raw = process.env.SUPABASE_WORKTREE_PORT_OFFSET
if (!raw)
return undefined
if (!/^\d+$/.test(raw))
throw new Error('SUPABASE_WORKTREE_PORT_OFFSET must be a non-negative integer.')
const offset = Number(raw)
if (!Number.isSafeInteger(offset) || offset % 10 !== 0 || offset > 11000)
throw new Error('SUPABASE_WORKTREE_PORT_OFFSET must be a multiple of 10 no greater than 11000.')
return offset
}
/**
* Returns a deterministic per-worktree Supabase configuration (unique project_id and ports)
* derived from the git worktree path and, when supplied, a CI job instance.
*
* This allows running multiple `supabase start` instances in parallel across worktrees
* without Docker container/volume name collisions or port conflicts.
*/
export function getSupabaseWorktreeConfig(cwd: string = process.cwd()): SupabaseWorktreeConfig {
const repoRootRaw = getGitRepoRoot(cwd)
const repoRoot = realpathSync(resolve(repoRootRaw))
const configuredOffset = getConfiguredPortOffset()
const instance = process.env.SUPABASE_WORKTREE_INSTANCE?.trim()
const identity = [repoRoot, instance, configuredOffset === undefined ? undefined : `ports:${configuredOffset}`]
.filter((value): value is string => Boolean(value))
.join('\0')
// Hashing the worktree plus job identity gives isolated Docker resources and generated config.
const worktreeHash = createHash('sha256').update(identity).digest('hex').slice(0, 8)
const offset = configuredOffset ?? (Number.parseInt(worktreeHash.slice(0, 6), 16) % 1000) * 10
const base = {
api: 54321,
db: 54322,
dbShadow: 54320,
dbPooler: 54329,
studio: 54323,
inbucket: 54324,
analytics: 54327,
edgeInspector: 8083,
} satisfies SupabaseWorktreePorts
return {
repoRoot,
worktreeHash,
projectId: `capgo-app-${worktreeHash}`,
ports: {
api: base.api + offset,
db: base.db + offset,
dbShadow: base.dbShadow + offset,
dbPooler: base.dbPooler + offset,
studio: base.studio + offset,
inbucket: base.inbucket + offset,
analytics: base.analytics + offset,
edgeInspector: base.edgeInspector + (offset / 10),
},
}
}