多模块 Gradle Java 项目发布到新 Maven中央仓库最新指南

背景说明

最近有个项目希望发布到 Maven 中央仓库,发现从 2024 年 3 月 12 日起,所有注册都将通过 Maven Central Portal 进行, 不再走过去的提交 issue 的那套流程。

而网上大部分都是介绍使用旧版本的 OSSRH 的发布方法,新版本的发包的介绍很少,并且由于 Gradle 插件目前没有官方的支持,三方插件很多,其中坑不少,有些插件按照文档配置并不能正常的发布。

加上,我们的这个项目是包含多个模块及支持 BOM 的 Gradle 项目,使得发布的配置很难有比较好的参考,折腾的好几天,总算找个比较合理的方案解决。

今天就把这个最新的方案分享一下,希望大家少踩坑,省时省力的发布到新的 Maven Central。

另外,旧版的注册与发布,可以参考下面的两个文档

  • 旧版注册的信息,可以看这个文档
  • 旧版本的OSSRH 发布方法,看这个文档

准备工作

注册Central Portal账户

  • 通过 Maven Central Portal 注册账号,建议使用 github 直接登录。
  • 创建命名空间如(io.github.username),需验证域名或GitHub仓库所有权。 对于 io.github 可以通过 github 直接注册登录Maven Central Portal即可自动完成验证。 对于自定义的域名,首先这个域名没有在 OSSRH 上注册过,如果注册过,会提示无法注册,并给了一个 support 邮件,可以通过邮件联系。
  • 通过 Portal 生成用户令牌(SONATYPE_USERNAMESONATYPE_USERNAME),后面的配置会用到

GPG密钥准备

安装 GnuPG

生成 key

gpg --gen-key

生成过程中需要输入一次密码,这个密码就是 GPG_PASSPHRASE(也有文档叫 GPG_SIGNING_PASSWORD) 也需要一个邮箱,后面导出秘钥需要用到。

列出 KEY 信息

plain 复制代码
$ gpg --list-keys /home/mylocaluser/.gnupg/pubring.kbx 
--------------------------------
- pub rsa3072 2021-06-23 [SC] [expires: 2023-06-23] 
      CA925CD6C9E8D064FF05B4728 uid [ultimate] Central Repo Test 
<central@example.com> sub rsa3072 2021-06-23 [E] [expires: 2023-06-23]

输出显示公钥环文件的路径。以 pub 开头的行显示大小 (rsa3072)、keyid (CA925CD6C9E8D064FF05B4728190C4130ABA0F98) 和创建日期 (2023-06-23) 的公钥. 分发公钥时,我们需要使用 keyid

分发公钥

bash 复制代码
gpg --keyserver keys.openpgp.org --send-keys CA925CD6C9E8D064FF05B4728
gpg --keyserver pgp.mit.edu --send-keys CA925CD6C9E8D064FF05B4728

导出秘钥

我们使用 Gradle 生成签名,需要 GPG_PASSPHRASE 和 GPG_SIGNING_KEY。 首先我们需要导出 GPG_SIGNING_KEY

bash 复制代码
gpg --armor --export-secret-key your_email
-----BEGIN PGP PRIVATE KEY BLOCK-----

xxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx

-----END PGP PRIVATE KEY BLOCK-----

导出 GPG_SIGNING_KEY 秘钥后的整个文本块都需要配置到环境变量里。

配置秘钥到环境变量里

修改 ~/.zshrc 或 ~/.bashrc

bash 复制代码
export GPG_PASSPHRASE='YOUR_PASSWORD'
export GPG_SIGNING_KEY='
-----BEGIN PGP PRIVATE KEY BLOCK-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PGP PRIVATE KEY BLOCK-----
'

Gradle 项目中配置发布 - 单模块

1. 在 build.gradle 或 build.gradle.kts 中添加必要的插件

对于 Groovy DSL(build.gradle):

groovy 复制代码
plugins {
    id 'java-library'
    id 'maven-publish'
    id 'signing'
}

2. 配置发布信息

关键点: mavenJava 这个是自定义的一个值

这个其实就是 publishing 插件产物的目录,签名需要这个目录。

groovy 复制代码
group = 'io.github.yourusername'
version = '1.0.0'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
            pom {
                name = 'Your Project Name'
                description = 'A description of your project'
                url = 'https://github.com/yourusername/project'
                
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                
                developers {
                    developer {
                        id = 'yourusername'
                        name = 'Your Name'
                        email = 'your.email@example.com'
                    }
                }
                
                scm {
                    connection = 'scm:git:git://github.com/yourusername/project.git'
                    developerConnection = 'scm:git:ssh://github.com/yourusername/project.git'
                    url = 'https://github.com/yourusername/project'
                }
            }
        }
    }
}

3. 配置签名

只要是配置了 publishing 的地方就需要配置签名。

