使用 Gradle 发布项目至 Maven Central

前言

将项目发布至 Maven Central,可以使开发者在使用时十分方便地进行整合和依赖管理。然而发布过程并不简单,因此将我发布的过程和遇到的问题记录于此。

Gradle

作为一个 Android 开发者,一般会使用 Gradle 进行构建与发布。广为使用的脚本是 Chris Banes 所写的 gradle-mvn-push,项目中的 README.md 有详细的步骤说明。

外部文章
gradle-mvn-push 的 README

我不喜欢将密码明文写在配置文件中,而是习惯从终端读入密码,因此我对 gradle-mvn-push.gradle 进行了一些修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}

def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
: "https://oss.sonatype.org/content/repositories/snapshots/"
}

...

gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.allTasks.any { it instanceof Sign }) {
allprojects {
ext."signing.password" = System.console() != null ? System.console().readLine("\nPGP private key password: ") : ""
}
}
}

此外,这个脚本在构建 JavaDoc 时会使用系统的语言设置,导致生成的文档可能是中文版本。通过将 LANG="en_US.UTF-8" 附加在 gradle 命令之前可以将语言设置为英文。

关于我自己采用的配置,可以参考我 在 Github 上的项目

OSSRH

为了在 Maven Central 上发布项目,一般会选择在 Sonatype OSSRH (OSS Repository Hosting) 上注册账户进行操作。最顶层的帮助是以下这篇文章,从其中的链接可以进入其他子页面查看详细步骤。

外部文章
OSSRH Guide

同时,这篇中文文章也较为详尽,对我的发布过程有很大帮助。

外部文章
使用 Gradle 发布 AAR 到 Maven 仓库

以下是我的步骤:

  1. 注册 JIRA 账户

  2. 创建工程工单

    Group Id 请填写顶级域名的反转,因为每个用户只需要创建一次工单,之后会将整个 Group Id 下的权限开放给用户。我填写了项目的 Group Id,之后被处理者修正。

    SCM URL 可以直接填写 GitHub 仓库的地址。我开始时一直尝试使用 SSH 克隆地址,但总是不能通过验证。

  3. 发布项目至暂存区。

    使用 gradle-mvn-push,执行 LANG="en_US.UTF-8" gradle clean build uploadArchives。这需要正确的 PGP 签名设置,将在这篇文章的稍后进行说明。

  4. 公开发布项目。

    前往 Sonatype OSS,登录,点击左侧边栏中的 Staging Repositories,找到并选择自己的项目,点击 Close,刷新并等待操作完成,之后点击 Release,即完成公开发布。

  5. 回复工单

    在处理者对工单的回复中,会要求你在公开发布第一个项目后回复此工单,照做即可。

GnuPG(2.1)

GnuPG 2.1 的使用是我在这次发布过程中遇到的最为棘手的问题,因为网络上大部分的教程还是基于之前的版本,导致许多已经更改的操作方式不再可用;并且 Gradle 也尚未支持这个版本,尽管 2.1 已在去年 11 月发布。因此在这里简单记录一些我遇到的问题和解决方案。

  1. 无法选定密钥长度。

    在 GnuPG 2.1 中,需要使用 --full-gen-key 以获取之前在 --gen-key 中可用的选项。

  2. 无法使用命令行输入密码。

    GnuPG 默认在有图形界面时使用会 XGrabKeyboard 的窗口进行密码输入,并且无法从剪贴板粘贴文本,给我使用随机密码带来了很多不便,因此我希望从命令行输入密码。

    在 GnuPG 2.1 中,一切相关的操作被交由 gpg-agent 完成,同时,--pinentry-mode loopback 被提示为不支持,即使在 gpg-agent.conf 中添加 allow-loopback-pinentry 也没有作用。

    最终我的解决方案是在执行 gpgDISPLAY="" 使其使用一个基于 curses 的命令行界面,然后对终端进行粘贴文本。

  3. secring.gpg 中找不到私钥。

    GnuPG 2.1 已经不再使用 secring.gpg,因此新创建的私钥不会向其中写入,而是会由 gpg-agent 写入至 private-keys-v1.d 目录。为了兼容 Gradle 签名插件,我将私钥导出,模拟了一个 secring.gpg

    1
    DISPLAY="" gpg --output secring.gpg --export-secret-keys <YOUR_KEY_ID>
  4. 需要将公钥发布至公钥服务器才能通过审核。

    在对项目进行 Close 时,会对签名进行验证,因此需要将你的公钥上传以使其可以被公开获取。我选择了发布至 MIT 公钥服务器。

    1
    gpg --keyserver pgp.mit.edu --send-key <YOUR_KEY_ID>

附加:为示例应用生成签名密钥

可以使用 Java 的 keytool 工具进行生成,但 Android Studio 提供了自带推荐参数并且简单易用的界面。

在 Android Studio 中,点击“Build > Generate Signed APK…”,如果已有密钥存储则点击“Choose existing…”并选择,之后点击“Create new…”填写信息并创建密钥即可。

这种方法并不会覆盖密钥存储中已有的密钥。但还是建议在操作之前,进行备份。

结语

发布项目到 Maven Central 的过程确实十分复杂,同时 GnuPG 进行了不兼容升级并且缺乏文档,所以令我花费了不少时间。

相比之下,通过 BinTray 发布则更为简单和自动化一些,然而它要求你将私钥上传至它的服务器,这令我不太舒服,因此并没有采用。

虽然麻烦,但是对于开发者来说,发布之后的使用还是十分舒服的,因此这些努力也算没有白费。记录在此,希望能够在其他开发者发布自己的项目时有所帮助。