太长不读篇
- 在 issues tracker 上注册
- 创建 issues
- 配置 build.gradle
- gpg 生成 key pair 以便签名
- 上传 Release Archive
- 关闭并验证 Staging 环境的 Archive
- 发布 Archive
- 通知 issue 管理员开启同步
细读篇
1. 注册
Maven Central 网站并不提供注册的功能,你需要到 Sonatype 网站上进行注册。而事实上,Sonatype 网站也没有直接提供一个注册链接。真正的注册入口在 issues tracker 上。一旦完成注册后,你需要创建包含待发布包信息的 issue。
2. 创建 issue
在 Sonatype 的 dashboard 上点击创建按钮,根据弹出框的提示,填写简介、描述、GroupId、Project URL、SCM url 以及你在 jira 上的用户名。创建完毕后,会被自动跳转到该 issue 的详情页并分配一个唯一的ID,如:OSSRH-33944。余下的时间只需要等待,一般在两个工作日之内,Sonatype 的工作人员就会着手处理,然后他会在该 issue 底下的评论区留言。
如果代码是托管在 github 上,按照惯例,GroupId 应该取 github 上的域名,比如:com.github.qianyan。不过,这里我预备上传的包的 GroupId 是 com.lambeta,这是我购买的域名。审核者对此有所顾虑,所以很贴心地留言如下:
Do you own the domain lambeta.com? If not, please read:
http://central.sonatype.org/pages/choosing-your-coordinates.html
You may also choose a groupId that reflects your project hosting, in this case, something like io.github.qianyan or com.github.qianyan
在回复这个域名确实为我所有之后,工作人员就贴出不同环境的仓库地址。
Configuration has been prepared, now you can:
Deploy snapshot artifacts into repository https://oss.sonatype.org/content/repositories/snapshots
Deploy release artifacts into the staging repository https://oss.sonatype.org/service/local/staging/deploy/maven2
Promote staged artifacts into repository 'Releases'
Download snapshot and release artifacts from group https://oss.sonatype.org/content/groups/public
Download snapshot, release and staged artifacts from staging group https://oss.sonatype.org/content/groups/staging
please comment on this ticket when you promoted your first release, thanks
最后一句很重要,说的是,当我第一次正式发布的时候,需要留言告知工作人员,以便他们开启中央仓库的同步,这样我的包才会在 Maven Central 仓库中可见。
3. 配置项目的 build.gradle
拿到仓库地址,我们就需要在自己的项目中进行一些必要的配置,包含:jar、sourcesJar、javadocJar 以及对这些产物的 signing(签名)。
maven 插件
apply plugin: 'maven'
maven 插件提供了 uploadArchives task,我们需要在这个 task 中配置仓库地址,以及 pom 的相关信息,因为上载到 maven 仓库的包必须要有 pom 文件,否则无法查找或被依赖。具体配置如下:
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { deployment -> signing.signPom(deployment) }
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots") {
authentication(userName: sonatypeUsername, password: sonatypePassword)
}
pom.project {
name project.name
packaging 'jar'
description 'underscore string in java'
url 'https://github.com/qianyan/underscore.string.java'
scm {
url 'https://github.com/qianyan/underscore.string.java'
connection 'https://github.com/qianyan/underscore.string.java.git'
developerConnection 'git@github.com:qianyan/underscore.string.java.git'
}
licenses {
license {
name 'MIT Licence'
url 'https://raw.githubusercontent.com/qianyan/underscore.string.java/master/LICENSE'
distribution 'repo'
}
}
developers {
developer {
id 'lambeta'
name 'Yan Qian'
email 'qianyan.lambeta@gmail.com'
}
}
}
}
}
}
对应生成的 pom.xml 大致如下:
<name>underscore.string.java</name>
<description>underscore string in java</description>
<url>https://github.com/qianyan/underscore.string.java</url>
<licenses>
<license>
<name>MIT Licence</name>
<url>https://raw.githubusercontent.com/qianyan/underscore.string.java/master/LICENSE</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>lambeta</id>
<name>Yan Qian</name>
<email>qianyan.lambeta@gmail.com</email>
</developer>
</developers>
<scm>
<connection>https://github.com/qianyan/underscore.string.java.git</connection>
<developerConnection>git@github.com:qianyan/underscore.string.java.git</developerConnection>
<url>https://github.com/qianyan/underscore.string.java</url>
</scm>
注意 authentication(userName: ossrhUsername, password: ossrhPassword)
和 authentication(userName: sonatypeUsername, password: sonatypePassword)
,这里的用户名和密码其实就是在 Sonatype 上注册的用户名和密码。为了让 gradle 脚本顺利执行,需要在当前工程下的 gradle.properties 文件中设置对应的变量,如下:
sonatypeUsername=
sonatypePassword=
ossrhUsername=
ossrhPassword=
这份文件会作为源代码的一部分提交,所以聪明的我们不会傻傻地把自己的用户名和密码 push 到 github 上面。和大部分 *nix 系统上的工具类似,gradle 也有本地配置,我们可以新建一份 gradle.properties 文件到 ~/.gradle/gradle.properties
,然后把用户名和密码写入其中。这样,实际运行时,本地配置就会覆盖项目下对应的这些变量值。
4. 设置 gpg 以签名 Archive
gpg 生成的 key pair 主要是供签名使用的。假定本机已经安装 gpg,首先使用 gpg 生成 key pair。
$ gpg --gen-key
然后,查找你的 keyId:
$ gpg --list-keys
# ->
pub 2048R/XXXXXX 2017-09-14 [expires: 2018-05-14]
uid [ultimate] Yan Qian (lambeta) <qianyan.lambeta@gmail.com>
其中 XXXXXX 就是你的 keyId。接下来必须发布你的公钥:
$ gpg --keyserver hkp://pgp.mit.edu --send-keys XXXXXX
验证公钥已经发布成功:
$ gpg --keyserver hkp://pgp.mit.edu --search-keys qianyan.lambeta@gamil.com # user email address
当然,上述操作都可以使用 gpg tools 在 UI 上完成。
signing 插件
完成上述步骤之后,我们需要在 build.gradle 中添加 signing 插件及其配置,如下:
apply plugin: 'signing'
signing {
required { gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
上面的配置明确了 gradle task 的 DAG 中必须含有 uploadArchives,之后针对 archives 进行签名。
signing 插件如何同 gpg 生成 key pair 交互呢?这就需要在~/.gradle/gradle.properties
再声明三个变量,如下:
signing.keyId=XXXXXX
signing.password=your_key_pair_password
signing.secretKeyRingFile=/Users/your_name/.gnupg/secring.gpg
还剩下最后的一步,归档 Jar,sourceJar(源代码)和javadocJar(API 文档),这些是需要签名的对象。
archive
apply plugin: 'java'
task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from 'build/docs/javadoc'
}
artifacts {
archives jar
archives sourcesJar
archives javadocJar
}
归档产物最终都会被签名,生成以 .asc 为后缀的签名文件。
build/libs
├── underscore.string.java-0.0.1-javadoc.jar
├── underscore.string.java-0.0.1-javadoc.jar.asc
├── underscore.string.java-0.0.1-sources.jar
├── underscore.string.java-0.0.1-sources.jar.asc
├── underscore.string.java-0.0.1.jar
└── underscore.string.java-0.0.1.jar.asc
这些产出物在最终发布的时候,需要经过验证,如果验证失败,比如:缺少 javadoc 或者某个 *.asc 文件,则不被允许发布。
5. 上传 Release Archives
根据的 maven 的标准,日常开发我们会使用 snapshot 版本,如:0.0.1-SNAPSHOT;发布时,去掉后缀-SNAPSHOT,即:0.0.1。而 maven 会根据这个特点,机智地辨识是上传到 snapshotRepository 还是 releaseRepository 的。因为我们得发布包,所以修改版本为 0.0.1 之后,我们只需要简单地执行如下命令:
$ gradle uploadArchives # or `gradle uA` this is a shortcut.
6. 关闭并验证 Staging 环境上的 Archive
登录 Sonatype 的 Nexus Repository Manager,然后点击左边侧边栏的 Staging Repositories,搜索comlambeta
(GroupId 去掉中间的'.')。接下来查看 Content tab,重点检查 pom 或者签名文件是否遗漏!当确认无误后,即可关闭 (Close) 这个 Repo。关闭过程中,Nexus 会逐项检查产物是否合规,如果出现验证错误,则在 Activity tab 中显示具体失败的步骤及原因。
7. 发布 Archive
如果上面的验证通过,上面本来不可用的 Release 按钮会变为可用。点击 Release 按钮,直接发布包。
8. 通知 issue 管理员开启同步
发布包之后,就可以通知管理员开启同步。我在原来的 issue 的评论区留言:
I have a first release version 0.0.1 for this library.
很快地,管理员就回复同步已经开启:
Central sync is activated for com.lambeta. After you successfully release, your component will be published to Central, typically within 10 minutes, though updates to search.maven.org can take up to two hours.
不过,由于当时所用 gradle2.1 的版本,导致了上传时 pom 文件被遗漏,在 search.maven.org 中搜索不到。管理员很热心地解释了这个现象:
search.maven.org needs a valid POM file to be a part of your uploaded artifacts. Browsing Maven Central directly:
http://repo1.maven.org/maven2/com/lambeta/underscore.string.java/0.0.1/
it appears that a POM file is missing.
遂升级到 3.1 版本,重新上传之后就能在 search.maven.org 中看到。
9. 检查同步成功
除了通过 search.maven.org 检查同步是否成功之外,查询mvnrepository也是常用的搜索方式。不过,值得一提的是,mvnrepository 相较于 search.maven.org 同步会更慢点,原因是 mvnrepository 引用了 central.maven.org 仓库。而 central.maven.org == repo1.maven.org
,两个域名对应的 IP 是一样的,而这个 repo1.maven.org 就是默认的 Maven central repository,也就是 search.maven.org 的仓库。
所以,你可以在以下两个仓库看到发布包:
http://central.maven.org/maven2/com/lambeta/underscore.string.java/
http://repo1.maven.org/maven2/com/lambeta/underscore.string.java/
参考链接
[1] simple library publishing with gradle
[2] release deployment
[3] ossrh guide