快速发布组件到mavenCentral

2,007 阅读13分钟

前言

在很久之前写过一篇发布开源库到jcenter的文章,但不幸的是几个月前Jfrog发布了终止Bintray服务的声明,声明的大概意思是说2021年3月31号之后Jcenter仓库将不再接收用户的组件提交,同时将Jcenter设为只读代码仓库,无限期地提供现有组件供用户下载,也就是说目前Jcenter仓库的状态是你无法再提交组件的更新,但你可以继续下载你以前托管的组件版本,所以现在你要做的就是把你的组件的新版本发布到其他仓库,例如Jitpack和MavenCentral,我曾经写过一篇快速发布开源库到jitpack的文章,本篇文章的内容是教你如何发布组件到MavenCentral仓库,同时写了一个发布脚本简化发布过程,使用这个脚本之前先看一下本篇文章的前期准备内容。

脚本地址:MavenPublishScript

前期准备

在讲解之前,有必要介绍一下Sonatype 、OSSRH、MavenCentral之间的关系,Sonatype是一间公司,它运营着MavenCentral仓库,我们想要发布组件到MavenCentral,必须要通过Sonatype的OSSRH,OSSRH即OSS Repository Hosting,对象存储仓库托管,Sonatype使用Nexus Repository Manager为组件提供存储库托管服务,我们发布组件时要先发布到Sonatype OSSRH上,然后才能同步到MavenCentral仓库,就好像我们之前使用Jcenter时,要先发布到Jfrog Bintray上,然后才能同步到Jcenter仓库。

1、注册Sonatype账号

首先你需要注册一个Sonatype Jira账号,Sonatype使用Jira管理你的groupId申请过程,注册地址如下:

Sonatype Jira - Sign up

点开注册地址后,如图:

mavencentral1.png

按要求填入你的邮箱地址、姓名、用户名、密码即可,其中用户名最好不要有中文,记住你的用户名和密码,会在后面用到。

2、申请groupId

我们平时使用托管在远程仓库的组件时都是通过它的GAV坐标来定位的,GAV即groupId、artifactId、version,其中groupId你可以理解为你自己在Sonatype OSSRH创建的仓库,groupId就是你仓库的名称,申请groupId就是在Sonatype OSSRH申请创建属于你的仓库,我们后面发布组件时要先发布到Sonatype OSSRH上名为groupId的仓库,然后才能同步到MavenCentral仓库。

还有申请的groupId并不是顺便填的,按照Sonatype的要求,groupId必须要是一个域名的反写,所以你要拥有一个域名,当你申请groupId时,Sonatype会用某种方式让你证明你是这个域名的所有者。

如果你拥有某个域名,例如example.com域名,你可以使用任何以com.example开头的groupId,例如com.example.test1、com.example.test2等,如果你没有自己的域名也没关系,Sonatype支持代码托管平台的Pages网站域名,例如Github,你可以在你的Github账号上开启你的Pages服务,这样你就拥有了一个与Github账号关联的个人域名,格式为{username}.github.io,例如我的Github Pages网站就是rain9155.github.io,很多人都是利用这种托管在三方平台的网站搭建自己的博客网站,除了Github,Sonatype还支持GitLab、Gitee等,下面表格列出这些常用的代码托管平台Pages服务开启的官方教程地址,和开启后对应的域名和相应的groupId:

代码托管平台Pages服务开启教程地址域名groupId
Githubpages.github.com/{username}.github.ioio.github.{username}
Giteegitee.com/help/articl…{username}.gitee.ioio.gitee.{username}
GitLababout.gitlab.com/stages-devo…{username}.gitlab.ioio.gitlab.{username}

下面就以我的Github Pages网站域名rain9155.github.io为例,申请名为io.github.rain9155的groupId,首先打开Sonatype Jira网站, 地址如下:

Sonatype Jira - Dashboard

首次进入需要你登陆,输入你刚才注册的Sonatype Jira用户名和密码登陆,然后就进入Sonatype Jira网站首页,然后点击导航栏的Create按钮,此时会弹出一个弹窗,会让你填一些申请groupId时需要的信息,其中Project选择Community Support - Open Source Project Repository Hosting (OSSRH),Issue Type选择New Project,Summary填一个标题,Group Id就填你要申请的groupId,Project URL随便填一个你的组件仓库地址,SCM url也是随便填一个你的组件仓库版本控制地址,如下:

mavencentral2.png

