Jenkins 定时构建增加版本未变化去重
- Full Build 改为每小时触发,并按已解析 commit 与全部构建参数判断是否重复 - AGC Windows Build 增加每小时触发与同一套去重逻辑,跳过时不再安装依赖、构建和上传 - 跳过记录为 NOT_BUILT,越过跳过记录后最近一次实际构建成功才允许跳过,失败或中断可重试 - 生产运维门禁补充两个 Job 的去重约束与各阶段跳过检查 - 同步开发运维文档与共享开发工作流说明
This commit is contained in:
@@ -605,7 +605,7 @@ const checks = [
|
||||
includes:
|
||||
"choice(name: 'STDB_API_ROLLOUT_MODE', choices: ['normal', 'pause-after-stdb']",
|
||||
reason:
|
||||
'Full Build 的 04:00 定时任务必须默认 normal 完整发布仅供开发使用的 dev 服务器。',
|
||||
'Full Build 的每小时定时任务必须默认 normal 完整发布仅供开发使用的 dev 服务器。',
|
||||
},
|
||||
{
|
||||
file: 'jenkins/Jenkinsfile.production-full-build-and-deploy',
|
||||
@@ -7639,6 +7639,107 @@ const fullPipelineContent = readFileSync(
|
||||
'jenkins/Jenkinsfile.production-full-build-and-deploy',
|
||||
'utf8',
|
||||
);
|
||||
for (const stageName of [
|
||||
'Build Components',
|
||||
'Deploy Stdb',
|
||||
'Stdb / Api Rollout Gate',
|
||||
'Deploy Api',
|
||||
'Deploy Web',
|
||||
'Exit Maintenance',
|
||||
]) {
|
||||
const stageStart = fullPipelineContent.indexOf(`stage('${stageName}')`);
|
||||
const stageBody =
|
||||
stageStart >= 0 ? fullPipelineContent.slice(stageStart) : '';
|
||||
const stageConditions = stageBody.split(/\b(?:steps|parallel)\s*\{/u)[0];
|
||||
if (
|
||||
!stageConditions.includes(
|
||||
"expression { return env.SKIP_UNCHANGED_BUILD != 'true' }",
|
||||
)
|
||||
) {
|
||||
failed = true;
|
||||
console.error(
|
||||
`[check:production-ops] Full Build 的 ${stageName} 必须在版本未变化时跳过,禁止触发下游构建、部署或维护操作。`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const [snippet, reason] of [
|
||||
["cron('H * * * *')", '必须每小时检查源码变化'],
|
||||
[
|
||||
"causes.any { it._class != 'hudson.triggers.TimerTrigger$TimerTriggerCause' }",
|
||||
'去重只允许纯 timer 触发,手动或重放必须执行',
|
||||
],
|
||||
[
|
||||
"previous.result == 'NOT_BUILT' && variables.SKIP_UNCHANGED_BUILD == 'true'",
|
||||
'只允许越过本逻辑标记的跳过记录',
|
||||
],
|
||||
[
|
||||
"previous.result == 'SUCCESS' && variables.SOURCE_COMMIT == sourceCommit && variables.FULL_BUILD_INPUTS == buildInputs",
|
||||
'必须同时验证最近实际运行成功、源码相同和参数相同',
|
||||
],
|
||||
[
|
||||
"groovy.json.JsonOutput.toJson(params.findAll { name, value -> name != 'NOTIFICATION_EMAILS' }.sort())",
|
||||
'必须稳定记录全部构建发布参数,仅排除通知邮箱',
|
||||
],
|
||||
[
|
||||
"currentBuild.result = 'NOT_BUILT'",
|
||||
'跳过记录必须为 NOT_BUILT,不能污染成功基线',
|
||||
],
|
||||
]) {
|
||||
if (!fullPipelineContent.includes(snippet)) {
|
||||
failed = true;
|
||||
console.error(`[check:production-ops] Full Build ${reason}。`);
|
||||
}
|
||||
}
|
||||
if (
|
||||
!/always\s*\{\s*script\s*\{\s*if \(env\.SKIP_UNCHANGED_BUILD == 'true'\)\s*\{\s*return\s*\}/u.test(
|
||||
fullPipelineContent,
|
||||
)
|
||||
) {
|
||||
failed = true;
|
||||
console.error(
|
||||
'[check:production-ops] Full Build 跳过时不得触发结束邮件通知。',
|
||||
);
|
||||
}
|
||||
const agcPipelineContent = readFileSync(
|
||||
'jenkins/Jenkinsfile.ai-game-creator-shell-build',
|
||||
'utf8',
|
||||
);
|
||||
for (const snippet of [
|
||||
"cron('H * * * *')",
|
||||
"causes.any { it._class != 'hudson.triggers.TimerTrigger$TimerTriggerCause' }",
|
||||
"previous.result == 'NOT_BUILT' && variables.SKIP_UNCHANGED_BUILD == 'true'",
|
||||
"previous.result == 'SUCCESS' && variables.SOURCE_COMMIT == sourceCommit && variables.FULL_BUILD_INPUTS == buildInputs",
|
||||
'env.FULL_BUILD_INPUTS = groovy.json.JsonOutput.toJson(params.sort())',
|
||||
"currentBuild.result = 'NOT_BUILT'",
|
||||
]) {
|
||||
if (!agcPipelineContent.includes(snippet)) {
|
||||
failed = true;
|
||||
console.error(
|
||||
`[check:production-ops] AGC Windows Build 缺少版本未变化去重约束: ${snippet}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const agcStageName of [
|
||||
'Toolchain preflight',
|
||||
'Install dependencies',
|
||||
'Build and upload',
|
||||
'Archive release',
|
||||
]) {
|
||||
const agcStageStart = agcPipelineContent.indexOf(`stage('${agcStageName}')`);
|
||||
const agcStageBody =
|
||||
agcStageStart >= 0 ? agcPipelineContent.slice(agcStageStart) : '';
|
||||
const agcStageConditions = agcStageBody.split(/\bsteps\s*\{/u)[0];
|
||||
if (
|
||||
!agcStageConditions.includes(
|
||||
"expression { return env.SKIP_UNCHANGED_BUILD != 'true' }",
|
||||
)
|
||||
) {
|
||||
failed = true;
|
||||
console.error(
|
||||
`[check:production-ops] AGC Windows Build 的 ${agcStageName} 必须在版本未变化时跳过。`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const forcedBuildOnlyCalls = fullPipelineContent.match(
|
||||
/booleanParam\(name: 'PUBLISH_AFTER_BUILD', value: false\)/gu,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user