Nexus-CI-[流水线插件集成|手动上传|自动上传]
在开始引入制品的时候,就应该制定制品库的管理和使用规范。 有了标准化的规范之后, 就很容易实现自动化。(为什么有些工作无法做成自动化? -无标准)
约定制品规范
1.版本号
- 主版本号:表示项目的重大架构变更。
- 次版本号:表示较大范围的功能增加和变化。
- 修订版本号:表示重大Bug的修复。
- 里程碑版本:表示某一个版本的里程碑。
2.仓库命名
类型 | 格式 | 示例 |
仓库组 | <技术>-group | maven-group |
仓库 | <业务简称>-<技术>-<类型> | devops-maven-RELEASE |
制品 | <应用名称>-<版本号> | demo-devops-service-1.1.0.jar |
3.目录结构: 按照 业务/服务/版本 层级。
使用maven指令上传制品
上传制品之前, 肯定得确定目标仓库是存在的。 如果不存在我们可以新建一个 hosted类型的maven仓库。
仓库已经有了, 需要更新maven的配置文件,在settings.xml中添加仓库的认证信息。如下:
<mirror> <id>hwf-maven</id> <mirrorOf>*</mirrorOf> <name>maven repo</name> <url>http://192.168.1.110:8081/nexus/repository/maven-releases/</url> </mirror>
注意使用mvn deploy 发布时,-DrepositoryId
参数的值要与上面配置文件中的<server>
标签中的<id>
一致。不然会出现401,用户认证失败的问题。
mvn deploy:deploy-file -DgroupId=xxxxxx pom中的groupId -DartifactId=xxxxxx pom中的artifactId -Dversion=xxxxxx pom中的版本号version -Dpackaging=xxxxxx pom中打包方式 -Dfile=xxxxxx 本地文件 -Durl=xxxxxx 仓库url -DrepositoryId=xxxxxx 对应的是setting.xml(认证)
Maven上传报错, 401 可以确定是认证的错误。 需要检查认证信息。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy-file (default-cli) on project standalone-pom: Failed to deploy artifacts: Could not transfer artifact wlmqpacx.app:wlmqpacx-app:jar:1.0 from/to remote-repository (http://192.168.1.110:8081/nexus/repository/hwf-maven-release/): authentication failed for http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/wlmqpacx-app/1.0/wlmqpacx-app-1.0.jar, status: 401 Unauthorized -> [Help 1]
在maven的settings.xml文件中添加server字段添加认证信息//id为仓库名称
<server> <id>wlmqpacx<id> <username>admin</username> <password>admin123</password> </server>
m命令执行添加认证名称
mvn deploy:deploy-file \ -DgroupId=wlmqpacx.app \ -DartifactId=app-test \ -Dversion=1.0 \ -Dpackaging=jar \ -Dfile=demo/target/demo-0.0.1-SNAPSHOT.jar \ -Durl=http://192.168.1.110:8081/nexus/repository/hwf-maven-release \ -DrepositoryId=wlmqpacx
[INFO] Scanning for projects... [INFO] [INFO] ------------------< org.apache.maven:standalone-pom >------------------- [INFO] Building Maven Stub Project (No POM) 1 [INFO] --------------------------------[ pom ]--------------------------------- [INFO] [INFO] --- maven-deploy-plugin:2.7:deploy-file (default-cli) @ standalone-pom --- Uploading to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/1.0/app-test-1.0.jar Uploaded to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/1.0/app-test-1.0.jar (22 B at 124 B/s) Uploading to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/1.0/app-test-1.0.pom Uploaded to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/1.0/app-test-1.0.pom (392 B at 10 kB/s) Downloading from wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/maven-metadata.xml Uploading to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/maven-metadata.xml Uploaded to wlmqpacx: http://192.168.1.110:8081/nexus/repository/hwf-maven-release/wlmqpacx/app/app-test/maven-metadata.xml (296 B at 8.0 kB/s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.908 s [INFO] Finished at: 2021-10-20T17:18:51+08:00 [INFO] ------------------------------------------------------------------------
直接读取pom文件(方便)
mvn deploy:deploy-file \ -DgeneratePom=false \ -DrepositoryId=wlmqpacx \ -Durl=http://192.168.1.110:8081/nexus/repository/hwf-maven-release \ -DpomFile=pom.xml \ -Dfile=target/demo-0.0.1-SNAPSHOT.jar
自定义pom信息(灵活)
mvn deploy:deploy-file -Dmaven.test.skip=true \ -Dfile=target/demo-0.0.1-SNAPSHOT.jar \ -DgroupId=wlmqpacx.app \ -DartifactId=app-test \ -Dversion=1.1 \ -Dpackaging=jar \ -DrepositoryId=wlmqpacx \ -Durl=http://192.168.1.110:8081/nexus/repository/hwf-maven-release
FAQ:
release类型的仓库只能上传release版本的包。如果你尝试用snapshot包上传到release类型的仓库时会遇到这些错误的。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy-file (default-cli) on project demo: Failed to deploy artifacts: Could not transfer artifact com.example:demo:jar:0.0.1 from/to maven-hosted (http://192.168.1.200:8081/repository/maven-zeyang-test/): transfer failed for http://192.168.1.200:8081/repository/maven-zeyang-test/com/example/demo/0.0.1/demo-0.0.1.jar, status: 400 Repository version policy: SNAPSHOT does not allow version: 0.0.1 -> [Help 1]
解决方法: 1. 更新pom中的版本号 2. 对号入座,上传到对应类型的仓库。
<groupId>com.example</groupId> <artifactId>myapp</artifactId> <version>0.0.2-SNAPSHOT</version> //改成0.0.2-RELEASE
将包上传到snapshot类型的仓库:
mvn deploy:deploy-file -Dmaven.test.skip=true \ -Dfile=target/demo-0.0.1-SNAPSHOT.jar \ -DgroupId=wlmqpacx.app \ -DartifactId=app-test \ -Dversion=1.1 \ -Dpackaging=jar \ -DrepositoryId=wlmqpacx \ -Durl=http://192.168.1.110:8081/nexus/repository/hwf-maven-release
使用Jenkins插件上传制品
安装Nexus Artifact Uploader插件、使用片段生成器生成DSL。[了解就行]
如下面代码:
nexusArtifactUploader artifacts: [[artifactId: 'wlmqpacx', classifier: '', file: 'target/demo-0.0.1-SNAPSHOT.jar', type: 'jar']], credentialsId: '8a3c99b1-a165-4ac6-a972-2355e6cc14a7', groupId: 'wlmqpacx-app', nexusUrl: 'http://192.168.1.110:8081/nexus', nexusVersion: 'nexus3', protocol: 'http', repository: 'app-test', version: '1.0'
进行优化, 将参数以变量的方式传递给函数。【方法参考sonarqube传参】
//NexusUploadByPlugin('devops-test', 'target/demo-0.0.1-SNAPSHOT.jar', 'jar', 'com.jenkins','1.1.2') def NexusUploadByPlugin(artifactId, file, type, groupId,version){ nexusArtifactUploader artifacts: [[artifactId: artifactId, classifier: '', file: file, type: type]], credentialsId: '1de05a13-197c-4a72-8c6a-cd330fc45559', groupId: groupId, nexusUrl: '192.168.1.200:8081', nexusVersion: 'nexus3', protocol: 'http', repository: 'mymavenrepo', version: version }
扩展: 可以在Jenkins页面添加参数, 让用户输入后进行发布。
@Library("mylib@main") _ import org.devops.* def checkout = new Checkout() def build = new Build() def unittest = new UnitTest() def sonar = new Sonar() pipeline { agent { label "build" } options { skipDefaultCheckout true } stages{ stage("Checkout"){ steps{ script { println("GetCode") checkout.GetCode("${env.srcUrl}", "${env.branchName}") } } } stage("Build"){ steps{ script{ println("Build") sh "mvn clean package " } } } stage("UnitTest"){ steps{ script{ unittest.CodeTest("${env.buildTool}") } } } stage("Upload"){ steps{ script{ NexusUploadByPlugin("${env.artifactId}", 'target/demo-0.0.1-SNAPSHOT.jar', "${env.type}", "${env.groupId}", "${env.version}") } } } } } //NexusUploadByPlugin('devops-test', 'target/demo-0.0.1-SNAPSHOT.jar', 'jar', 'com.jenkins','1.1.2') def NexusUploadByPlugin(artifactId, file, type, groupId,version){ nexusArtifactUploader artifacts: [[artifactId: artifactId, classifier: '', file: file, type: type]], credentialsId: '1de05a13-197c-4a72-8c6a-cd330fc45559', groupId: groupId, nexusUrl: '192.168.1.110:8081', nexusVersion: 'nexus3', protocol: 'http', repository: 'mymavenrepo', version: version }
如果是maven类型的具有源码的项目, 可以直接使用mvn命令上传,更加方便。
//上传制品使用maven命令 def PushArtifactsByMvn(repoName,filePath ){ sh """ mvn deploy:deploy-file \ -DgeneratePom=false \ -DrepositoryId="maven-hosted" \ -Durl=http://192.168.1.110:8081/repository/"${repoName}" \ -DpomFile=pom.xml \ -Dfile="${filePath}" """ } PushArtifactsByMvn("${params.repoName}","target/${pkg}")
我的流水线模板
Build.groovy
package hwf.devops //Maven def MavenBuild(){ def buildTools = ["maven": "/data/apache-maven-3.8.3"] sh "${buildTools["maven"]}/bin/mvn clean package -DskipTests -f demo/pom.xml \ -s settings.xml" } //Gradle def GradleBuild(){ def buildTools = ["gradle": "/data/gradle-7.2"] sh "${buildTools["gradle"]}/bin/gradle clean && ${buildTools["gradle"]}/bin/gradle build -x test" } //Ant def AntBuild(configPath="./build.xml"){ sh "ant -f ${configPath}" } //Golang def GoBuild(){ def buildTools = ["go": "/data/go" ] sh "${buildTools["go"]}/bin/go build demo.go" } //Npm def NpmBuild(){ def buildTools = ["npm": "/data/node-v14.18.0-linux-x64" ] sh """ export PATH=\$PATH:${buildTools["npm"]}/bin ${buildTools["npm"]}/bin/npm install ${buildTools["npm"]}/bin/npm run build """ } //Yarn def YarnBuild(){ sh "yarn install && yarn build " } //Main def CodeBuild(type){ switch(type){ case "maven": MavenBuild() break; case "gradle": GradleBuild() break; case "npm": NpmBuild() break; case "yarn": YarnBuild() break; default: error "No such tools ... [maven/ant/gradle/npm/yarn/go]" break } }
Checkout.groovy
package hwf.devops //下载代码 def GetCode(srcUrl, branchName){ checkout([$class: 'GitSCM', branches: [[name: branchName]], extensions: [], userRemoteConfigs: [[ credentialsId: 'gitlab-root', url: srcUrl]]]) }
Nexuspackage.groovy
package hwf.devops //nexusupload def Upload(artifaceId,file,type,groupId,repository,version){ nexusArtifactUploader artifacts: [[artifactId: artifaceId, classifier: '', file: file, type: type]], credentialsId: '8a3c99b1-a165-4ac6-a972-2355e6cc14a7', groupId: groupId, nexusUrl: '192.168.1.110:8081/nexus', nexusVersion: 'nexus3', protocol: 'http', repository: repository, version: version }
Sonar.groovy
package hwf.devops def SonarRequest(apiUrl,method){ withCredentials([string(credentialsId: "16ed65f2-1b1e-4fbb-b976-a5fd60698d60", variable: 'SONAR_TOKEN')]) { sonarApi = "http://192.168.1.110:9000/api" response = sh returnStdout: true, script: """ curl --location \ --request ${method} \ "${sonarApi}/${apiUrl}" \ --header "Authorization: Basic ${SONAR_TOKEN}" """ // json格式化 try { response = readJSON text: """ ${response - "\n"} """ } catch(e){ response = readJSON text: """{"errors" : true}""" } return response } } //查找项目 def SearchProject(projectName){ apiUrl = "projects/search?projects=${projectName}" response = SonarRequest(apiUrl,"GET") if (response.paging.total == 0){ return false } return true } //创建项目 def CreateProject(projectName){ apiUrl = "projects/create?name=${projectName}&project=${projectName}" response = SonarRequest(apiUrl,"POST") try{ if (response.project.key == projectName ) { println("Project Create success!...") return true } }catch(e){ println(response.errors) return false } } // 更新质量阈 def UpdateQualityProfiles(lang, projectName, profileName){ apiUrl = "qualityprofiles/add_project?language=${lang}&project=${projectName}&qualityProfile=${profileName}" response = SonarRequest(apiUrl,"POST") if (response.errors != true){ println("ERROR: UpdateQualityProfiles ${response.errors}...") return false } else { println("SUCCESS: UpdateQualityProfiles ${lang} > ${projectName} > ${profileName}" ) return true } } def SonarJava(projectName, groupName ){ withCredentials([string(credentialsId: '9a2fd415-eebb-403b-9116-db07f9ae2605', variable: 'AUTH_TOKEN')]) { sh """ sonar-scanner -Dsonar.host.url=http://192.168.1.110:9000 \ -Dsonar.projectKey=${projectName} \ -Dsonar.projectName=${projectName} \ -Dsonar.projectVersion=${BUILD_ID} \ -Dsonar.login=${AUTH_TOKEN} \ -Dsonar.ws.timeout=30 \ -Dsonar.projectDescription="my first project!" \ -Dsonar.links.homepage=http://192.168.1.110:9080/${groupName}/${projectName} \ -Dsonar.links.ci=http://192.168.1.110:8080/job/${groupName}/job/${projectName}/ \ -Dsonar.sources=./demo/src \ -Dsonar.sourceEncoding=UTF-8 \ -Dsonar.java.binaries=./demo/target/classes \ -Dsonar.java.test.binaries=./demo/target/test-classes """ } } def SonarWeb(projectName, groupName ){ withCredentials([string(credentialsId: '9a2fd415-eebb-403b-9116-db07f9ae2605', variable: 'AUTH_TOKEN')]) { sh """ sonar-scanner -Dsonar.host.url=http://192.168.1.110:9000 \ -Dsonar.projectKey=${projectName} \ -Dsonar.projectName=${projectName} \ -Dsonar.projectVersion=${BUILD_ID} \ -Dsonar.login=${AUTH_TOKEN} \ -Dsonar.ws.timeout=30 \ -Dsonar.projectDescription="my first project!" \ -Dsonar.links.homepage=http://192.168.1.200/${groupName}/${projectName} \ -Dsonar.links.ci=http://192.168.1.200:8080/job/${groupName}/job/${projectName}/ \ -Dsonar.sources=src \ -Dsonar.sourceEncoding=UTF-8 """ } } //Main def CodeSonar(type, projectName, groupName){ if (SearchProject(projectName)){ println("${projectName} exists....") } else { // 项目不存在 println("${projectName} not found....") //创建项目 CreateProject(projectName) } //质量配置 profileMap = ["maven": "java", "npm": "js"] UpdateQualityProfiles(profileMap[type], projectName, groupName) switch(type){ case "maven": SonarJava(projectName, groupName) break; case "gradle": SonarJava(projectName, groupName) break; case "npm": SonarWeb(projectName, groupName) break; default: println("No such tools ... [maven/ant/gradle/npm/yarn/go]") break } }
UnitTest.groovy
package hwf.devops //Maven def MavenTest(){ def buildTools = ["maven": "/data/apache-maven-3.8.3"] sh """ ${buildTools["maven"]}/bin/mvn test -f demo/pom.xml -s settings.xml """ } //Gradle def GradleTest(){ def buildTools = ["gradle": "/data/gradle-7.2"] sh """ ${buildTools["gradle"]}/bin/gradle test junit 'build/test-results/test/*.xml' """ } //Ant //def AntBuild(configPath="./build.xml"){ // sh "ant -f ${configPath}" //} //Golang //def GoTest(){ //sh " go test" //} //Npm def NpmTest(){ def buildTools = ["npm": "/data/node-v14.18.0-linux-x64" ] sh """ export PATH=\$PATH:${buildTools["npm"]}/bin ${buildTools["npm"]}/bin/npm test" """ } //Yarn //def YarnTest(){ // sh "yarn test " //} //Main def CodeTest(type){ switch(type){ case "maven": MavenTest() break; case "gradle": GradleTest() break; default: println("No such tools ... [maven/ant/gradle/npm/yarn/go]") break } }
jenkinsfile
@Library("hwf@main") _ import hwf.devops.* // 导入库 def checkout = new Checkout() def build = new Build() def unittest = new UnitTest() def sonar = new Sonar() def nexuspackage = new Nexuspackage() pipeline { agent { label "build" } stages{ stage("Checkout"){ steps{ script { println("GetCode") checkout.GetCode("${env.srcUrl}", "${env.branchName}") } } } stage("Build"){ steps{ script{ println("Build") build.CodeBuild("${env.buildTool}") } } } stage("Nexuspackage"){ steps{ script{ nexuspackage.Upload("${env.artifaceId}", "${env.file}", "${env.type}", "${env.groupId}", "${prository}", "${version}") //nexuspackage.Upload('app-test','./demo/target/demo-0.0.1-SNAPSHOT.jar','jar','wlmqpacx.app','wlmqpacx','1.11') } } } stage("UnitTest"){ steps{ script{ unittest.CodeTest("${env.buildTool}") } } } stage("SonarScan"){ steps { script{ groupName = "${JOB_NAME}".split("/")[0] projectName = "${JOB_NAME}".split("/")[-1] //devops03/devops03-maven-service sonar.CodeSonar("${env.buildTool}", projectName, groupName) } } } } }

评论