最后点击create按钮,它会创建一个issue,issue名称格式为OSSRH-{taskId},如我这里为OSSRH-69596,你可以在All Projects面板中看到它,然后接下来就是等待Sonatype Jira的邮件通知,邮件会发送到你注册账号时填写的邮箱,它的第一封邮件会叫你在你的Github中创建一个名为OSSRH-{taskId}的空仓库,从而证明你是groupId对应域名的拥有者,当你创建之后,你需要到OSSRH-{taskId}下的comment面板中回复,当你回复后,它又会再发一封邮件给你,告诉你groupId已经申请完毕,此时你可以发布组件到Sonatype OSSRH中,如何发布请看后面的内容,当你发布后,你需要在Sonatype OSSRH中把你的组件同步到MavenCentral后才可以通过GAV引用它,如何同步请看后面的内容,当你同步后,你需要再次到OSSRH-{taskId}下的comment面板中回复,然后Sonatype OSSRH才会为你激活组件同步到MavenCentral的程序,整个交流过程可以参考我OSSRH-69596中的comment面板,如下:

mavencentral3.png

回复的内容是什么不重要,只要你回复了就行,上述就是申请groupId和激活MavenCentral同步的整个流程,只需要在第一次发布组件时进行一次就行,以后发布组件时不需要再进行上面的过程,直接使用该groupId就行。

3、生成gpg签名信息

Sonatype要求发布到MavenCentral仓库的组件中的每个文件都需要通过gpg签名,gpg是一个命令行工具,提供了对数据的签名和加密能力,支持主流的签名和加密算法,并提供了密钥管理系统,要使用gpg签名,我们必须先在电脑上安装gpg,然后使用gpg生成签名需要的密钥对。

我们首先来安装gpg,对于mac电脑,直接通过Homebrew安装就行,在命令行执行:

$ brew install gpg

对于window电脑,我们可以下载gpg的执行文件安装,下载地址如下:

gpg download

安装完成后,在命令行输入gpg --version 输出gpg的版本信息表示安装完成,如下:

$ gpg --version
gpg (GnuPG) 2.2.27
libgcrypt 1.8.7
Copyright (C) 2021 g10 Code GmbH
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: C:/Users/HY/AppData/Roaming/gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

然后在命令行输入gpg --full-generate-key 进行密钥对生成,这个命令会让你一步步选择生成密钥对需要的信息,如下:

$ gpg --full-generate-key
gpg (GnuPG) 2.2.27; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)
Requested keysize is 3072 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: rain9155
Email address: jianyu9155@gmail.com
Comment:
You selected this USER-ID:
    "rain9155 <jianyu9155@gmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key DF7B4B4D1A32FB02 marked as ultimately trusted
gpg: revocation certificate stored as 'C:/Users/HY/AppData/Roaming/gnupg/openpgp-revocs.d\9E02D0E104E3D3517B5B0E54DF7B4B4D1A32FB02.rev'
public and secret key created and signed.

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.
pub   rsa3072 2021-07-06 [SC]
      9E02D0E104E3D3517B5B0E54DF7B4B4D1A32FB02
uid                      rain9155 <jianyu9155@gmail.com>

首先它会让你选择算法,我这里选择了RSA,仅用于签名,然后会让你输入密钥长度,默认是3072bits,然后会让你选择密钥的过期时间,我这里选择了永久有效,建议大家也选择永久有效,然后确认信息后会让你输入姓名、邮箱、注释来作为密钥的唯一标识,这里我生成的标识为"rain9155 <jianyu9155@gmail.com>",最后确认后会弹出一个弹窗让你输入一个密码来保护你的私钥,记住你输入的密码后面会用到,点击确认后看到public and secret key created and signed这句话说明密钥对生成完毕,我们可以通过gpg --list-keys列出生成的公钥信息,通过gpg --list-secret-keys列出生成的私钥信息,但其实列出的信息是类似的,我们以gpg --list-keys为例,如下:

$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
C:/Users/HY/AppData/Roaming/gnupg/pubring.kbx
---------------------------------------------
pub   rsa3072 2021-07-06 [SC]
      9E02D0E104E3D3517B5B0E54DF7B4B4D1A32FB02
uid           [ultimate] rain9155 <jianyu9155@gmail.com>

列出的信息首先有公钥的文件位置,这里为C:/Users/HY/AppData/Roaming/gnupg/pubring.kbx,接着是pub rsa3072 2021-07-06 [SC],表示使用RSA算法,公钥长度为3072bits,创建日期为2021-07-06,接着是长长的一串id值9E02D0E104E3D3517B5B0E54DF7B4B4D1A32FB02,它表示公钥的id,我们使用时只需要用到它的后八位就行,所以我们后面使用这个公钥时只需要使用1A32FB02就行,最后是公钥的唯一标识uid,根据我们前面输入的姓名、邮箱、注释生成。