groovy 复制代码
signing {
    def signingKey = findProperty('GPG_SIGNING_KEY') ?: System.getenv('GPG_SIGNING_KEY')
    def signingPassword = findProperty('GPG_PASSPHRASE') ?: System.getenv('GPG_PASSPHRASE')
    
    useInMemoryPgpKeys(signingKey, signingPassword)
    sign publishing.publications.mavenJava
}

4. 配置源码和 JavaDoc 和 Java Source

发布到 Maven Central 必须要有 Java Docs 以及 Java Sources

groovy 复制代码
java {
    withJavadocJar()
    withSourcesJar()
}

javadoc 编译有可能失败,可以优化对 JavaDoc 的编译限制。

groovy 复制代码
tasks.withType(Javadoc) {
    options {
        encoding = 'UTF-8'
        charSet = 'UTF-8'
        author = true
        version = true
        links = ['https://docs.oracle.com/en/java/javase/17/docs/api/']
        if (JavaVersion.current().isJava9Compatible()) {
            addBooleanOption('html5', true)
        }
        // 忽略一些警告
        addStringOption('Xdoclint:none', '-quiet')
    }
    // 忽略 Javadoc 错误
    failOnError = false
}

多模块buildSrc结构项目配置

buildSrc 插件配置

我们在 buildSrc 的 gradle 插件扩展为: io.github.yourname.myproject.java-conventions.gradle 通过修改 java-conversion.gradle 文件,来添加 publish,内容与单模块一样。

groovy 复制代码
// java-conversion.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
    id 'signing'
}

// 其他配置参考单模块

publishing {
    publications {
        mavenJava(MavenPublication) {
            groupId = project.group
            version = project.version
            from(components.java)
            pom {
            // 参考单模块
            }
        }
    }
}

signing {
    required { gradle.taskGraph.hasTask("publish") }
    def signingKey = System.getenv("GPG_SIGNING_KEY")
    def signingPassword = System.getenv("GPG_SIGNING_PASSWORD")
    useInMemoryPgpKeys(signingKey, signingPassword)
    // 注意:这个使用 mavenJava
    sign publishing.publications.mavenJava
}

因为子模块会通过 id 'io.github.yourname.myproject.java-conventions' 来引用,所以配置会统一生效。

bom gradle 配置

groovy 复制代码
// 在 BOM 模块的 build.gradle 中
plugins {
    id 'java-platform'
    id 'maven-publish'
    id 'signing'
}

publishing {
    publications {
        mavenBom(MavenPublication) {
            from components.javaPlatform
            pom {
                name = 'Your Project BOM'
                description = 'Bill of Materials for Your Project'
                // ...其他 POM 信息
            }
        }
    }
}

signing {
    required { gradle.taskGraph.hasTask("publish") }
    def signingKey = System.getenv("GPG_SIGNING_KEY")
    def signingPassword = System.getenv("GPG_SIGNING_PASSWORD")
    useInMemoryPgpKeys(signingKey, signingPassword)
    // 注意:这个使用 mavenBom
    sign publishing.publications.mavenBom
}

上传 Maven Central

ncmp 插件

在项目 root build.gradle 中添加上传 maven central 插件,此次推荐使用 ncmp。

注意 : 这里SONATYPE_USERNAME不是咱们注册时的账号,而是通过 portal 生成的账号。

groovy 复制代码
// 主 build.gradle
plugins {
    id 'java-platform'
    id 'maven-publish'
    id 'signing'
    id("com.gradleup.nmcp").version("0.0.8")
}

allprojects {
    group = project.findProperty('group')
    version = project.findProperty('version')

    nmcp {
        publishAllPublications {

        }
    }
}

nmcp {
    publishAllProjectsProbablyBreakingProjectIsolation {
        username = System.getenv("SONATYPE_USERNAME");
        password = System.getenv("SONATYPE_PASSWORD");
        // 如果子模块很多建议使用 AUTOMATIC, 也可以使用 USER_MANAGED
        publicationType = "AUTOMATIC"
    }
}

发布

  1. 发布之前,使用 publish 命令检查下文件
bash 复制代码
./gradlew publish

可以看到一个 ncmp 的目录,检查是否存在 .asc 的签名文件,以及 .md5 等 checksum 文件

  1. 使用这个命令发布
bash 复制代码
./gradlew publishAllPublicationsToCentralPortal
相关推荐
两码事8 分钟前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili36 分钟前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手1 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
易元1 小时前
模式组合应用-桥接模式(一)
后端·设计模式
柑木1 小时前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析
灵魂猎手1 小时前
1. Mybatis Mapper动态代理创建&实现
java·后端·源码
泉城老铁1 小时前
在秒杀场景中,如何通过动态调整线程池参数来应对流量突增
后端·架构
小悲伤1 小时前
金蝶eas-dep反写上游单据
后端
用户9194287745951 小时前
FastAPI (Python 3.11) Linux 实战搭建与云部署完全指南(经验)
后端
板板正1 小时前
Spring Boot 整合MongoDB
spring boot·后端·mongodb