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 的完整性要求很严格,少一个 developerscm 都不行。所以建议先把 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 后,整个人都清爽了。

注意:SNAPSHOT 版本不要发布到 Maven Central 的 release 仓库。Sonatype 的 Nexus 会自动拒绝。SNAPSHOT 应该走 snapshot 仓库。

插件标记(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 解析失败。

核心要点:插件标记是 Gradle 插件生态的「路由表」。没有它,Gradle 就不知道插件 ID 对应哪个 Maven 坐标。

发布流程的完整示意图

下面这张图,是我自己总结的发布流程。你看一眼,基本就明白整个链路了。

插件发布到 Maven Central 流程 1. 本地构建 ./gradlew build 2. 生成 POM + 签名 signing + generatePom 3. 发布到 Sonatype publishToSonatype 4. 关闭并发布 closeAndReleaseStaging 5. Maven Central 同步 自动同步(约2小时) 插件标记(Plugin Marker) 坐标格式: 插件ID:插件ID.gradle.plugin:版本 示例: com.example.my-plugin: com.example.my-plugin.gradle.plugin:1.0.0 由 java-gradle-plugin 自动生成

这张图里,我特意把「插件标记」单独拎出来放在左侧。为什么?因为很多人发布时只关注了右侧的流程,忘了标记的存在。结果插件发布成功了,别人却用不了。

避坑指南:我踩过的几个坑

  • 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