mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
pnpm worktree next [--go] [--no-migrate] agora provisiona um ambiente determinístico por worktree: slot = parte numérica do código da Issue (C15→15; hash sem dígitos; bump em colisão entre worktrees vivos), dev server em 3100+slot, bancos teqo_wt<slot> / teqo_wt<slot>_test no container compartilhado (-p teqo) com a cadeia de migrations aplicada; escreve .env.local + .env.test.local (gitignored, marcador gerado; envs manuais nunca sobrescritos) e copia PAYLOAD_SECRET/Blob/VAPID do main. kill remove o worktree e dropa os bancos gerados (WITH FORCE, best-effort). Suites leem .env.test.local depois de .env.test (vitest.setup / playwright / db-doctor / gate-ci espelham o layering) e assertTestDatabase aceita teqo_test ou teqo_<worktree>_test em localhost só (fail-closed preservado). db:start/db:stop fixam -p teqo (um container de qualquer diretório); max_connections 200→400. Sem Docker (Cursor Cloud): degrada ao compartilhado com aviso. Derivação pura em scripts/lib/worktree-env.mjs (unit-tested); fluxo validado end-to-end: provision → migrate → 598/598 int no banco do worktree → kill sem resíduo.
67 lines
2.7 KiB
JavaScript
67 lines
2.7 KiB
JavaScript
/**
|
|
* Pure helpers for the per-worktree environment provisioning in
|
|
* `scripts/worktree.mjs` — deterministic port + database names derived from
|
|
* the branch, so parallel agents (each in their own worktree) never fight
|
|
* over port 3000, the shared `teqo_test` database, or a second Postgres
|
|
* container on 5432.
|
|
*
|
|
* Contract: `slot` is the single collision domain. Port and BOTH database
|
|
* names derive from the final slot, so a bumped slot frees all three at
|
|
* once. Deterministic for a given (branch, code, live taken-slots set) —
|
|
* the same "depends on live state" determinism the claim-queue branch picker
|
|
* already has.
|
|
*/
|
|
|
|
/** Dev-server ports start here: slot 15 → http://localhost:3115. */
|
|
export const DEV_PORT_BASE = 3100
|
|
|
|
/** Marker line identifying a generated env file (never overwrite manual ones). */
|
|
export const GENERATED_ENV_MARKER = '# teqo worktree env (generated by pnpm worktree next)'
|
|
|
|
/** Slot cap — ports above 3100 + 999 would leave the usual ephemeral range. */
|
|
const MAX_SLOT = 999
|
|
|
|
/** First run of digits in the issue code (`C15` → 15, `B164` → 164); null when none. */
|
|
export const numericSlotOfCode = (code) => {
|
|
const match = /([0-9]+)/.exec(code ?? '')
|
|
return match ? Number(match[1]) : null
|
|
}
|
|
|
|
/** Stable 32-bit FNV-1a hash → slot in [0, MAX_SLOT] for codes without digits. */
|
|
export const hashSlotOf = (value) => {
|
|
let hash = 0x811c9dc5
|
|
for (const byte of Buffer.from(String(value), 'utf8')) {
|
|
hash ^= byte
|
|
hash = Math.imul(hash, 0x01000193) >>> 0
|
|
}
|
|
return hash % (MAX_SLOT + 1)
|
|
}
|
|
|
|
/** The slot a branch prefers before collision resolution. */
|
|
const preferredSlotForBranch = (branch, code) => {
|
|
const numeric = numericSlotOfCode(code)
|
|
return numeric !== null && numeric <= MAX_SLOT ? numeric : hashSlotOf(branch)
|
|
}
|
|
|
|
export const devPortForSlot = (slot) => DEV_PORT_BASE + slot
|
|
export const devDatabaseForSlot = (slot) => `teqo_wt${slot}`
|
|
export const testDatabaseForSlot = (slot) => `teqo_wt${slot}_test`
|
|
|
|
/** DB name charset guard — derived names are fed into `CREATE DATABASE` SQL. */
|
|
export const isGeneratedDatabaseName = (name) => /^teqo_wt[0-9]+(_test)?$/.test(name ?? '')
|
|
|
|
/**
|
|
* Final environment for a branch. `takenSlots` = slots already claimed by
|
|
* OTHER live worktrees; bumps +1 until free (bounded — live slot sets are
|
|
* tiny). Every value a consumer needs derives from the one final slot.
|
|
*/
|
|
export const worktreeEnvironment = ({ branch, code, takenSlots = new Set() }) => {
|
|
let slot = preferredSlotForBranch(branch, code)
|
|
while (takenSlots.has(slot)) slot += 1
|
|
return {
|
|
slot,
|
|
devPort: devPortForSlot(slot),
|
|
devDatabase: devDatabaseForSlot(slot),
|
|
testDatabase: testDatabaseForSlot(slot),
|
|
}
|
|
}
|