Jenkins Artifactory插件升级后仅发布多项目仓库单个组件问题
问题描述
升级Jenkins及Artifactory插件后,多模块Gradle项目仅一个模块发布到Artifactory
我正在升级公司的Jenkins服务器及相关插件,首个迁移的项目是包含两个存在构建依赖的Java服务的多项目仓库,使用Gradle作为构建工具。在旧版Jenkins及旧插件环境下,两个服务均可正常构建并发布到Artifactory;但升级后,两个服务均构建成功,却仅有一个服务被发布到Artifactory。将Artifactory插件降级至旧版本后,两个服务又可正常发布。
我对比了新旧环境下artifactoryPublish任务的日志,未发现明显差异,想请教该问题的可能原因,是否与Artifactory插件的更新变化有关?
版本信息
| 旧版本 | 新版本 | |
|---|---|---|
| Jenkins | 2.190.1 | 2.387.3 |
| Artifactory插件 | 3.9.0 | 3.18.1 |
| Gradle | 8.2.1 | 8.2.1 |
相关配置文件
Jenkinsfile
pipeline { agent any options { buildDiscarder(logRotator(numToKeepStr: '10')) skipStagesAfterUnstable() } tools { jdk "Temurin 17" } environment { sonarProjectName = "jenkins-migration-test-service" sonarBaseFolder = "jenkins-migration-test-service/" REVISION = getRevision() GRADLE_OPTS = '-XX:+IgnoreUnrecognizedVMOptions' } stages { stage('Artifactory Configuration') { steps { sh "echo Revision = ${getRevision()}" sh "echo Repository = ${getRepository()}" defineArtifactory() } } stage('Build') { steps { script { dir("kids-service/src/main/resources") { sh "echo '\ninfo.build.version=${REVISION}\n' >> application.properties" sh "echo 'eureka.instance.metadata-map.release-version=${REVISION}\n' >> application.properties" } } runGradleTask("clean build test") } post { always { archiveArtifacts artifacts: "**/build/libs/*.jar", fingerprint: true lastChanges since: "PREVIOUS_REVISION", format: "SIDE", matching: "LINE" junit "**/build/test-results/**/*.xml" } } } stage("Publish Artifacts") { when { branch "master" } steps { script { sshagent(["***************"]) { sh "git tag ${REVISION}" sh "git push --tags" } } runGradleTask("artifactoryPublish") } } stage("Publish Build Information") { when { branch "master" } steps { rtPublishBuildInfo( serverId: "98747153@1401865198163" ) } } stage("Deploy on dev") { when { branch "master" } steps { build job: '/deploy-backend', parameters: [ string(name: 'service_host', value: 'service-host'), string(name: 'service_name', value: 'kids-service'), string(name: 'service_version', value: "${REVISION}"), string(name: 'health_check_port', value: "48202") ], wait: true, propagate: true, quietPeriod: 0 } } } } def getRevision() { def apiVersion = "0.1" def patchNumber = "1339" def branchName = "${env.BRANCH_NAME}" def serviceName = "jenkins-migration-test-service" if (branchName == "master") { return apiVersion + "." + patchNumber } else { return apiVersion + "." + branchName + "." + patchNumber + "-SNAPSHOT" } } void runGradleTask(String tasks) { rtGradleRun( tool: 'gradle', rootDir: './', useWrapper: true, buildFile: "build.gradle.kts", tasks: tasks, deployerId: 'GRADLE_DEPLOYER', resolverId: 'GRADLE_RESOLVER', switches: '-Pversion=${REVISION} --warning-mode all' ) } void runGradleTaskWithDebug(String tasks) { rtGradleRun( tool: 'gradle', rootDir: './', useWrapper: true, buildFile: "build.gradle.kts", tasks: tasks, deployerId: 'GRADLE_DEPLOYER', resolverId: 'GRADLE_RESOLVER', switches: '-Pversion=${REVISION} --warning-mode all --debug' ) } void defineArtifactory() { rtGradleDeployer( id: 'GRADLE_DEPLOYER', serverId: '98747153@1401865198163', deployIvyDescriptors: true, deployMavenDescriptors: true, ivyPattern: '[organisation]/[module]/ivy-[revision].xml', artifactPattern: '[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]', repo: getRepository() ) rtGradleResolver( id: 'GRADLE_RESOLVER', serverId: '98747153@1401865198163' ) rtBuildInfo( captureEnv: true, includeEnvPatterns: ["*"] ) } def getRepository() { def branchName = "${env.BRANCH_NAME}" if (branchName == "master") { return "libs-release-local" } else { return "libs-snapshot-local" } }
父项目build.gradle.kts
plugins { id("maven-publish") id("com.diffplug.spotless") version "6.22.0" id("io.freefair.lombok") version "8.4" apply false } allprojects { apply(plugin = "com.diffplug.spotless") group = "de.********" repositories { mavenLocal() maven { url = uri("https://artifactory.********.***/artifactory/vrepo") } } spotless { format("misc") { target("*.md", ".gitignore") trimTrailingWhitespace() indentWithSpaces() endWithNewline() } kotlinGradle { target("*.kts") ktlint() } } } subprojects { apply(plugin = "java") apply(plugin = "maven-publish") apply(plugin = "com.diffplug.spotless") apply(plugin = "io.freefair.lombok") spotless { java { googleJavaFormat() } } publishing { publications { create<MavenPublication>("maven") { from(components["java"]) } } } }
父项目settings.gradle.kts
pluginManagement { repositories { mavenLocal() maven { url = uri("https://artifactory.********.***/artifactory/gradle-plugins-remote/") } } } rootProject.name = "kids-service-parent" include("kids-service-api", "kids-service")
kids-service build.gradle.kts(未被发布)
plugins { id("org.springframework.boot") version "3.1.5" id("jacoco") id("org.sonarqube") version "4.4.1.3373" } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 withSourcesJar() } sonar { properties { property("sonar.projectKey", "de.***********:kids-service") property("sonar.projectName", "kids-service") property("sonar.coverage.exclusions", "**/mock/**") } } springBoot { buildInfo() } jacoco { toolVersion = "0.8.8" } dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:3.1.5")) implementation(platform("org.springframework.cloud:spring-cloud-dependencies:2022.0.4")) implementation(project(":kids-service-api")) implementation("de.***********.lib:cps-rest-lib-all:0.1.9") implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-configuration-processor") implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client") implementation("net.logstash.logback:logstash-logback-encoder:7.4") implementation("org.flywaydb:flyway-core") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") implementation("org.springdoc:springdoc-openapi-starter-webmvc-api:2.2.0") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") testRuntimeOnly("com.h2database:h2") runtimeOnly("org.postgresql:postgresql") } tasks.bootJar { launchScript() } tasks.jar { enabled = false } tasks.test { useJUnitPlatform() finalizedBy(tasks.jacocoTestReport) } tasks.jacocoTestReport { dependsOn(tasks.test) reports { xml.required.set(true) } } configurations { arrayOf(apiElements, runtimeElements).forEach { it.get().outgoing.artifact(tasks.bootJar) } compileOnly { extendsFrom(configurations.annotationProcessor.get()) } }
kids-service-api build.gradle.kts(已被发布)
plugins { id("java-library") id("org.springframework.boot") version "3.1.5" } tasks.bootJar { enabled = false } tasks.jar { enabled = true } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 withSourcesJar() withJavadocJar() } dependencies { implementation(platform("org.springframework.boot:spring-boot-dependencies:3.1.5")) api("de.***********.lib:cps-rest-lib-models:0.1.9") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") implementation("org.springframework.boot:spring-boot-starter-web") compileOnly("com.google.code.findbugs:jsr305:3.0.2") }
问题分析与解决方案
核心原因:Artifactory插件对Spring Boot模块的发布逻辑变更
从版本3.9.0升级到3.18.1,Artifactory Gradle插件的发布逻辑有调整,尤其是针对Spring Boot模块的处理:
- Spring Boot模块的Publication匹配问题:
kids-service模块禁用了默认jar任务,启用了bootJar,并通过configurations将bootJar设置为apiElements和runtimeElements的输出。但Artifactory插件新版本不再自动识别这种自定义配置的Publication,只默认识别标准jar任务对应的Publication。而kids-service-api模块保留了jar任务,所以能正常发布。 - 插件对多模块项目的遍历逻辑变化:旧版本插件会遍历所有子模块的
publications,即使Publication的输出不是标准jar;新版本增加了过滤条件,只处理符合特定规则的Publication。
解决方法
方法1:显式配置Artifactory插件要发布的Publication
在kids-service的build.gradle.kts中,为Artifactory插件指定要发布的Publication,确保bootJar被包含进去:
plugins { id("com.jfrog.artifactory") version "3.18.1" } artifactory { publish { defaults { publications("maven") artifact(tasks.bootJar) artifact(tasks.sourcesJar) } } }
方法2:调整Spring Boot模块的Publication配置
修改kids-service的publishing配置,让MavenPublication直接关联bootJar:
publishing { publications { create<MavenPublication>("maven") { artifact(tasks.bootJar) artifact(tasks.sourcesJar) pom { name.set("kids-service") description.set("Kids Service Application") packaging = "jar" } } } }
方法3:调整Jenkins任务参数
在Jenkins的runGradleTask中,显式指定要发布的子模块,确保kids-service的artifactoryPublish任务被执行:
void runGradleTask(String tasks) { rtGradleRun( tool: 'gradle', rootDir: './', useWrapper: true, buildFile: "build.gradle.kts", tasks: "${tasks} :kids-service:artifactoryPublish :kids-service-api:artifactoryPublish", deployerId: 'GRADLE_DEPLOYER', resolverId: 'GRADLE_RESOLVER', switches: '-Pversion=${REVISION} --warning-mode all' ) }
验证步骤
- 应用上述任一修改后,在本地执行
./gradlew artifactoryPublish -Pversion=xxx,检查两个模块的产物是否都上传到Artifactory。 - 在Jenkins中重新运行流水线,确认发布阶段两个模块都成功上传。
内容的提问来源于stack exchange,提问作者Hamsterfels
相关产品推荐
相关产品推荐

