加固既有生产部署流程(07-27-yang-jun-reimbursement-readiness)
这个提交包含在:
@@ -16,6 +16,9 @@ const checklistPath = path.join(rootDir, 'deploy/trialrun/checklist.md');
|
||||
const deployScriptPath = path.resolve(
|
||||
process.env.ENV_CONTRACT_DEPLOY_SCRIPT_PATH || path.join(rootDir, 'deploy/trialrun/deploy.sh'),
|
||||
);
|
||||
const deployExistingScriptPath = path.join(rootDir, 'deploy/trialrun/deploy-existing.sh');
|
||||
const remotePreflightScriptPath = path.join(rootDir, 'deploy/trialrun/preflight-remote.sh');
|
||||
const dockerfilePath = path.join(rootDir, 'deploy/trialrun/Dockerfile');
|
||||
const timerScriptPath = path.resolve(
|
||||
process.env.ENV_CONTRACT_TIMER_SCRIPT_PATH
|
||||
|| path.join(rootDir, 'deploy/trialrun/install-warch3-ops-timer.sh'),
|
||||
@@ -155,6 +158,9 @@ const composeContent = readFile(composePath);
|
||||
const runbookContent = readFile(runbookPath);
|
||||
const checklistContent = readFile(checklistPath);
|
||||
const deployScriptContent = readFile(deployScriptPath);
|
||||
const deployExistingScriptContent = readFile(deployExistingScriptPath);
|
||||
const remotePreflightScriptContent = readFile(remotePreflightScriptPath);
|
||||
const dockerfileContent = readFile(dockerfilePath);
|
||||
const timerScriptContent = readFile(timerScriptPath);
|
||||
const automaticBackupTimerScriptContent = readFile(automaticBackupTimerScriptPath);
|
||||
const offhostReceiverInstallerContent = readFile(offhostReceiverInstallerPath);
|
||||
@@ -439,12 +445,52 @@ for (const requiredFragment of [
|
||||
'DEPLOY_DATA_MODE" != "existing"',
|
||||
'DEPLOY_DATA_MODE" != "initialize"',
|
||||
'"$POSTGRES_DATA_DIR/PG_VERSION"',
|
||||
'>>> 3. 同步前只读校验远端部署合同',
|
||||
'REMOTE_PREFLIGHT_SCRIPT="deploy/trialrun/preflight-remote.sh"',
|
||||
]) {
|
||||
if (!deployScriptContent.includes(requiredFragment)) {
|
||||
errors.push(`deploy.sh 缺少正式部署数据目录护栏: ${requiredFragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (deployScriptContent.indexOf('>>> 3. 同步前只读校验远端部署合同')
|
||||
> deployScriptContent.indexOf('rsync -az --delete')) {
|
||||
errors.push('deploy.sh 必须在 rsync 前执行远端只读 preflight');
|
||||
}
|
||||
|
||||
for (const requiredFragment of [
|
||||
'systemctl show',
|
||||
'DEPLOY_DATA_MODE=existing',
|
||||
'SYNC_ENV=0',
|
||||
'路径与标识不回显',
|
||||
]) {
|
||||
if (!deployExistingScriptContent.includes(requiredFragment)) {
|
||||
errors.push(`deploy-existing.sh 缺少既有部署合同: ${requiredFragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const requiredFragment of [
|
||||
'REMOTE_DIR 是现存仓库根目录',
|
||||
'SYNC_ENV=0 要求同步前已有普通远端 .env 文件',
|
||||
'"$POSTGRES_DATA_DIR/PG_VERSION"',
|
||||
]) {
|
||||
if (!remotePreflightScriptContent.includes(requiredFragment)) {
|
||||
errors.push(`preflight-remote.sh 缺少同步前护栏: ${requiredFragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [earlier, later] of [
|
||||
['RUN apk add --no-cache bash curl', 'ARG VITE_BUILD_SHA'],
|
||||
['RUN cd client && npm ci', 'ARG VITE_BUILD_SHA'],
|
||||
['RUN cd server && npm ci', 'ARG BUILD_SHA'],
|
||||
]) {
|
||||
if (dockerfileContent.indexOf(earlier) === -1
|
||||
|| dockerfileContent.indexOf(later) === -1
|
||||
|| dockerfileContent.indexOf(earlier) > dockerfileContent.indexOf(later)) {
|
||||
errors.push(`Dockerfile 缓存顺序无效: ${earlier} 必须早于 ${later}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const { filePath, content } of nginxContents) {
|
||||
const relativePath = path.relative(rootDir, filePath);
|
||||
if (content.includes('$proxy_add_x_forwarded_for')) {
|
||||
|
||||
在新工单中引用
屏蔽一个用户