From b012d64b54ceb1e47e3bb6d9096969aadd121584 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 24 Aug 2026 13:43:38 +0800 Subject: [PATCH] perf(ci): run selected tests once before service builds --- Jenkinsfile | 33 ++++++++++++++++++- Jenkinsfile.service | 9 +++-- scripts/controller-pipeline-contract.test.mjs | 11 +++++++ ...est-environment-pipeline-contract.test.mjs | 11 +++++-- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d1a5cc91b..e80ce973e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -108,6 +108,9 @@ pipeline { } env.RESOLVED_JOB_PREFIX = jobPrefix env.DATABASE_CHANGE_DETECTED = 'false' + if (env.RESOLVED_ENVIRONMENT == 'production' && params.SKIP_TESTS) { + error('生产环境禁止跳过测试') + } List selected = [] if (params.BUILD_MODE == 'ALL') { @@ -177,6 +180,33 @@ pipeline { } } + stage('统一 Maven 测试门禁') { + when { + expression { env.NO_SERVICE_CHANGE != 'true' && !params.SKIP_TESTS } + } + agent { label 'java-builder' } + tools { maven 'maven-3.8.8' } + steps { + script { + String selectedModules = env.SELECTED_SERVICES.split(',') + .collect { it.trim() } + .findAll { it } + .join(',') + echo "统一测试本次选中的服务及其公共依赖: ${selectedModules}" + sh """ + test \"\$(git rev-parse HEAD)\" = \"${env.SOURCE_COMMIT}\" + mvn --settings deploy/maven/settings-ci.xml --batch-mode --errors -pl ${selectedModules} -am test + """ + echo "统一测试门禁通过: ${env.SOURCE_COMMIT};下游单服务任务不再重复执行测试" + } + } + post { + always { + junit allowEmptyResults: true, testResults: '**/target/surefire-reports/TEST-*.xml' + } + } + } + stage('并行调度独立服务 Job') { when { expression { env.NO_SERVICE_CHANGE != 'true' } } steps { @@ -196,7 +226,8 @@ pipeline { string(name: 'IMAGE_TAG', value: env.IMAGE_TAG), string(name: 'ENVIRONMENT', value: env.RESOLVED_ENVIRONMENT), booleanParam(name: 'DEPLOY', value: params.DEPLOY), - booleanParam(name: 'SKIP_TESTS', value: params.SKIP_TESTS), + // 总控已完成统一测试,或用户已明确选择跳过;下游仅负责编译、打包和部署。 + booleanParam(name: 'SKIP_TESTS', value: true), string(name: 'IMAGE_REPOSITORY', value: env.RESOLVED_IMAGE_REPOSITORY), string(name: 'DOCKER_BASE_IMAGE', value: params.DOCKER_BASE_IMAGE), string(name: 'EVENT_BRIDGE_REGION', value: params.EVENT_BRIDGE_REGION) diff --git a/Jenkinsfile.service b/Jenkinsfile.service index 379caf60c..ed42f05bf 100644 --- a/Jenkinsfile.service +++ b/Jenkinsfile.service @@ -114,12 +114,15 @@ pipeline { stage('Maven 测试与打包') { steps { script { - if (params.ENVIRONMENT == 'production' && params.SKIP_TESTS) { - error('生产环境禁止跳过测试') + boolean coordinatorPinnedCommit = params.SOURCE_COMMIT?.trim() as boolean + if (params.ENVIRONMENT == 'production' && params.SKIP_TESTS && !coordinatorPinnedCommit) { + error('生产环境手工构建禁止跳过测试;总控流水线可在统一测试通过后执行快速打包') } boolean skipTestExecution = params.ENVIRONMENT == 'test' || params.SKIP_TESTS String testOption = skipTestExecution ? '-DskipTests' : '' - if (params.ENVIRONMENT == 'test') { + if (params.SKIP_TESTS && coordinatorPinnedCommit) { + echo "总控已处理提交 ${env.RESOLVED_SOURCE_COMMIT} 的测试门禁,本单服务仅编译测试源码并打包" + } else if (params.ENVIRONMENT == 'test') { echo '测试环境快速部署:编译主代码和测试代码,但不重复执行单元测试;完整测试由提交门禁和预发布执行' } sh "mvn --settings ${env.MAVEN_SETTINGS} --batch-mode --errors -pl ${env.RESOLVED_SERVICE} -am clean package ${testOption}" diff --git a/scripts/controller-pipeline-contract.test.mjs b/scripts/controller-pipeline-contract.test.mjs index bd941c9cd..802e7e569 100644 --- a/scripts/controller-pipeline-contract.test.mjs +++ b/scripts/controller-pipeline-contract.test.mjs @@ -13,6 +13,17 @@ test('coordinator releases its builder while waiting for downstream jobs', () => assert.doesNotMatch(pipeline, /cleanWs\(/) }) +test('coordinator runs selected tests once before parallel service packaging', () => { + const gateIndex = pipeline.indexOf("stage('统一 Maven 测试门禁')") + const dispatchIndex = pipeline.indexOf("stage('并行调度独立服务 Job')") + + assert.ok(gateIndex >= 0, 'unified test gate must exist') + assert.ok(dispatchIndex > gateIndex, 'unified tests must finish before downstream dispatch') + assert.match(pipeline, /-pl \$\{selectedModules\} -am test/) + assert.match(pipeline, /booleanParam\(name: 'SKIP_TESTS', value: true\)/) + assert.doesNotMatch(pipeline, /TESTED_COMMIT/) +}) + test('AUTO detects database migrations without expanding them to all services', () => { assert.match(pipeline, /def databaseMigrationPaths = \['deploy\/sql\/'\]/) assert.match(pipeline, /!databaseMigration && commonPaths\.any/) diff --git a/scripts/test-environment-pipeline-contract.test.mjs b/scripts/test-environment-pipeline-contract.test.mjs index 85f55937c..c6f238ce2 100644 --- a/scripts/test-environment-pipeline-contract.test.mjs +++ b/scripts/test-environment-pipeline-contract.test.mjs @@ -30,8 +30,9 @@ test('coordinator jobs present their own deployment environment as the default c }) test('production deployments cannot skip tests', () => { - assert.match(pipeline, /params\.ENVIRONMENT == 'production' && params\.SKIP_TESTS/) - assert.match(pipeline, /error\('生产环境禁止跳过测试'\)/) + assert.match(coordinatorPipeline, /env\.RESOLVED_ENVIRONMENT == 'production' && params\.SKIP_TESTS/) + assert.match(coordinatorPipeline, /error\('生产环境禁止跳过测试'\)/) + assert.match(pipeline, /生产环境手工构建禁止跳过测试/) }) test('Java builds use the shared builder pool and serialize incremental target deployments', () => { @@ -51,6 +52,12 @@ test('Java builds use the shared builder pool and serialize incremental target d assert.match(directDeployScript, /--pull never/) }) +test('production only accepts skipped downstream tests for a coordinator-pinned commit', () => { + assert.match(pipeline, /boolean coordinatorPinnedCommit = params\.SOURCE_COMMIT\?\.trim\(\) as boolean/) + assert.match(pipeline, /params\.ENVIRONMENT == 'production' && params\.SKIP_TESTS && !coordinatorPinnedCommit/) + assert.match(pipeline, /生产环境手工构建禁止跳过测试/) +}) + test('development and test deployment locks are acquired before target executors', () => { const directDeployStage = pipeline.slice( pipeline.indexOf("stage('部署开发或测试环境')"),