teqo-backup/scripts/ensure-repo-deps.mjs
Francisco Solla e04db48742 feat(ops): auto-promote stage→main e merge em stage sem exigir head (#68)
* feat(ops): auto-promote stage→main e merge em stage sem exigir head

Substitui o promote manual por workflow disparado no ci-stage green,
relaxa strict na proteção de stage (script configure:branch-protection)
e documenta o novo paradigma em AGENT-OPS e work-issue.

* style: format AGENT-OPS and configure-branch-protection for ci-pr

* feat(ops): pnpm push com ensure-repo-deps determinístico

Bootstrap install + Husky antes de todo push (pnpm push e pre-push).
Documenta fluxo canônico em work-issue, agent-pr-workflow e AGENT-OPS.

* fix(ops): pnpm push roda gate no script, sem depender do Husky

Cloud environment.json chama prepare explicitamente após install.

* test(int): isolate advisor portfolio search from shared cairu slug

Parallel int specs were stomping municipality.advisors on the fixed
cairu row; allocate per-test municipalities instead.
2026-07-30 21:12:52 -03:00

57 lines
1.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Idempotent bootstrap for worktrees/clones before hooks or gate scripts run.
* Installs deps when `node_modules` is missing/stale and wires Husky hooks.
*
* Used by `.husky/pre-push` and `pnpm push` — no imports from `scripts/lib/`
* (those need `node_modules` first).
*/
import { execFileSync, spawnSync } from 'node:child_process'
import { existsSync, statSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const runOrDie = (label, command, args) => {
console.log(`[ensure-deps] ${label}`)
const result = spawnSync(command, args, { cwd: repoRoot, stdio: 'inherit' })
if ((result.status ?? 1) !== 0) {
console.error(`[ensure-deps] failed: ${command} ${args.join(' ')}`)
process.exit(result.status ?? 1)
}
}
const needsInstall = () => {
const nodeModules = path.join(repoRoot, 'node_modules')
const lockfile = path.join(repoRoot, 'pnpm-lock.yaml')
if (!existsSync(nodeModules) || !existsSync(path.join(nodeModules, '.pnpm'))) return true
if (!existsSync(path.join(nodeModules, '.bin', 'prettier'))) return true
if (!existsSync(lockfile)) return false
return statSync(lockfile).mtimeMs > statSync(nodeModules).mtimeMs
}
const huskyHooksMissing = () => {
try {
const hooksPath = execFileSync('git', ['config', 'core.hooksPath'], {
cwd: repoRoot,
encoding: 'utf8',
}).trim()
return !hooksPath.includes('husky')
} catch {
return true
}
}
export const ensureRepoDeps = () => {
if (needsInstall()) {
runOrDie('pnpm install --frozen-lockfile', 'pnpm', ['install', '--frozen-lockfile'])
}
if (huskyHooksMissing()) {
runOrDie('pnpm run prepare (husky)', 'pnpm', ['run', 'prepare'])
}
}
if (process.argv[1]?.endsWith('ensure-repo-deps.mjs')) {
ensureRepoDeps()
}