17、自定义插件开发(四):插件发布到Maven Central、版本管理、插件标记(Plugin Marker Artifact)
插件写好了,测试也通过了,接下来就是把它发布出去。说实话,这一步才是真正让插件「活起来」的关键。我自己早期做插件开发时,插件写了一大堆,结果全躺在本地硬盘里吃灰。后来才意识到,发布到公共仓库,尤其是 Maven Central,才是让团队甚至社区用上你插件的唯一正途。
这一章我们重点聊三件事:怎么把插件发布到 Maven Central,版本号怎么管,以及那个容易被忽略的「插件标记」到底是什么鬼。
发布到 Maven Central 的完整流程
Maven Central 是 Java 生态里最权威的仓库。Gradle 插件要发布上去,得走一套标准流程。我当年第一次搞的时候,被 GPG 签名折腾了一下午,后来发现其实就是几个步骤没理顺。
先看一个典型的 build.gradle.kts 配置:
plugins {
`java-gradle-plugin`
`maven-publish`
signing
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}
group = "com.example"
version = "1.0.0"
gradlePlugin {
plugins {
register("myPlugin") {
id = "com.example.my-plugin"
implementationClass = "com.example.MyPlugin"
}
}
}
publishing {
publications {
create<MavenPublication>("pluginMaven") {
from(components["java"])
pom {
name.set("My Plugin")
description.set("A custom Gradle plugin for ...")
url.set("https://github.com/your/repo")
licenses {
license {
name.set("Apache-2.0")
url.set("https://opensource.org/licenses/Apache-2.0")
}
}
developers {
developer {
id.set("yourId")
name.set("Your Name")
}
}
scm {
connection.set("scm:git:git://github.com/your/repo.git")
url.set("https://github.com/your/repo")
}
}
}
}
}
nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}
}
signing {
sign(publishing.publications["pluginMaven"])
}
这个配置看起来长,其实核心就几块:
- java-gradle-plugin:让 Gradle 知道这是个插件项目
- maven-publish:提供发布能力
- signing:签名是 Maven Central 的硬性要求
- nexus-publish-plugin:帮你把产物推送到 Sonatype 的 Nexus 仓库
发布命令也很简单:
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
这里有个坑,我踩过。第一次执行时,closeAndRelease 可能会失败,原因是 POM 文件里缺少某些字段。Maven Central 对 POM 的完整性要求很严格,少一个 developer 或 scm 都不行。所以建议先把 pom 块写全,别偷懒。
./gradlew generatePomFileForPluginMavenPublication 生成 POM 文件,检查一下内容是否完整。
版本管理:别让版本号成为你的噩梦
版本管理这件事,说大不大,说小不小。我见过不少项目,版本号乱得像一锅粥。我个人习惯用 语义化版本,也就是 主版本.次版本.补丁 的格式。
| 版本变化 | 含义 | 举例 |
|---|---|---|
| 主版本递增 | 不兼容的 API 修改 | 1.0.0 → 2.0.0 |
| 次版本递增 | 向下兼容的功能新增 | 1.0.0 → 1.1.0 |
| 补丁版本递增 | 向下兼容的问题修复 | 1.0.0 → 1.0.1 |
在 Gradle 里,版本号可以直接写在 build.gradle.kts 里,但我更推荐用 gradle.properties 来管理:
# gradle.properties
version=1.0.0
然后在构建脚本里引用:
version = project.findProperty("version") ?: "1.0.0-SNAPSHOT"
这样做的好处是,CI/CD 脚本可以轻松替换版本号,不用改构建文件。我曾经在一个项目里,因为版本号硬编码在 build.gradle 里,每次发版都要手动改,后来改成 gradle.properties 后,整个人都清爽了。
插件标记(Plugin Marker Artifact)是什么?
这个问题,说实话,我刚开始也没搞明白。直到有一次,别人用我的插件时,Gradle 报错说找不到插件标记,我才去研究了一下。
插件标记,说白了就是一个特殊的 POM 文件。它的作用是把「插件 ID」和「实际的 Maven 坐标」关联起来。
举个例子,你的插件 ID 是 com.example.my-plugin,实际的 Maven 坐标是 com.example:my-plugin:1.0.0。Gradle 怎么知道这两者的对应关系?就是靠插件标记。
插件标记的坐标格式是固定的:
插件ID:插件ID.gradle.plugin:版本号
比如:
com.example.my-plugin:com.example.my-plugin.gradle.plugin:1.0.0
这个标记的 POM 文件里,会通过 <dependency> 指向实际的插件 artifact:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.my-plugin</groupId>
<artifactId>com.example.my-plugin.gradle.plugin</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-plugin</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
当你使用 java-gradle-plugin 插件时,Gradle 会自动生成这个标记 artifact。你不需要手动写。但如果你用的是 maven-publish 自定义发布,就得小心了——我曾经见过有人忘了发布标记,结果插件 ID 解析失败。
发布流程的完整示意图
下面这张图,是我自己总结的发布流程。你看一眼,基本就明白整个链路了。
这张图里,我特意把「插件标记」单独拎出来放在左侧。为什么?因为很多人发布时只关注了右侧的流程,忘了标记的存在。结果插件发布成功了,别人却用不了。
避坑指南:我踩过的几个坑
- GPG 密钥没上传到公共服务器:签名时本地能通过,但 Sonatype 验证时会失败。记得用
gpg --keyserver keyserver.ubuntu.com --send-keys 你的密钥ID上传。 - POM 里缺少
<scm>或<licenses>:Maven Central 要求这些字段必须存在。我一开始觉得麻烦没写,结果被驳回两次。 - 插件标记没发布:如果你自定义了发布逻辑,记得检查是否包含了标记 artifact。否则用户会收到
Plugin with id 'xxx' not found的错误。 - 版本号带
-SNAPSHOT却发到了 release 仓库:Sonatype 会直接拒绝。SNAPSHOT 版本只能发到 snapshot 仓库。
./gradlew publishToMavenLocal,确认本地能正常使用。然后再走远程发布流程。这一步能省下不少来回折腾的时间。
好了,插件发布这块的内容就这些。说白了,流程不复杂,但细节多。你只要把 POM 写全、签名配好、标记别漏,基本一次过。我当年第一次发布时,前前后后折腾了三天,现在回想起来,其实就是几个小细节没注意到。希望你能一次成功。
公众号:蓝海资料掘金营,微信deep3321