Jenkins CI/CD pipeline 中跳过 Build Code 阶段有什么影响?
构建阶段可以保留,也可以与分析合并
题目中的 Jenkinsfile 通常应保留 mvn clean package,也可以把它与 SonarQube 分析合并为一次 Maven 调用。
这两个命令默认不会重复编译:
mvn clean package清理旧产物,执行构建生命周期,通常包括编译、单元测试和打包。mvn sonar:sonar调用 SonarScanner for Maven,分析代码并提交分析报告。单独执行这个目标,通常不会编译、测试或打包项目。
直接删除 Build Code 阶段,只运行 mvn sonar:sonar,可能导致 Java 分析因缺少字节码而失败,也会失去对本次提交的编译、测试和打包验证。
Jenkins 的阶段名称只用于组织流程,是否编译取决于阶段里执行的命令。
两个 Maven 命令分别做什么?
mvn clean package:构建并生成产物
这个命令包含两部分:
clean:清理上一次构建的输出,通常是target目录。package:执行默认生命周期中截至package的阶段。
对于常见的 Java JAR 项目,流程可以简化为:
处理资源 → 编译主代码 → 编译测试代码 → 执行单元测试 → 打包
成功后,通常会得到:
target/classes/ 编译后的主代码
target/test-classes/ 编译后的测试代码
target/surefire-reports/ 单元测试报告
target/*.jar 打包产物
实际行为取决于 pom.xml、项目的 packaging 类型、启用的 profile 和插件配置。例如,设置跳过测试的参数后,构建成功也不能说明测试已经通过。
package 尚未执行后面的 integration-test 和 verify 阶段。如果项目把集成测试和相关检查绑定在这些阶段,可以考虑使用:
mvn clean verify
verify 会先经过 package,因此仍然包含打包。具体执行哪些集成测试,则取决于项目配置的相应插件。
mvn sonar:sonar:执行静态分析
sonar:sonar 是 Maven 插件目标。它读取项目配置、源码和分析所需的构建输出,生成分析报告并提交给 SonarQube。
Java 分析器会使用编译后的 .class 文件完成部分语义分析。包含多个 Java 源文件的项目通常需要提供这些字节码,SonarScanner for Maven 可以根据 Maven 项目配置找到相应的输出目录。
因此,静态代码分析仍可能需要先编译:分析器要使用编译结果,而扫描命令本身通常不负责生成这些结果。
| 工作 | mvn clean package | 单独执行 mvn sonar:sonar |
|---|---|---|
| 编译项目源码 | 通常会 | 通常不会 |
| 执行单元测试 | 默认会,受配置影响 | 不会主动执行 |
| 生成 JAR/WAR | 按 packaging 配置生成 | 不负责生成 |
| 提交 SonarQube 分析报告 | 默认不会 | 会 |
如果项目通过自定义插件配置增加了其他行为,应以实际 Maven 执行日志为准。
跳过 Build Code 后会发生什么?
干净的工作区可能无法完成 Java 分析
假设 Jenkins 使用新 agent,检出后只有源码,没有 target/classes:
Checkout
↓
mvn sonar:sonar
↓
缺少分析所需的 Java 字节码,分析可能失败
具体表现取决于项目内容和分析器版本。不同语言的 SonarQube 分析对编译的要求不同,题目中的 Maven Java 项目应优先按“先构建,再分析”安排。
复用工作区可能掩盖问题
如果工作区保留了上次构建的 target 目录,删除构建阶段后,扫描可能仍能启动。但此时源码来自当前提交,字节码却来自旧提交。分析可能失败,也可能使用不匹配的输入,导致结果不可靠。
即使删除阶段后 Jenkins 仍显示绿色,也要检查扫描使用的字节码、测试报告和其他输入是否属于当前提交,才能判断构建步骤是否可以省略。
编译、测试和交付验证会缺失
一个方法可能正常编译,却返回错误的业务结果。单元测试可以通过断言发现这个问题,SonarQube 不会替你执行测试。
静态分析完成,也不能据此确认:
- 当前提交能够成功编译。
- 单元测试或集成测试已经通过。
- 项目能够成功打包。
- 后续部署需要的 JAR/WAR 已经生成。
如果后面还有归档、制作镜像或部署阶段,缺少本次构建产物会直接影响这些步骤,工作区中的旧产物也可能被误用。
如何安排 Jenkins 阶段?
方案一:保留独立的构建和分析阶段
分开执行便于排查失败原因。构建阶段失败时,先处理编译或测试问题;分析阶段失败时,再检查扫描配置、认证或 SonarQube 连接。
下面沿用 Windows agent 的 bat,构建执行到 verify:
pipeline {
agent any
options {
skipDefaultCheckout(true)
}
environment {
SONAR_HOST_URL = 'https://sonarqube.example.com'
SONAR_PROJECT_KEY = 'your-project-key'
}
stages {
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'git-url']]
])
}
}
stage('Build and Test') {
steps {
bat 'mvn -B clean verify'
}
}
stage('Static Code Analysis') {
steps {
withCredentials([
string(
credentialsId: 'sonarqube',
variable: 'SONAR_TOKEN'
)
]) {
bat '@mvn -B sonar:sonar "-Dsonar.token=%SONAR_TOKEN%" "-Dsonar.host.url=%SONAR_HOST_URL%" "-Dsonar.projectKey=%SONAR_PROJECT_KEY%"'
}
}
}
}
}
使用前需要替换仓库地址、SonarQube 地址和项目 key,并确保 agent 上的 JDK、Maven 及扫描插件配置兼容。
Groovy 单引号字符串让 Windows 命令行展开 %SONAR_TOKEN%,避免通过 Groovy 字符串插值传入凭据。
两个阶段需要使用同一份源码和构建输出。示例没有配置阶段级 agent,正常执行时会共享顶层 agent 的工作区。如果改用不同节点或容器,需要显式传递扫描所需的构建输出,target 不会自动出现在新环境中。
方案二:把构建与分析合并
如果不需要分别展示耗时和失败位置,可以把上述两个阶段替换为:
stage('Build, Test and Analyze') {
steps {
withCredentials([
string(
credentialsId: 'sonarqube',
variable: 'SONAR_TOKEN'
)
]) {
bat '@mvn -B clean verify sonar:sonar "-Dsonar.token=%SONAR_TOKEN%" "-Dsonar.host.url=%SONAR_HOST_URL%" "-Dsonar.projectKey=%SONAR_PROJECT_KEY%"'
}
}
}
Maven 会先完成截至 verify 的构建,再执行 sonar:sonar。默认情况下,构建失败后,扫描不会继续执行。
合并后,独立的 Build Code 阶段可以删除,编译、测试和打包工作仍然保留。sonar:sonar 本身不会再执行一遍完整构建。
如果项目目前只需要执行到打包,也可以使用:
mvn clean package sonar:sonar
哪些情况下可以跳过构建?还要注意什么?
扫描所需的构建结果已经可靠准备好时,才适合跳过当前流水线中的构建步骤。例如,上游任务已经为同一提交生成并传递了所需字节码和报告。除了确认文件存在,还要检查构建配置、依赖及工具链是否与当前分析匹配,并确保当前提交已有测试验证。
安排阶段时,还需注意:
- 扫描前要保留构建输出。不要先构建,再执行
mvn clean sonar:sonar,因为clean会删除刚生成的字节码和报告。 - 覆盖率报告需要单独生成。SonarQube 不会通过静态扫描运行测试并计算覆盖率,需要配置 JaCoCo 等工具生成报告,再由扫描器导入。
- 多模块项目拆成两次 Maven 调用时,扫描可能需要从本地仓库解析模块间依赖。这类项目通常应先执行
mvn clean install,再单独扫描。install会额外把构建产物安装到 agent 的本地 Maven 仓库。 - 扫描成功与 Quality Gate 通过需要分别处理。如果发布受质量门禁约束,应在分析后等待 Quality Gate 结果。使用 Jenkins 的
waitForQualityGate时,需要按插件要求配置withSonarQubeEnv和 SonarQube webhook。 - 在项目配置中明确插件版本,并让构建与分析使用一致的 profile、源码和 JDK 配置,避免不同阶段实际处理不同的项目内容。
备注:内容仅供参考。
