15、发布与签名:Maven Central发布、Artifactory、GPG签名
说实话,发布这件事,在Gradle生态里一直是个「看起来简单,做起来坑多」的活儿。我最早做开源库的时候,以为配个uploadArchives就完事了,结果被GPG签名折腾了一整天。嗯,今天咱们就把这块彻底讲透。
15.1 发布的核心流程
发布一个库到Maven Central,说白了就是三步:打包 → 签名 → 上传。但每一步都有讲究。
核心流程概览:
- 生成JAR、Sources JAR、Javadoc JAR
- 用GPG对产物进行签名(生成.asc文件)
- 上传到Maven Central的Staging仓库
- 在Sonatype的Nexus UI上执行Close和Release操作
我个人习惯用maven-publish插件配合signing插件来做。为什么不用旧的uploadArchives?因为maven-publish是官方推荐的,而且对Kotlin DSL支持更好。
15.2 配置maven-publish插件
先看一个最基础的配置。我在项目中通常这样写:
// build.gradle.kts
plugins {
`maven-publish`
signing
}
publishing {
publications {
register<MavenPublication>("release") {
from(components["java"])
// 这些信息会出现在POM文件中
pom {
name.set("My Library")
description.set("A useful library for Android")
url.set("https://github.com/your/repo")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("your-id")
name.set("Your Name")
email.set("your@email.com")
}
}
scm {
connection.set("scm:git:git://github.com/your/repo.git")
developerConnection.set("scm:git:ssh://github.com/your/repo.git")
url.set("https://github.com/your/repo")
}
}
}
}
repositories {
maven {
name = "sonatype"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.findProperty("sonatypeUsername") as String?
password = project.findProperty("sonatypePassword") as String?
}
}
}
}
小提示:我建议把sonatype的用户名密码放在~/.gradle/gradle.properties里,而不是直接写进build脚本。这样既安全,又方便不同项目复用。
15.3 GPG签名配置
签名这块,我曾经踩过一个坑:以为配好key就行,结果忘了指定signing.keyId,导致签名一直失败。
正确的做法是这样的:
// 在 gradle.properties 中
signing.keyId=你的密钥ID(8位十六进制)
signing.password=你的密钥密码
signing.secretKeyRingFile=/Users/you/.gnupg/secring.gpg
// 在 build.gradle.kts 中
signing {
sign(publishing.publications["release"])
}
⚠️ 重要提醒:GPG 2.1版本之后,默认不再生成secring.gpg文件。你需要手动导出:gpg --export-secret-keys -o ~/.gnupg/secring.gpg。我当初就是被这个坑了半小时。
15.4 发布到Artifactory
如果你用的是公司内部的Artifactory,流程会简单很多——不需要签名,也不需要Sonatype那一套Close/Release操作。
publishing {
repositories {
maven {
name = "artifactory"
url = uri("https://artifactory.company.com/artifactory/libs-release-local")
credentials {
username = project.findProperty("artifactoryUser") as String?
password = project.findProperty("artifactoryPassword") as String?
}
}
}
}
你想想看,Artifactory其实就是一个私有的Maven仓库。你只需要把产物推上去,别人就能通过maven { url }引用了。不需要签名,也不需要审核。
15.5 完整的发布脚本示例
下面是我在实际项目中用的一个完整脚本,包含了所有必要的配置:
plugins {
`java-library`
`maven-publish`
signing
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
register<MavenPublication>("mavenJava") {
from(components["java"])
versionMapping {
usage("java-api") {
fromResolutionOf("runtimeClasspath")
}
usage("java-runtime") {
fromResolutionResult()
}
}
pom {
name.set("My Library")
description.set("A concise description")
url.set("https://github.com/your/project")
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
id.set("yourname")
name.set("Your Name")
email.set("your.email@example.com")
}
}
scm {
connection.set("scm:git:git://github.com/your/project.git")
developerConnection.set("scm:git:ssh://github.com/your/project.git")
url.set("https://github.com/your/project")
}
}
}
}
repositories {
maven {
name = "sonatype"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.findProperty("ossrhUsername") as String?
password = project.findProperty("ossrhPassword") as String?
}
}
}
}
signing {
sign(publishing.publications["mavenJava"])
}
tasks.withType<Javadoc>() {
if (JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption("html5", true)
}
}
15.6 避坑指南
我整理了几个常见的坑,都是我自己或同事踩过的:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 签名失败:No secret key | secring.gpg文件不存在或路径不对 | 用gpg --export-secret-keys导出 |
| 上传401 Unauthorized | Sonatype账号未激活或token过期 | 检查ossrhUsername和ossrhPassword |
| Close失败:Missing POM | POM信息不完整 | 确保pom中包含了licenses、developers、scm |
| Javadoc生成报错 | Java 8+的Javadoc严格模式 | 添加options.addBooleanOption("html5", true) |
我的经验:第一次发布时,建议先用./gradlew publishToMavenLocal在本地测试一遍。确认所有产物(jar、sources-jar、javadoc-jar、asc文件)都生成正确了,再往远程推。这样能省去很多来回调试的时间。
15.7 自动化发布流程
如果你经常发布,手动操作Sonatype的Nexus UI会非常烦人。我推荐用io.github.gradle-nexus.publish-plugin这个插件,它可以自动完成Close和Release操作:
plugins {
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}
nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
username = project.findProperty("ossrhUsername") as String?
password = project.findProperty("ossrhPassword") as String?
}
}
}
配置好之后,一行命令就能完成全部流程:
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
嗯,这才是现代开发该有的体验。不用再手动点网页了。
小技巧:我习惯在CI/CD里配置这个流程。每次打tag的时候自动触发发布,省心又可靠。记得在CI环境里也要配置好GPG密钥和Sonatype凭据。
公众号:蓝海资料掘金营,微信deep3321