style: format gate-ci; preflight só teqo_test no gate:ci
Prettier em gate-ci.mjs e gate-push-local.md. db:doctor exigia DATABASE_URL de dev — worktrees sem .env bloqueavam o push antes de chegar no format:check.
This commit is contained in:
parent
644047d92d
commit
f4a41ccc11
2 changed files with 77 additions and 59 deletions
|
|
@ -28,7 +28,7 @@ Decisão de produto (2026-07-30, brief do lote CI): scripts DRY (`gate:fast`, `g
|
|||
|
||||
## Decisões travadas
|
||||
|
||||
- **knip fora do pre-push.** *(Revertido 2026-07-30 — `gate:ci` inclui knip para espelhar ci-pr.)* O grafo do knip é incompleto (não carrega `payload.config.ts`, ledger P3) e é lento para um hook; CI já o roda como erro bloqueante. **Rejeitado na v1:** knip no hook (custo por push + falsos positivos já fichados).
|
||||
- **knip fora do pre-push.** _(Revertido 2026-07-30 — `gate:ci` inclui knip para espelhar ci-pr.)_ O grafo do knip é incompleto (não carrega `payload.config.ts`, ledger P3) e é lento para um hook; CI já o roda como erro bloqueante. **Rejeitado na v1:** knip no hook (custo por push + falsos positivos já fichados).
|
||||
- **Sem pre-commit hook.** A divisão fica: nada no commit (commits intermediários de WIP são legítimos), tudo no push. **Rejeitado:** lint-staged no commit (adiciona toolchain e atrito sem pegar classe de erro que o push não pegue segundos depois).
|
||||
- **Escape hatch = `--no-verify` nativo, documentado; sem variável mágica própria.** **Rejeitado:** `SKIP_GATE=1` (mais uma convenção para manter; `--no-verify` é o padrão git que todo agente/humano já conhece).
|
||||
- **Hook chama `pnpm gate:push`, nunca inline de comandos.** Uma fonte de verdade editável em `package.json`. **Rejeitado:** manter a linha de shell no hook (foi o que causou o drift atual).
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* `e2e-affected.mjs` vs `origin/stage`). Build always runs. `migration-lock` is
|
||||
* GitHub-only.
|
||||
*
|
||||
* Requires local Postgres with `teqo_test` when tests or build run.
|
||||
* Escape hatch: `git push --no-verify`.
|
||||
* Preflight checks only `teqo_test` (ci-pr never touches the dev DB). Escape:
|
||||
* `git push --no-verify`.
|
||||
*/
|
||||
import { execFileSync, spawnSync } from 'node:child_process'
|
||||
import { readFileSync } from 'node:fs'
|
||||
|
|
@ -15,6 +15,8 @@ import { fileURLToPath } from 'node:url'
|
|||
|
||||
import { parse as parseEnv } from 'dotenv'
|
||||
|
||||
import { diagnoseDatabaseTarget } from './db-doctor.mjs'
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
|
||||
const readTestEnv = () => {
|
||||
|
|
@ -57,73 +59,89 @@ const run = (label, command, args, env = process.env) => {
|
|||
}
|
||||
}
|
||||
|
||||
const testScope = readJsonScript('scripts/test-affected.mjs')
|
||||
const e2eScope = readJsonScript('scripts/e2e-affected.mjs')
|
||||
const main = async () => {
|
||||
const testScope = readJsonScript('scripts/test-affected.mjs')
|
||||
const e2eScope = readJsonScript('scripts/e2e-affected.mjs')
|
||||
|
||||
console.log(`[gate:ci] vitest: ${testScope.mode} — ${testScope.reason} (base ${testScope.base})`)
|
||||
console.log(`[gate:ci] e2e: ${e2eScope.mode} — ${e2eScope.reason} (base ${e2eScope.base})`)
|
||||
if (e2eScope.unmapped?.length > 0) {
|
||||
console.error(
|
||||
`[gate:ci] e2e unmapped src/ paths:\n ${e2eScope.unmapped.join('\n ')}`,
|
||||
)
|
||||
}
|
||||
console.log(`[gate:ci] vitest: ${testScope.mode} — ${testScope.reason} (base ${testScope.base})`)
|
||||
console.log(`[gate:ci] e2e: ${e2eScope.mode} — ${e2eScope.reason} (base ${e2eScope.base})`)
|
||||
if (e2eScope.unmapped?.length > 0) {
|
||||
console.error(`[gate:ci] e2e unmapped src/ paths:\n ${e2eScope.unmapped.join('\n ')}`)
|
||||
}
|
||||
|
||||
run('preflight (db:doctor)', 'pnpm', ['db:doctor'])
|
||||
run('lint', 'pnpm', ['lint'])
|
||||
run('format:check', 'pnpm', ['format:check'])
|
||||
run('typecheck', 'pnpm', ['typecheck'])
|
||||
run('knip', 'pnpm', ['exec', 'knip'])
|
||||
run('check:cycles', 'pnpm', ['check:cycles'])
|
||||
console.log('\n[gate:ci] ▶ preflight (teqo_test)')
|
||||
const dbOk = await diagnoseDatabaseTarget({
|
||||
label: 'test',
|
||||
databaseUrl: dbEnv.DATABASE_URL,
|
||||
})
|
||||
if (!dbOk) {
|
||||
console.error('\n[gate:ci] ✗ preflight (teqo_test) failed — run `pnpm db:start`')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (testScope.mode === 'full') {
|
||||
run('test:unit (full)', 'pnpm', ['test:unit'])
|
||||
} else if (testScope.mode === 'changed') {
|
||||
run('test:unit (changed)', 'pnpm', [
|
||||
'test:unit',
|
||||
'--',
|
||||
'--changed',
|
||||
testScope.base,
|
||||
'--passWithNoTests',
|
||||
])
|
||||
} else {
|
||||
console.log('\n[gate:ci] ⊘ test:unit skipped (no src/tests blast radius)')
|
||||
}
|
||||
run('lint', 'pnpm', ['lint'])
|
||||
run('format:check', 'pnpm', ['format:check'])
|
||||
run('typecheck', 'pnpm', ['typecheck'])
|
||||
run('knip', 'pnpm', ['exec', 'knip'])
|
||||
run('check:cycles', 'pnpm', ['check:cycles'])
|
||||
|
||||
if (testScope.mode !== 'none') {
|
||||
run('migrate', 'pnpm', ['migrate'], dbEnv)
|
||||
run('db:seed:minimal', 'pnpm', ['db:seed:minimal'], dbEnv)
|
||||
if (testScope.mode === 'full') {
|
||||
run('test:int (full)', 'pnpm', ['test:int'])
|
||||
} else {
|
||||
run('test:int (changed)', 'pnpm', [
|
||||
'test:int',
|
||||
run('test:unit (full)', 'pnpm', ['test:unit'])
|
||||
} else if (testScope.mode === 'changed') {
|
||||
run('test:unit (changed)', 'pnpm', [
|
||||
'test:unit',
|
||||
'--',
|
||||
'--changed',
|
||||
testScope.base,
|
||||
'--passWithNoTests',
|
||||
])
|
||||
}
|
||||
} else {
|
||||
console.log('\n[gate:ci] ⊘ test:int skipped (no src/tests blast radius)')
|
||||
}
|
||||
|
||||
run('build', 'pnpm', ['build'], dbEnv)
|
||||
|
||||
if (e2eScope.mode === 'none') {
|
||||
console.log('\n[gate:ci] ⊘ e2e skipped (no e2e blast radius)')
|
||||
} else {
|
||||
run('playwright install chromium', 'pnpm', ['exec', 'playwright', 'install', 'chromium'])
|
||||
run('e2e migrate', 'pnpm', ['migrate'], dbEnv)
|
||||
run('e2e seed:minimal', 'pnpm', ['db:seed:minimal'], dbEnv)
|
||||
run('e2e build', 'pnpm', ['build'], { ...dbEnv, NEXT_DIST_DIR: '.next/e2e' })
|
||||
|
||||
const e2eEnv = { ...dbEnv, E2E_PROD: '1', CI: '1', NEXT_DIST_DIR: '.next/e2e' }
|
||||
if (e2eScope.mode === 'full') {
|
||||
run('test:e2e (full)', 'pnpm', ['test:e2e'], e2eEnv)
|
||||
} else {
|
||||
const specPaths = e2eScope.specs.map((name) => `tests/e2e/${name}.e2e.spec.ts`)
|
||||
run(`test:e2e (selected: ${e2eScope.specs.join(', ')})`, 'pnpm', ['test:e2e', '--', ...specPaths], e2eEnv)
|
||||
console.log('\n[gate:ci] ⊘ test:unit skipped (no src/tests blast radius)')
|
||||
}
|
||||
|
||||
if (testScope.mode !== 'none') {
|
||||
run('migrate', 'pnpm', ['migrate'], dbEnv)
|
||||
run('db:seed:minimal', 'pnpm', ['db:seed:minimal'], dbEnv)
|
||||
if (testScope.mode === 'full') {
|
||||
run('test:int (full)', 'pnpm', ['test:int'])
|
||||
} else {
|
||||
run('test:int (changed)', 'pnpm', [
|
||||
'test:int',
|
||||
'--',
|
||||
'--changed',
|
||||
testScope.base,
|
||||
'--passWithNoTests',
|
||||
])
|
||||
}
|
||||
} else {
|
||||
console.log('\n[gate:ci] ⊘ test:int skipped (no src/tests blast radius)')
|
||||
}
|
||||
|
||||
run('build', 'pnpm', ['build'], dbEnv)
|
||||
|
||||
if (e2eScope.mode === 'none') {
|
||||
console.log('\n[gate:ci] ⊘ e2e skipped (no e2e blast radius)')
|
||||
} else {
|
||||
run('playwright install chromium', 'pnpm', ['exec', 'playwright', 'install', 'chromium'])
|
||||
run('e2e migrate', 'pnpm', ['migrate'], dbEnv)
|
||||
run('e2e seed:minimal', 'pnpm', ['db:seed:minimal'], dbEnv)
|
||||
run('e2e build', 'pnpm', ['build'], { ...dbEnv, NEXT_DIST_DIR: '.next/e2e' })
|
||||
|
||||
const e2eEnv = { ...dbEnv, E2E_PROD: '1', CI: '1', NEXT_DIST_DIR: '.next/e2e' }
|
||||
if (e2eScope.mode === 'full') {
|
||||
run('test:e2e (full)', 'pnpm', ['test:e2e'], e2eEnv)
|
||||
} else {
|
||||
const specPaths = e2eScope.specs.map((name) => `tests/e2e/${name}.e2e.spec.ts`)
|
||||
run(
|
||||
`test:e2e (selected: ${e2eScope.specs.join(', ')})`,
|
||||
'pnpm',
|
||||
['test:e2e', '--', ...specPaths],
|
||||
e2eEnv,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n[gate:ci] ✓ all checks passed (ci-pr mirror, affected scope)')
|
||||
}
|
||||
|
||||
console.log('\n[gate:ci] ✓ all checks passed (ci-pr mirror, affected scope)')
|
||||
await main()
|
||||
|
|
|
|||
Loading…
Reference in a new issue