背景说明
最近有个项目希望发布到 Maven 中央仓库,发现从 2024 年 3 月 12 日起,所有注册都将通过 Maven Central Portal 进行, 不再走过去的提交 issue 的那套流程。
而网上大部分都是介绍使用旧版本的 OSSRH
的发布方法,新版本的发包的介绍很少,并且由于 Gradle 插件目前没有官方的支持,三方插件很多,其中坑不少,有些插件按照文档配置并不能正常的发布。
加上,我们的这个项目是包含多个模块及支持 BOM 的 Gradle 项目,使得发布的配置很难有比较好的参考,折腾的好几天,总算找个比较合理的方案解决。
今天就把这个最新的方案分享一下,希望大家少踩坑,省时省力的发布到新的 Maven Central。
另外,旧版的注册与发布,可以参考下面的两个文档
准备工作
注册Central Portal账户
- 通过 Maven Central Portal 注册账号,建议使用 github 直接登录。
- 创建命名空间如(
io.github.username
),需验证域名或GitHub仓库所有权。 对于io.github
可以通过 github 直接注册登录Maven Central Portal即可自动完成验证。 对于自定义的域名,首先这个域名没有在 OSSRH 上注册过,如果注册过,会提示无法注册,并给了一个 support 邮件,可以通过邮件联系。 - 通过 Portal 生成用户令牌(
SONATYPE_USERNAME
和SONATYPE_USERNAME
),后面的配置会用到
GPG密钥准备
安装 GnuPG
- mac: 直接使用
brew install gpg
- windows: 从 https//www.gnupg.org/download/ 下载 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"
}
}
发布
- 发布之前,使用 publish 命令检查下文件
bash
./gradlew publish
可以看到一个 ncmp 的目录,检查是否存在 .asc 的签名文件,以及 .md5 等 checksum 文件
- 使用这个命令发布
bash
./gradlew publishAllPublicationsToCentralPortal