我们签名只需要用到私钥,而公钥需要上传到公钥服务器,这样我们用私钥签名的文件上传到MavenCenral后,它才能使用公钥验证这个文件,这里我选择了keyserver.ubuntu.com这个公钥服务器,上传公钥的命令格式为gpg --keyserver 公钥服务器地址 --send-keys 公钥id,如下我把刚刚生成的公钥1A32FB02上传到公钥服务器:

$ gpg --keyserver keyserver.ubuntu.com --send-keys 1A32FB02
gpg: sending key DF7B4B4D1A32FB02 to hkp://keyserver.ubuntu.com

没有错误提示就表示上传成功,我们还可以使用gpg --keyserver 公钥服务器地址 --recv-keys 公钥id从公钥服务器导入公钥到本地从而验证公钥是否上传成功,如下:

$ gpg --keyserver keyserver.ubuntu.com --recv-keys 1A32FB02
gpg: key DF7B4B4D1A32FB02: "rain9155 <jianyu9155@gmail.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1

可以看到导入成功,表示公钥已经成功上传到公钥服务器。

还有最后一步,把私钥导出到一个文件中,这样我们发布组件时才能通过引用这个文件,从而使用私钥进行签名,命令格式为gpg -o 导出的文件路径 --export-secret-key 私钥id,执行导出私钥命令会弹出一个弹窗让你输入私钥密码,输入正确后才能成功导出,如下把私钥导出到名为1A32FB02.gpg的文件中,私钥id和公钥id是一样的:

$ gpg -o D:/File/keystore/1A32FB02.gpg --export-secret-key 1A32FB02

现在已经成功把私钥导出D:/File/keystore/1A32FB02.gpg中,记住你导出的私钥文件路径,后面会用到。

发布到OSSRH

现在万事俱备,可以发布组件了,发布组件使用maven-publish插件,我们主要发布的内容有组件的aar或jar文件、sources文件、javadoc文件,pom文件,还有这些文件的签名文件,以.asc为后缀,签名要使用signing插件,为了简化发布过程,我已经把发布过程编写成了一个gradle脚本 - MavenPublishScript,使用时只需要apply进来,然后填写发布的基本信息,执行发布任务就可以自动生成发布所需的所有文件并发布到OSSRH中,下面简单介绍如何使用这个gradle脚本。

首先在你的组件的build.gradle中apply该脚本:

apply from: 'https://raw.githubusercontent.com/rain9155/MavenPublishScript/main/script/publication.gradle'

接下来准备好你在前面注册的Sonatype账号、申请好的groupId、生成好的gpg私钥信息,然后在组件的gradle.properties(没有则创建)中添加组件信息,其中GAV坐标是必填信息,其他是可选信息:

### GAV坐标
publish.groupId=io.github.rain9155
publish.artifactId=mavenpublishscript
publish.version=1.0.0

### 下面都是可选信息
# 基本描述
publish.description=发布组件到Maven仓库的gradle脚本,支持aar和jar发布
publish.url=https://github.com/rain9155/MavenPublishScript
# 开发者信息
publish.developerName=rain9155
publish.developerEmail=jianyu9155@gmail.com
# license信息
publish.licenseName=The Apache License, Version 2.0
publish.licenseUrl=http://www.apache.org/licenses/LICENSE-2.0.txt
# scm信息,格式参考http://maven.apache.org/scm/scm-url-format.html
publish.scmUrl=https://github.com/rain9155/MavenPublishScript/tree/master
publish.scmConnection=scm:git:git://github.com/rain9155/MavenPublishScript.git
publish.scmDeveloperConnection=scm:git:ssh://github.com:rain9155/MavenPublishScript.git
# 如果发布的是android组件,当为false时不根据flavor动态生成组件的artifactId,如果你不想组件的artifactId拼接flavorName,可以设置为false,默认为true
publish.artifactId.isAppendFavorName=false

然后在项目根目录的local.properties(没有则创建)中添加gpg签名信息和ossrh账号信息,记得要把local.properties从你的版本控制中移除,避免泄漏你的签名信息和账号信息:

# gpg签名信息
signing.keyId=你的密钥id,例如1A32FB02
signing.password=你的私钥密码
signing.secretKeyRingFile=你的私钥文件路径,例如D:/File/keystore/1A32FB02.gpg

