实现(08-08-production-deployment-execution): 校准生产身份只读门禁
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-08 17:33:25 +08:00
父节点 6ccb9cd5e3
当前提交 f11e2f2c25
共修改 11 个文件,包含 666 行新增和 30 行删除
+47 -1
查看文件
@@ -43,6 +43,14 @@ const productionPreflightRemoteScriptPath = path.join(
rootDir,
'deploy/trialrun/production-preflight-remote.sh',
);
const productionIdentityBootstrapScriptPath = path.join(
rootDir,
'deploy/trialrun/production-identity-bootstrap.mjs',
);
const productionIdentityBootstrapRemoteScriptPath = path.join(
rootDir,
'deploy/trialrun/production-identity-bootstrap-remote.sh',
);
const dockerfilePath = path.join(rootDir, 'deploy/trialrun/Dockerfile');
const timerScriptPath = path.resolve(
process.env.ENV_CONTRACT_TIMER_SCRIPT_PATH
@@ -279,6 +287,8 @@ const deploymentRefreshHelperContent = readFile(deploymentRefreshHelperPath);
const remotePreflightScriptContent = readFile(remotePreflightScriptPath);
const productionPreflightScriptContent = readFile(productionPreflightScriptPath);
const productionPreflightRemoteScriptContent = readFile(productionPreflightRemoteScriptPath);
const productionIdentityBootstrapScriptContent = readFile(productionIdentityBootstrapScriptPath);
const productionIdentityBootstrapRemoteScriptContent = readFile(productionIdentityBootstrapRemoteScriptPath);
const dockerfileContent = readFile(dockerfilePath);
const timerScriptContent = readFile(timerScriptPath);
const automaticBackupTimerScriptContent = readFile(automaticBackupTimerScriptPath);
@@ -604,8 +614,12 @@ for (const requiredFragment of [
for (const requiredFragment of [
'EXPECTED_PRODUCTION_ORIGIN',
'EXPECTED_PRODUCTION_DB_NAME',
'EXPECTED_PRODUCTION_DB_NAME_SHA256',
'EXPECTED_PENDING_MIGRATIONS',
'EXPECTED_DEPLOY_SERVER',
'EXPECTED_REMOTE_DIR',
'EXPECTED_COMPOSE_PROJECT_NAME',
'PRODUCTION_READ_ONLY_PREFLIGHT',
'production-preflight.mjs',
'CANDIDATE_COMMIT',
'PRODUCTION_PREFLIGHT_REPORT_DIR',
@@ -616,6 +630,34 @@ for (const requiredFragment of [
}
}
for (const requiredFragment of [
'HISTORICAL_TARGET_COMMIT',
'HISTORICAL_PROJECT_COMMIT',
'discoverRefreshTarget',
'assertExactHistoricalContract',
'bootstrapDatabaseIdentityHash',
'EXPECTED_PRODUCTION_DB_NAME_SHA256',
'DEPLOY_EXISTING_SCRIPT',
]) {
if (!productionIdentityBootstrapScriptContent.includes(requiredFragment)) {
errors.push(`production-identity-bootstrap.mjs 缺少生产身份引导合同: ${requiredFragment}`);
}
}
for (const requiredFragment of [
'EXPECTED_PRODUCTION_ORIGIN_SHA256',
'PROD_TARGET_RUNTIME_ORIGIN_MISMATCH',
'PROD_TARGET_DB_NON_PRODUCTION_IDENTITY',
'DB_SHA256',
]) {
if (!productionIdentityBootstrapRemoteScriptContent.includes(requiredFragment)) {
errors.push(`production-identity-bootstrap-remote.sh 缺少只读身份合同: ${requiredFragment}`);
}
}
if (productionIdentityBootstrapRemoteScriptContent.includes('prisma migrate status')
|| productionIdentityBootstrapRemoteScriptContent.includes('migrate deploy')) {
errors.push('production-identity-bootstrap-remote.sh 不得进入 Prisma status 或迁移写入');
}
for (const requiredFragment of [
'systemctl show',
'DEPLOY_CONTRACT_KEYS',
@@ -735,6 +777,10 @@ if (deployExistingScriptContent.indexOf('node "$ROOT_DIR/$PRODUCTION_PREFLIGHT_S
> deployExistingScriptContent.indexOf('bash "$ROOT_DIR/deploy/trialrun/deploy.sh"')) {
errors.push('deploy-existing.sh 必须在 deploy.sh 前执行生产目标与迁移 preflight');
}
if (deployExistingScriptContent.indexOf('PRODUCTION_READ_ONLY_PREFLIGHT')
> deployExistingScriptContent.indexOf('bash "$ROOT_DIR/deploy/trialrun/deploy.sh"')) {
errors.push('deploy-existing.sh 的只读停止点必须位于 deploy.sh 前');
}
if (!deployScriptContent.includes('DEPLOY_STATUS_OUTPUT_MODE')) {
errors.push('deploy.sh 必须支持受控发布 wrapper 的 quiet migration status 输出模式');
}