文件
hyb-oa/scripts/qa/tests/fixtures/release-deploy-fake.cjs

90 行
5.3 KiB
JavaScript

// Private subprocess fixture. There is deliberately no real-network fallback.
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const root = process.env.HYB_RELEASE_FIXTURE_ROOT;
if (!root || !path.basename(root).startsWith('hyb-release-shell-')) process.exit(97);
const args = process.argv.slice(2), name = path.basename(process.argv[1]);
const trace = path.join(root, 'artifacts/commands.jsonl');
fs.mkdirSync(path.dirname(trace), { recursive: true });
fs.appendFileSync(trace, JSON.stringify({ name, args }) + '\n');
function run(command, values) {
const result = spawnSync(command, values, { cwd: root, env: process.env, stdio: 'inherit' });
process.exit(result.status ?? 97);
}
function output(value) { process.stdout.write(value + '\n'); process.exit(0); }
function git(values) {
const result = spawnSync('/usr/bin/git', values, { cwd: root, env: process.env, encoding: 'utf8' });
if (result.status !== 0) process.exit(97);
return result.stdout.trim();
}
function mutate(point) {
if (process.env.HYB_MUTATE_AT !== point || fs.existsSync(path.join(root, 'artifacts/mutated'))) return;
fs.writeFileSync(path.join(root, 'artifacts/mutated'), point);
const kind = process.env.HYB_MUTATE_KIND || 'tracked';
if (kind === 'receipt') {
const file = path.join(process.env.RELEASE_GATE_RECEIPT_DIR, 'release-result.json');
const record = JSON.parse(fs.readFileSync(file)); record.executionExit = 23;
fs.writeFileSync(file, JSON.stringify(record));
} else if (kind === 'head') {
git(['-c', 'user.name=Fixture', '-c', '[email protected]', 'commit', '--allow-empty', '-qm', 'changed']);
} else {
fs.appendFileSync(path.join(root, 'client/package.json'), ' ');
if (kind === 'index') git(['add', 'client/package.json']);
}
}
const fullCommit = () => git(['rev-parse', 'HEAD']);
const contract = 'REMOTE_DIR=/fixture/app REMOTE_ENV_FILE=/fixture/app/deploy/trialrun/.env COMPOSE_FILE=deploy/trialrun/docker-compose.yml COMPOSE_PROJECT_NAME=fixture-project POSTGRES_DATA_DIR=/fixture/db STORAGE_HOST_DIR=/fixture/storage';
if (name === 'git') {
if (args[0] === 'show' && args[1] === '34181e3:deploy/trialrun/deploy.sh') output('DEPLOY_SERVER="${DEPLOY_SERVER:-fixture-target}"');
if (args[0] === 'show' && args[1] === '5e2d038938265c926e06d3cc6d0841f6dcc06148:deploy/trialrun/deploy.sh') output('COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-fixture-project}"');
const values = args[0] === '-C' && args[1] === root ? args.slice(2) : args;
if (['ls-files', 'status', 'rev-parse', 'archive'].includes(values[0])) run('/usr/bin/git', values);
process.exit(97);
}
if (name === 'node') {
if (args[0] === '-e') run(process.execPath, args);
const file = path.basename(args[0] || '');
if (file === 'release-gate-result.mjs' || file === 'production-identity-bootstrap.mjs') {
run(process.execPath, ['--require', path.join(root, 'bin/fs-guard.cjs'), ...args]);
}
if (file === 'check-source-provenance.mjs') {
mutate('provenance');
const commit = process.env.HYB_WRONG_SOURCE ? 'f'.repeat(40) : fullCommit();
output(JSON.stringify({ commit, branch: 'fixture', published: true, remoteVerified: true, overrideUsed: false, buildEnv: { BUILD_SHA: commit, VITE_BUILD_SHA: commit.slice(0, 12) } }));
}
if (file === 'production-preflight.mjs') { mutate('preflight'); process.exit(0); }
if (file === 'validate-env-contract.js' && process.env.FAIL_STATIC) process.exit(23);
if (['validate-env-contract.js', 'check-public-bundle-copy.mjs', 'check-ui-design-rules.mjs', 'check-error-reason-contract.mjs', 'check-mobile-route-contract.mjs', 'check-flow-coverage.mjs', 'check-live-smoke-env.mjs'].includes(file)) process.exit(0);
process.exit(97);
}
if (name === 'bash') {
const file = path.basename(args[0] || '');
if (['run-release-gate.sh', 'deploy-existing.sh', 'deploy.sh'].includes(file)) run('/bin/bash', args);
if (file === 'run-web-gates.sh' && args[1] === 'full-local') {
if (process.env.OMIT_EVIDENCE) process.exit(0);
run(process.execPath, [path.join(root, 'bin/evidence.mjs')]);
}
if (['run-web-gates.sh', 'run-business-flow-gates.sh', 'run-live-smoke-readiness.sh', 'check-local-migration-drift.sh', 'collect-failure-artifacts.sh', 'check-existing-deployment.sh'].includes(file)) process.exit(0);
process.exit(97);
}
if (name === 'ssh') {
const last = args.at(-1) || '';
if (last === 'probe') output('[deployment-contract-refresh][PASS] probe');
if (last === 'refresh') output('[deployment-contract-refresh][PASS] refreshed');
if (last.startsWith('systemctl show ')) output(contract);
if (last.includes('bash -s')) {
fs.readFileSync(0); // Drain only the synthetic script; never execute it.
if (last.includes("IDENTITY_BOOTSTRAP_MODE='origin'")) { mutate('origin'); output('[production-identity-bootstrap][PASS] origin_identity'); }
if (last.includes("IDENTITY_BOOTSTRAP_MODE='database'")) output('[production-identity-bootstrap][DB_SHA256] ' + 'a'.repeat(64));
if (!last.includes('BUILD_SHA=')) mutate('remote-preflight');
process.exit(0);
}
if (last === "mkdir -p '/fixture/app'") process.exit(0);
process.exit(97);
}
if (name === 'rsync') process.exit(0);
if (name === 'curl') output(JSON.stringify({ data: { buildSha: fullCommit().slice(0, 12) } }));
if (name === 'npm' || name === 'python3') process.exit(0);
process.exit(97);