# ossrh账号信息
ossrh.username=你的ossrh账号名,即Sonatype账号用户名
ossrh.password=你的ossrh账号密码,即Sonatype账号密码

最后在命令行执行gradle任务发布组件,如果你发布的是android组件(aar),执行的任务的名称格式为publish{flavorName}AndroidlibPublicationToMavenRepositorypublish{flavorName}AndroidlibPublicationToMavenLocal,其中flavorName为android组件的flavor的名称,首字母大写,没有则不填flavorName,如果你发布的是java组件(jar),执行的任务名称为publishJavalibPublicationToMavenRepositorypublishJavalibPublicationToMavenLocal

//发布android组件到maven本地仓库
gradle publishAndroidlibPublicationToMavenLocal

//发布android组件到maven远程release或snapshot仓库
gradle publishAndroidlibPublicationToMavenRepository

//假设android组件含有flavorName为china,发布china版本的android组件到maven本地仓库
gradle publishChinaAndroidlibPublicationToMavenLocal

//假设android组件含有flavorName为oversea,发布oversea版本的android组件到maven远程release或snapshot仓库
gradle publishOverseaAndroidlibPublicationToMavenRepository

//发布java组件到maven本地仓库
gradle publishJavalibPublicationToMavenLocal

//发布java组件到maven远程release或snapshot仓库
gradle publishJavalibPublicationToMavenRepository

//发布所有android组件和java组件到maven本地仓库
gradle publishToMavenLocal

//发布所有android组件和java组件到maven远程release或snapshot仓库
gradle publishAllPublicationsToMavenRepository

//发布所有android组件和java组件到maven本地仓库和maven远程release或snapshot仓库
gradle publish

上述发布任务根据组件发布的是android组件(aar)还是java组件(jar)来执行,同时发布android组件时支持flavor发布,publishXXToMavenRepository任务表示发布到远程仓库OSSRH中,publishXXToMavenLocal任务表示发布到maven本地仓库,maven本地仓库路径为{用户目录}/.m2/repository/,也可以在AS左侧的gradle面板中查看这些任务,如下:

mavencentral4.png

选中合适的任务双击执行就行,如果你执行的是publishXXToMavenRepository任务,任务执行成功后你就可以到OSSRH查看你发布上去的组件,首次进入需要登陆,输入你的Sonatype用户名和密码登陆,然后点击左下侧的Staging Repository就可以看到你发布到OSSRH的组件,如下:

mavencentral5.png

在Staging Repository的面板中可以浏览你发布的组件,选中它,然后点击下面的content面板就可以查看组件发布的详细文件。

同步到MavenCentral

组件发布到OSSRH后,还不能通过GAV引用它,还要同步到MavenCentral后才能通过GAV引用它,首先要在OSSRH上Close组件,让它从open状态变为close状态,如下:

mavencentral6.png

当你点击Close按钮后,会弹出一个弹窗叫你确认,确认后Close按钮会变灰,此时下方的Activity面板会显示close过程中进行的规则校验,例如校验你发布的文件是否有签名、是否含有javadoc文件、是否含有sources文件,pom文件是否合格等,当所有校验通过后你才可以同步组件到MavenCentral,当校验通过后你会收到Sonatype发送的邮件,此时你可以点击上方的Release按钮同步组件到MavenCentral,如下:

mavencentral7.png

当你点击Release按钮后,会弹出一个弹窗叫你确认,确认后Release按钮会变灰,如果你是第一次发布组件,在申请groupId时我讲过,你还要到OSSRH-{taskid}的comment面板下回复它一次OSSRH才会为你激活同步程序,激活同步程序后大概十几分钟你的组件就会被同步到MavenCentral,这时你就可以在MavenCentral网站搜索到你的组件,并可以在gradle或maven等构建工具中通过GAV引用它,激活同步程序后,你下次Release组件时就会自动同步到MavenCentral而无需再做其他操作。

结语

发布到MavenCentral的步骤相比于发布到Jitpack要复杂的多,Jitpack只需要一个代码托管仓库账号,而MavenCentral需要准备Sonatype账号、个人域名、签名信息,但是MavenCentral比Jitpack成熟得多,它目前是最大的java组件和其他开源组件的托管仓库,也是很多构建系统如Maven的默认存储仓库,如何选择就看个人爱好了。

以上就是本文的所有内容!

参考资料:

JCenter service update

The Central Repository Documentation

Use the Maven Publish plugin