gradle 项目如何发布到 Maven 中央库

如何把开发完成的 gradle 项目的产物发布到 Maven Central Repository 中,让大家可以使用到你的成果?上周走了一遍流程,把写的剪贴板操作的一个简单项目发布了一下,发现有点麻烦的,流程比较长,而且使用 gradle 的话,gradle 更新实在是太快,兼容性也太差,导致网上搜到到文档,其实都不太一样,更难绷的是是连 sonatype 官方给出的 gradle 发布教学文档 也是跟实际最新 gradle8 版本的不一致,因此只能参考下里面的流程,而细节只能靠自己摸索。

发布本质

项目发布本质上是将项目打包成一个可用的 artifact,里面有编译好字节码,以及包含项目相关信息的配置 pom 文件。这些产物可以上传到可供大众使用到托管平台中,如 Sonatype Nexus Repository,并最终同步到 Central Repository 当中。当需要使用依赖时,声明依赖的标识 groupId:artifactId:version,构建工具 Maven/gradle 就会自动到这些仓库获取依赖并使用。因此发布的核心操作其实就两个

  1. 项目打包成产物
  2. 产物上传到托管平台

发布流程

这里以发布到 sonatype 的仓库为例,这是一个开源的 artifact 托管平台,大家都可以使用,产物也都是公开的。

这里用的都是最新的 gradle 8.2

创建 ticket

想要发布产物到公开仓库,首先需要到 sonatype 注册一个 JIRA 帐号,然后提交一个 issue,为需要发布的产物创建一个 ticket。在这个 ticket 当中,需要填写将发布产物的基本信息,包括 groupId,项目地址,项目代码管理地址(SCM)。

在填写好 ticket 后,会由 sonatype 来审核相关信息,并会确认用户是否对使用的 groupId 具有所有权(即是否有 groupId 倒过来的域名控制权),比如如果我想要用自己的域名 top.yeungyeah 作为 groupId,就会要求我给域名的 dns 加个解析。除了这些通用域名,还可以使用 GitHub 提供的 GitHub Page 作为域名,如 io.github.username,这个的验证方法就比较简单,去创个 GitHub repo 即可。所以发布时的 groupId 得需要考虑下,不能乱填了。

完善项目

sonatype 对于发布的项目有一定的质量要求,其中就要求,除了项目编译后 jar 包,还需要将项目的源代码,以及 javadoc 都一并上传发布到托管平台。这就需要在 gradle 的构建配置文件build.gradle.kts当中进行额外的配置。

1
2
3
4
java {
    withJavadocJar()
    withSourcesJar()
}

另外,发布的项目其最终生成的 pom 配置文件里包含的信息要求也是完整的,比如项目的开发者信息,开源的 license,都需要进行额外的配置。

maven-publish

看到这里可能有些人会觉得很奇怪,为什么用 gradle,但是一直在说 Maven 的发布,发布也是发布到 Maven 的中央仓库当中。其实 gradle 是可以支持很多类型的依赖,除了 maven 以外,也可以支持 ivy。不过 Maven 的使用现在要更加广泛,所以基本都是在用 maven 仓库当中的依赖(而且 gradle 好像也没有搞自己的依赖格式标准)。

最新的 gradle 可以用其提供的 maven-publish 插件来完成发布相关的功能,而不是此前的 maven 插件,上面文档提到的许多配置,在这个新的 maven-publish 插件都不可用了😂

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
plugins {
    ...
    `maven-publish`
}

publishing {
    publications {
        create<MavenPublication>("maven") {
            // 自行替换产物描述信息
            groupId = "io.github.yeung66" 
            artifactId = "clipboard-jvm"
            version = version

            from(components["java"])

            pom {
                // 填充生成的 POM 信息
                name.set("clipboard-jvm")
                description.set("A simple clipboard library for jvm.")
                url.set("https://github.com/yeung66/clipboard-jvm")
                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("yeung66")
                        name.set("YeungYeah")
                        email.set("email@host.com")
                    }
                }
                scm {
                    connection.set("scm:git:git://github.com/yeung66/clipboard-jvm.git")
                    developerConnection.set("scm:git:ssh://github.com/yeung66/clipboard-jvm.git")
                    url.set("https://github.com/yeung66/clipboard-jvm")
                }

            }
        }
    }
    repositories {
        maven {
            name = "OSSRH"
            url = URI.create("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
            credentials {
                username = project.properties["ossrhUsername"] as String
                password = project.properties["ossrhPassword"] as String
            }
        }
    }   
}

其中帐号密码可以放到用户目录下的 gradle.properties 当中,防止泄漏。

signing

在生成产物后,还需要对生成的产物进行一次签名,这样托管平台才能确保这个上传是由本人完成,保证产物的可靠性。对发布产物签名,需要先生成自己的密钥,并使用直接的私钥文件签名。然后把公钥上传到公钥服务器上面,供托管平台取得公钥验证签名。

具体步骤如下

  1. 首先使用 gpg 生成自己的密钥,可参考阮一峰的 这个文档
  2. 根据上面生成的密钥,取得私钥的路径,使用私钥的密码,公钥的 keyId,并填入 gradle.properties 文件当中。为了安全,不泄漏个人信息,一般这种敏感信息可以放到用户目录下的配置文件而不是项目当中的,这样就能够好地避免随着代码仓库上传到 GitHub 而被泄漏。
    • signing.keyId= #The last 8 symbols of the keyId
      signing.password= #passphrase used to protect your private key
      signing.secretKeyRingFile= #absolute path to the secret key ring file containing your private key
      
    • (Since gpg 2.1, you need to export the keys with command gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg).
  3. 把公钥上传到公钥服务器上
  4. 在构建问题当中添加 signing 的插件,并进行配置
    plugins {
        ...
        signing
    }
    
    signing {
        sign(publishing.publications["maven"])
    }
    

在进行上面的配置后,执行构建发布任务时,会自动对构建产物进行一个签名。

执行发布

直接执行 gradle 的发布任务即可

执行顺利的话,项目会将编译得到的产物打包,签名,然后上传到托管平台当中。此时可以在 托管平台 里面进行一个 release 发布。

在 staging repositories 当中找到刚刚发布的产物,检查上传的文件无误后,点击 close 按钮,就会自动推进到检查阶段,检查发布的产物是否符合标准,这里面的检查就包括上面提到的质量检查,信息检查。检查通过后会就会发送邮件提醒,然后就可以点击 release 按钮,正式将产物发布。发布后产物会同步到 Maven Central 仓库当中,就可以在 mvnrepository 或者 sonatype central 查询到。 不过这个 staging 的步骤好像也能通过 插件 进行自动化。如果可以自动化估计可以简单点。


至此,gradle 项目就可以成功发布到 Maven Central 仓库当中,供大家使用了。