You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins Artifactory插件升级后仅发布多项目仓库单个组件问题

问题描述

升级Jenkins及Artifactory插件后,多模块Gradle项目仅一个模块发布到Artifactory

我正在升级公司的Jenkins服务器及相关插件,首个迁移的项目是包含两个存在构建依赖的Java服务的多项目仓库,使用Gradle作为构建工具。在旧版Jenkins及旧插件环境下,两个服务均可正常构建并发布到Artifactory;但升级后,两个服务均构建成功,却仅有一个服务被发布到Artifactory。将Artifactory插件降级至旧版本后,两个服务又可正常发布。

我对比了新旧环境下artifactoryPublish任务的日志,未发现明显差异,想请教该问题的可能原因,是否与Artifactory插件的更新变化有关?

版本信息

旧版本新版本
Jenkins2.190.12.387.3
Artifactory插件3.9.03.18.1
Gradle8.2.18.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'
    )
}

验证步骤

  1. 应用上述任一修改后,在本地执行./gradlew artifactoryPublish -Pversion=xxx,检查两个模块的产物是否都上传到Artifactory。
  2. 在Jenkins中重新运行流水线,确认发布阶段两个模块都成功上传。

内容的提问来源于stack exchange,提问作者Hamsterfels

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:49:52