From e4ee999098b2f597e57e5bcfa1e8d4f3ce148630 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 2 Sep 2026 20:14:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 32 +++++++++++++------ ...est-environment-pipeline-contract.test.mjs | 6 +++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index cedb33e92..b9f53cf7f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -60,14 +60,15 @@ def databaseMigrationPaths = ['deploy/sql/'] def downstreamResults = [:] def parseAutoBuildState(String description, List allowedServices) { - String marker = ' | RESULTS' - if (!description?.startsWith('AUTO ') || !description.contains(marker)) return null - int markerIndex = description.indexOf(marker) + if (!description?.startsWith('AUTO ')) return null + int markerIndex = description.indexOf(' | ') + if (markerIndex < 0) return null String commit = description.substring('AUTO '.length(), markerIndex).trim() if (!(commit ==~ /[0-9a-fA-F]{7,40}/)) return null - String summary = description.substring(markerIndex + marker.length()).trim() + String state = description.substring(markerIndex + ' | '.length()).trim() List failedServices = [] - if (summary) { + if (state.startsWith('RESULTS')) { + String summary = state.substring('RESULTS'.length()).trim() summary.split(',').collect { it.trim() }.findAll { it }.each { item -> int separator = item.indexOf('=') if (separator > 0) { @@ -76,6 +77,17 @@ def parseAutoBuildState(String description, List allowedServices) { if (allowedServices.contains(service) && result != 'SUCCESS') failedServices << service } } + } else if (state.startsWith('PENDING')) { + // 上一轮若在统一测试、排队或并行部署期间失败/取消,description 仍是 PENDING。 + // 将该轮已选服务作为精确补跑范围,同时让 Git 基线前进到该轮提交, + // 避免回退到很早的成功构建后把历史公共文件再次解释成全量变更。 + String selected = state.substring('PENDING'.length()).trim() + int suffixIndex = selected.indexOf(' | ') + if (suffixIndex >= 0) selected = selected.substring(0, suffixIndex).trim() + failedServices.addAll(selected.split(',').collect { it.trim() } + .findAll { allowedServices.contains(it) }) + } else { + return null } return [commit: commit, failedServices: failedServices.unique()] } @@ -85,7 +97,8 @@ def previousAutoBuildState(def previousBuild, List allowedServices) { int inspected = 0 while (candidate != null && inspected < 30) { def state = parseAutoBuildState(candidate.description, allowedServices) - if (state != null && ['SUCCESS', 'UNSTABLE'].contains(candidate.result)) return state + // 完成的 RESULTS 和中断留下的 PENDING 都能推进 AUTO 基线;PENDING 中的服务会被精确补跑。 + if (state != null) return state candidate = candidate.previousBuild inspected++ } @@ -190,9 +203,10 @@ pipeline { String base = autoState?.commit?.trim() List retryServices = autoState?.failedServices ?: [] if (!base) { - // 首次启用新算法时兼容旧构建记录;后续只读取标记为 AUTO 的完成记录, - // SELECT/ALL 不会推进 AUTO 基线。 - base = env.GIT_PREVIOUS_SUCCESSFUL_COMMIT?.trim() + // 首次启用或旧构建记录已淘汰时,只比较当前提交的直接父提交。 + // 需要初始化全部服务时应显式使用 ALL,不能因历史成功基线过旧而意外全量部署。 + base = sh(script: 'git rev-parse HEAD^ 2>/dev/null || true', returnStdout: true).trim() + if (base) echo "AUTO 未找到历史记录,本次使用直接父提交作为基线: ${base}" } if (!base || sh(script: "git cat-file -e ${base}^{commit} 2>/dev/null", returnStatus: true) != 0) { selected = serviceModules diff --git a/scripts/test-environment-pipeline-contract.test.mjs b/scripts/test-environment-pipeline-contract.test.mjs index 70c1ea8f9..dd9fc812a 100644 --- a/scripts/test-environment-pipeline-contract.test.mjs +++ b/scripts/test-environment-pipeline-contract.test.mjs @@ -37,13 +37,17 @@ test('coordinator jobs present their own deployment environment as the default c assert.match(coordinatorPipeline, /downstreamJobPrefixForJob\(env\.JOB_BASE_NAME\)/) }) -test('AUTO keeps its own baseline and retries only previously failed services', () => { +test('AUTO advances interrupted baselines and retries only their selected services', () => { assert.match(coordinatorPipeline, /def parseAutoBuildState\(/) assert.match(coordinatorPipeline, /def previousAutoBuildState\(/) assert.match(coordinatorPipeline, /previousAutoBuildState\(currentBuild\.previousBuild, serviceModules\)/) + assert.match(coordinatorPipeline, /state\.startsWith\('PENDING'\)/) + assert.match(coordinatorPipeline, /failedServices\.addAll\(selected\.split\(','\)/) + assert.match(coordinatorPipeline, /git rev-parse HEAD\^/) assert.match(coordinatorPipeline, /selected\.addAll\(retryServices\)/) assert.match(coordinatorPipeline, /AUTO \$\{env\.IMAGE_TAG\} \| PENDING/) assert.match(coordinatorPipeline, /AUTO \$\{env\.IMAGE_TAG\} \| RESULTS/) + assert.doesNotMatch(coordinatorPipeline, /GIT_PREVIOUS_SUCCESSFUL_COMMIT/) assert.doesNotMatch(coordinatorPipeline, /commonChanged \? serviceModules/) })