jenkins 管道中的 Maven 生命周期 - 如何最好地分离职责?
Maven lifecycle within jenkins pipeline - how to best separate responsibilities?
在使用 jenkins 2(声明式)管道和 maven 时,我总是对如何在管道内组织事物以使其可重用和灵活有问题。
一方面,我想将管道分成逻辑阶段,例如:
pipeline
{
stages
{
stage('Clean') {}
stage('Build') {}
stage('Test') {}
stage('Sanity check') {}
stage('Documentation') {}
stage('Deploy - Test') {}
stage('Selenium tests') {}
stage('Deploy - Production') {}
stage('Deliver') {}
}
}
另一方面,我有运行
的maven
mvn clean deploy site
简单地说,我可以将 maven 拆分为
mvn clean
mvn deploy
mvn site
但是 'deploy' 包括来自
的所有生命周期阶段
- 验证
- 编译
- 测试
- 套餐
- 验证
- 安装
- 部署
所以我看到了很多管道示例,它们做的事情像
sh 'mvn clean compile'
和
sh 'mvn test'
这导致第二次重复验证和编译步骤,并以这种方式浪费 "time/resources"。
这可以通过执行 a
来解决
sh 'mvn surefire:test'
而不是再次 运行 整个生命周期。
所以我的问题是 - 哪个是在 jenkins 管道阶段和 maven 生命周期之间取得良好平衡的最佳方式?
对我来说,我看到两种方式:
- 将 maven 生命周期拆分为尽可能多的管道阶段 - 这将导致更好的 jenkins 用户反馈(查看哪个阶段失败等)
- 让 maven 做所有事情,使用 jenkins 管道只处理 maven 的结果(即分析单元测试结果等)
还是我在CI/CD练习中误解了什么?
我认为没有正确答案,但以下示例对我们有用。
stage('Build and Unit Test') {
mvn clean deploy -> with unit tests, without integration tests, deploy local
deploy local:
You can define in a maven profile the distributionManagement like:
<distributionManagement>
<repository>
<id>localFile</id>
<url>file:target/repository/</url>
</repository>
<snapshotRepository>
<id>localFile</id>
<url>file:target/repository/</url>
</snapshotRepository>
</distributionManagement>
}
stage('Pre Integration Tests') {
The binaries are now in target/repository.
From there you can use the binaries as you like.
Copy them to a server, deploy them on an application server, etc.
}
stage('Integration Tests') {
maven failsafe:integration-test failsafe:verify
Already all tests are compiled, just execute them and verify the result.
}
stage('Deploy to Binary Repository (Nexus, Artifactory, etc)') {
Now if everything is ok, finally upload the Binaries.
For that we use wagon-maven-plugin
So from target/repository the files are uploaded to the Binary Repository.
}
总结一下:
- 快速失败。如果单元测试有错误 -> 构建失败。
- 只构建一次。使用相同的二进制文件进行测试,deployment/integration 测试,
上传到存储库等
- 因此,阶段是逻辑单元,
这会为您提供足够的反馈来查找错误。
两个月后,我认为我有一个平衡良好的 Jenkins 管道脚本,它不完整,但在 windows 和 linux 上运行稳定。它避免了我见过的其他例子的陷阱。
詹金斯文件
pipeline
{
agent any
tools
{
maven 'Maven3'
jdk 'JDK8'
}
options
{
buildDiscarder(logRotator(numToKeepStr: '4'))
skipStagesAfterUnstable()
disableConcurrentBuilds()
}
triggers
{
// MINUTE HOUR DOM MONTH DOW
pollSCM('H 6-18/4 * * 1-5')
}
stages
{
stage('Clean')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode clean'
}
else
{
bat 'mvn --batch-mode clean'
}
}
}
}
stage('Build')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode compile'
}
else
{
bat 'mvn --batch-mode compile'
}
}
}
}
stage('UnitTests')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode resources:testResources compiler:testCompile surefire:test'
}
else
{
bat 'mvn --batch-mode resources:testResources compiler:testCompile surefire:test'
}
}
}
post
{
always
{
junit testResults: 'target/surefire-reports/*.xml'
}
}
}
stage('Sanity check')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode checkstyle:checkstyle pmd:pmd pmd:cpd com.github.spotbugs:spotbugs-maven-plugin:spotbugs'
}
else
{
bat 'mvn --batch-mode checkstyle:checkstyle pmd:pmd pmd:cpd com.github.spotbugs:spotbugs-maven-plugin:spotbugs'
}
}
}
}
stage('Packaging')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode jar:jar'
}
else
{
bat 'mvn --batch-mode jar:jar'
}
}
}
}
stage('install local')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode jar:jar source:jar install:install'
}
else
{
bat 'mvn --batch-mode jar:jar source:jar install:install' // maven-jar-plugin falseCreation default is false, so no doubled jar construction here, but required for maven-install-plugin internal data
}
}
}
}
stage('Documentation')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode site'
}
else
{
bat 'mvn --batch-mode site'
}
}
}
post
{
always
{
publishHTML(target: [reportName: 'Site', reportDir: 'target/site', reportFiles: 'index.html', keepAll: false])
}
}
}
stage('Deploy test')
{
steps
{
script
{
if (isUnix())
{
// todo
}
else
{
bat returnStatus: true, script: 'sc stop Tomcat8'
sleep(time:30, unit:"SECONDS")
bat returnStatus: true, script: 'C:\scripts\clean.bat'
bat returnStatus: true, script: 'robocopy "target" "C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps" Test.war'
bat 'sc start Tomcat8'
sleep(time:30, unit:"SECONDS")
}
}
}
}
stage('Integration tests')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode failsafe:integration-test failsafe:verify'
}
else
{
bat 'mvn --batch-mode failsafe:integration-test failsafe:verify'
}
}
}
}
}
}
希望外面的其他开发人员对此感兴趣。
当我随着时间的推移显着改进它时,我会在这里更新它。
对于那些还希望看到 maven pom 和 Jenkinsfile 的人,请在 github 查看我的小示例项目:TemplateEngine
在使用 jenkins 2(声明式)管道和 maven 时,我总是对如何在管道内组织事物以使其可重用和灵活有问题。
一方面,我想将管道分成逻辑阶段,例如:
pipeline
{
stages
{
stage('Clean') {}
stage('Build') {}
stage('Test') {}
stage('Sanity check') {}
stage('Documentation') {}
stage('Deploy - Test') {}
stage('Selenium tests') {}
stage('Deploy - Production') {}
stage('Deliver') {}
}
}
另一方面,我有运行
的mavenmvn clean deploy site
简单地说,我可以将 maven 拆分为
mvn clean
mvn deploy
mvn site
但是 'deploy' 包括来自
的所有生命周期阶段- 验证
- 编译
- 测试
- 套餐
- 验证
- 安装
- 部署
所以我看到了很多管道示例,它们做的事情像
sh 'mvn clean compile'
和
sh 'mvn test'
这导致第二次重复验证和编译步骤,并以这种方式浪费 "time/resources"。 这可以通过执行 a
来解决sh 'mvn surefire:test'
而不是再次 运行 整个生命周期。
所以我的问题是 - 哪个是在 jenkins 管道阶段和 maven 生命周期之间取得良好平衡的最佳方式? 对我来说,我看到两种方式:
- 将 maven 生命周期拆分为尽可能多的管道阶段 - 这将导致更好的 jenkins 用户反馈(查看哪个阶段失败等)
- 让 maven 做所有事情,使用 jenkins 管道只处理 maven 的结果(即分析单元测试结果等)
还是我在CI/CD练习中误解了什么?
我认为没有正确答案,但以下示例对我们有用。
stage('Build and Unit Test') {
mvn clean deploy -> with unit tests, without integration tests, deploy local
deploy local:
You can define in a maven profile the distributionManagement like:
<distributionManagement>
<repository>
<id>localFile</id>
<url>file:target/repository/</url>
</repository>
<snapshotRepository>
<id>localFile</id>
<url>file:target/repository/</url>
</snapshotRepository>
</distributionManagement>
}
stage('Pre Integration Tests') {
The binaries are now in target/repository.
From there you can use the binaries as you like.
Copy them to a server, deploy them on an application server, etc.
}
stage('Integration Tests') {
maven failsafe:integration-test failsafe:verify
Already all tests are compiled, just execute them and verify the result.
}
stage('Deploy to Binary Repository (Nexus, Artifactory, etc)') {
Now if everything is ok, finally upload the Binaries.
For that we use wagon-maven-plugin
So from target/repository the files are uploaded to the Binary Repository.
}
总结一下:
- 快速失败。如果单元测试有错误 -> 构建失败。
- 只构建一次。使用相同的二进制文件进行测试,deployment/integration 测试, 上传到存储库等
- 因此,阶段是逻辑单元, 这会为您提供足够的反馈来查找错误。
两个月后,我认为我有一个平衡良好的 Jenkins 管道脚本,它不完整,但在 windows 和 linux 上运行稳定。它避免了我见过的其他例子的陷阱。
詹金斯文件
pipeline
{
agent any
tools
{
maven 'Maven3'
jdk 'JDK8'
}
options
{
buildDiscarder(logRotator(numToKeepStr: '4'))
skipStagesAfterUnstable()
disableConcurrentBuilds()
}
triggers
{
// MINUTE HOUR DOM MONTH DOW
pollSCM('H 6-18/4 * * 1-5')
}
stages
{
stage('Clean')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode clean'
}
else
{
bat 'mvn --batch-mode clean'
}
}
}
}
stage('Build')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode compile'
}
else
{
bat 'mvn --batch-mode compile'
}
}
}
}
stage('UnitTests')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode resources:testResources compiler:testCompile surefire:test'
}
else
{
bat 'mvn --batch-mode resources:testResources compiler:testCompile surefire:test'
}
}
}
post
{
always
{
junit testResults: 'target/surefire-reports/*.xml'
}
}
}
stage('Sanity check')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode checkstyle:checkstyle pmd:pmd pmd:cpd com.github.spotbugs:spotbugs-maven-plugin:spotbugs'
}
else
{
bat 'mvn --batch-mode checkstyle:checkstyle pmd:pmd pmd:cpd com.github.spotbugs:spotbugs-maven-plugin:spotbugs'
}
}
}
}
stage('Packaging')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode jar:jar'
}
else
{
bat 'mvn --batch-mode jar:jar'
}
}
}
}
stage('install local')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode jar:jar source:jar install:install'
}
else
{
bat 'mvn --batch-mode jar:jar source:jar install:install' // maven-jar-plugin falseCreation default is false, so no doubled jar construction here, but required for maven-install-plugin internal data
}
}
}
}
stage('Documentation')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode site'
}
else
{
bat 'mvn --batch-mode site'
}
}
}
post
{
always
{
publishHTML(target: [reportName: 'Site', reportDir: 'target/site', reportFiles: 'index.html', keepAll: false])
}
}
}
stage('Deploy test')
{
steps
{
script
{
if (isUnix())
{
// todo
}
else
{
bat returnStatus: true, script: 'sc stop Tomcat8'
sleep(time:30, unit:"SECONDS")
bat returnStatus: true, script: 'C:\scripts\clean.bat'
bat returnStatus: true, script: 'robocopy "target" "C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps" Test.war'
bat 'sc start Tomcat8'
sleep(time:30, unit:"SECONDS")
}
}
}
}
stage('Integration tests')
{
steps
{
script
{
if (isUnix())
{
sh 'mvn --batch-mode failsafe:integration-test failsafe:verify'
}
else
{
bat 'mvn --batch-mode failsafe:integration-test failsafe:verify'
}
}
}
}
}
}
希望外面的其他开发人员对此感兴趣。
当我随着时间的推移显着改进它时,我会在这里更新它。
对于那些还希望看到 maven pom 和 Jenkinsfile 的人,请在 github 查看我的小示例项目:TemplateEngine