teqo/scripts/ci-scope.mjs
Francisco Solla b015af508d chore(OPS62): CI fail-fast estrutural — job único checks sequencial (X1)
ci-pr.yml e ci.yml deixam de ter jobs irmãos: 1 job 'checks' com steps em
série (guards → lint → format → typecheck → knip → cycles → unit → int →
build → e2e). Step vermelho interrompe o job, o run falha e o slot é
liberado — sem API de cancel (Forgejo 9.0.3 não tem), sem watchdog, sem
upgrade do Forgejo. Skips do ci-scope.mjs viram step id 'scope'; e2e em 1
processo com PLAYWRIGHT_WORKERS=4 (substitui 2 shards × 2 workers); 2
services Postgres por run com portas host derivadas do run_id (runs
concorrentes não colidem); contexto 'CI (PR) / checks' preservado (job id
checks — contrato do automerger OPS61); deploy OPS53 gated em needs checks.
Plumbing morto removido (e2eShardConfig/e2e_shards). Docs: AGENT-OPS,
TESTING.md, changelog. Impl: docs/plans/ops62-ci-fail-fast-forgejo-impl.md
(a intenção watchdog+upgrade fica como histórico do gate).
2026-08-18 19:43:35 -03:00

68 lines
1.8 KiB
JavaScript

#!/usr/bin/env node
/**
* ci-scope — single classifier for PR step-level skips. Prints JSON:
* { base, code, build, test, e2e } where each of code/build/test/e2e is the
* classify* / selectE2eSpecs result. Used by the `scope` step (id: scope) in
* ci-pr.yml — its outputs drive `if:` on the heavy steps.
*
* Base ref: $GITHUB_BASE_REF (PR target) or origin/main locally.
*/
import { execFileSync } from 'node:child_process'
import { E2E_AFFECTED_MANIFEST } from './lib/e2e-affected-manifest.mjs'
import {
classifyBuildScope,
classifyStaticScope,
classifyTestScope,
selectE2eSpecs,
} from './lib/test-affected-core.mjs'
const base = `origin/${process.env.GITHUB_BASE_REF ?? 'main'}`
let mergeBase
try {
mergeBase = execFileSync('git', ['merge-base', 'HEAD', base], { encoding: 'utf8' }).trim()
} catch {
const full = {
mode: 'full',
reason: `no merge-base with ${base} (shallow clone?)`,
}
console.log(
JSON.stringify({
base,
code: { mode: 'code', reason: full.reason },
build: { mode: 'build', reason: full.reason },
test: { ...full },
e2e: { mode: 'full', specs: [], reason: full.reason, unmapped: [] },
}),
)
process.exit(0)
}
const raw = execFileSync('git', ['diff', '--name-status', `${mergeBase}...HEAD`], {
encoding: 'utf8',
})
const files = raw
.split('\n')
.filter(Boolean)
.map((line) => {
const [status, path] = line.split('\t')
return { status, path }
})
const e2e = selectE2eSpecs(files, E2E_AFFECTED_MANIFEST)
if (e2e.unmapped.length > 0) {
console.error(
`[ci-scope] src/ paths with no e2e manifest mapping:\n ${e2e.unmapped.join('\n ')}`,
)
}
console.log(
JSON.stringify({
base,
code: classifyStaticScope(files),
build: classifyBuildScope(files),
test: classifyTestScope(files),
e2e,
}),
)