实现(08-08-production-deployment-execution): 校准生产身份只读门禁
这个提交包含在:
@@ -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 输出模式');
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户