发布到 Maven Central 相关的教程挺多的,但是大部分存在问题。这篇文章是我在解决了很多问题的基础之上总结的,用来帮助需要的同学避免重蹈覆彻。需要的可收藏,万一用到了呢~
1、Farewell to Bintray jCenter
首先,告别下 bintray jcenter. 相比于 Maven Central,bintray jcenter 的性能和方便性确实好得多。可惜,jcenter 将要关闭了。jcenter 还是给我提供了很多的便利,对于其关闭深表可惜:
2、发布到 Maven Central
Step 1: 注册和激活 sonatype
发布到 Maven Central 之前,首先要到 sonatype 注册一个账号,
https://issues.sonatype.org/
注册账号完账号之后需要通过创建 issue 激活账号,页面如下:
这里有几个需要注意的地方:
- 项目选择 Community Support - Open Source Project Repository Hosting
- 问题类型选择 New Project
- 概要:描述项目功能,不重要
- Group Id 比较重要,我们后续说明
- Project URL 填写自己开源项目地址即可,要与 Group Id 有一定的关联性
- SCM url 版本仓库的拉取地址,填写自己的项目的 git 链接地址,通常是项目地址后加
.git
对于这里的 group id,它就是发布完成之后,引用时的 group id. 你可以使用自己的域名,但是需要证明域名是你自己的。如果没用自己的域名,一个简单而通用的方式是使用 Github 的地址,比如我的 GitHub 地址是 https://github.com/Shouheng88 ,就可以写作 com.github.Shouheng88. 创建完 issue 之后几分钟内就会收到对方发送的邮件,邮件内会提示通过在 Github 里面创建项目来验证激活:
当我们按照邮件说明创建完项目之后,修改问题状态,过几分钟之后就激活成功了。
Step 2: 申请密钥
为了确保中央存储库中可用组件的质量水平,OSSRH 对提交的文件有明确的要求。除了 jar 包和 pom 文件,Javadoc 和 Sources 是必须的(这点和 bintray jcenter 类似),并且每个文件都要有一个对应的 asc 文件,即 GPG 签名文件,用于校验文件。所以,这步骤中我们需要申请密钥。
OSX 下面可以使用 brew 安装,
$ brew update
$ brew install -v gpg
Windows 下面也可以直接下载安装,
https://www.gpg4win.org/get-gpg4win.html
安装完毕之后,可以通过如下指令生成密钥:
gpg --generate-key
创建密钥的过程中会要求输入密码,这里的密码非常重要,我们发布的时候会使用到它。
创建完毕之后可以通过 gpg -k
显示所有已创建的密钥:
这里的字符串 AB7FxxxxxxxxxxxxABA846D44F7B66
叫做密钥指纹。后面 8 位 D44F7B66,叫做 KEY ID,我们发布的时候将使用到它。
另外,进行文件签名的时候需要用到名为 secretKeyRingFile 的文件,我们可以通过如下命令生成:
gpg --export-secret-keys [密钥指纹] > secret.gpg
在 sonatype 的仓库提交后,需要从多个公钥服务器上下载匹配的公钥,然后来校验你上传的文件的签名。所以,我们需要通过下面的命令上传公钥到公钥服务器:
gpg --keyserver keyserver.ubuntu.com --send-keys [密钥指纹]
Step 3: 准备发布脚本
首先需要引入两个插件,
apply plugin: 'maven-publish'
apply plugin: 'signing'
然后编写发布的 gralde 脚本如下
task androidSourcesJar(type: Jar) {
archiveClassifier.set("sources")
from android.sourceSets.main.java.source
exclude "**/R.class"
exclude "**/BuildConfig.class"
}
publishing {
publications {
mavenJava(MavenPublication) {
// group id,发布后引用的依赖的 group id
groupId 'com.github.Shouheng88'
// 发布后引用的依赖的 artifact id
artifactId 'sil'
// 发布的版本
version '0.1.0'
// 发布的 arr 的文件和源码文件
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
artifact androidSourcesJar
pom {
// 构件名称,可以自定义
name = 'uix-common'
// 构件描述
description = 'Android UIX'
// 构件主页
url = 'https://github.com/Shouheng88/Android-uix'
// 许可证名称和地址
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
// 开发者信息
developers {
developer {
name = 'ShouHeng'
email = '[email protected]'
}
}
// 版本控制仓库地址
scm {
url = 'https://github.com/Shouheng88/Android-uix'
connection = 'scm:git:github.com/Shouheng88/Android-uix.git'
developerConnection = 'scm:git:ssh://[email protected]/Shouheng88/Android-uix.git'
}
}
}
}
repositories {
maven {
// 发布的位置,这里根据发布的版本区分了 SNAPSHOT 和最终版本两种情况
def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
// 这里就是之前在 issues.sonatype.org 注册的账号
username ossrhUsername
password ossrhPassword
}
}
}
}
signing {
sign publishing.publications
}
注意:这里的 group id 必须和申请的 sonatype 申请账号时申请的 group id 一致。这里的要发布到的仓库的地址可能是每个用户不同的,具体是多少要看查看自己的激活邮件(可查看下面的“到 Sonatype 处理发布结果”小节)。
此外,我们需要在 gradle.properties 中定义我们的账户相关的信息:
signing.keyId=密钥keyId
signing.password=密钥password
signing.secretKeyRingFile=私钥keyRingFile路径
sonatypeUsername=sonatype账号
sonatypePassword=sonatype密码
Step 4: 发布
配置完成之后发布就比较简单了,只需要在 AS 的右边栏的 gradle 任务栏里指定的 module 下面选择 publishing 任务,然后选择 publishMavenJavaPublishingToMavenRepository
即可:
Step 5:到 Sonatype 处理发布结果
实际上每个用户发布到的分区可能是不同的,一个准确的查看的方法是,当你完成账号激活之后会收到一封邮件,这里写明了你的文件发布到的位置,
比如我的在 https://s01.oss.sonatype.org 域名下,而其他用户的可能在 https://oss.sonatype.org/ 域名下,如果我用自己注册的账号登录其他的域名就会报权限相关的错误,这里也是一个坑。
登录到自己的仓库之后点击左侧的 Staging Repositories
就可以看到我们刚刚发布的文件。
勾选我们的文件之后点击 Close,snoatype 将对我们发布的文件进行校验,校验需要一分钟左右的时间,校验完毕之后点击 Release,就最终发布了我们的文件。
Step 6: 引用我们的库
发布完成之后就可以引用了,引用依赖的方式和其他仓库完全一样,需要说明的是,需要先添加 Maven Central 的仓库的地址:
repositories {
//推荐: release成功后会直接从mavenCentral拉取aar
mavenCentral()
//或者
maven {url "https://s01.oss.sonatype.org/content/groups/public"}
//或者
maven {url "https://s01.oss.sonatype.org/content/repositories/releases"}
}
Step 7: 处理引用的问题
我发现大部分教程都是介绍到上面就结束了,但是实际上还有很重要的一个步骤需要解决。
通常我们的库并不是独立的,它可能通过各种方式引用其他的库,比如 implementation、api、compile 或者 compileOnly 等。使用 Bintray Jcenter 的时候,它可以自动根据我们项目的依赖关系生成 pom 文件中的依赖。但是发布到 Maven Central 的时候需要我们自己解决这个问题。如果不解决这个问题,引用的时候就只引用到了我们自己发布的这个 aar 文件。这显然是不行的。
我通过对比 Bintray JCenter 对各种引用方式,编写了下面的代码用来生成 pom 中的依赖关系:
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
project.configurations.all { configuration ->
def name = configuration.name
if (name != "implementation" && name != "compile" && name != "api") {
return
}
println(configuration)
configuration.dependencies.each {
println(it)
if (it.name == "unspecified") {
// 忽略无法识别的
return
}
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (name == "api" || name == "compile") {
dependencyNode.appendNode("scope", "compile")
} else { // implementation
dependencyNode.appendNode("scope", "runtime")
}
}
}
}
这里会先对引用的方式做一个判断,然后根据引用的方式选择对应的 scope,同时处理了一些异常的情况。
最终的发布脚本
task androidSourcesJar(type: Jar) {
archiveClassifier.set("sources")
from android.sourceSets.main.java.source
exclude "**/R.class"
exclude "**/BuildConfig.class"
}
publishing {
// 定义发布什么
publications {
mavenJava(MavenPublication) {
// group id,发布后引用的依赖的 group id
groupId 'com.github.Shouheng88'
// 发布后引用的依赖的 artifact id
artifactId 'sil'
// 发布的版本
version '0.1.0'
// 发布的 arr 的文件和源码文件
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
artifact androidSourcesJar
pom {
// 构件名称,可以自定义
name = 'uix-common'
// 构件描述
description = 'Android UIX'
// 构件主页
url = 'https://github.com/Shouheng88/Android-uix'
// 许可证名称和地址
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
// 开发者信息
developers {
developer {
name = 'ShouHeng'
email = '[email protected]'
}
}
// 版本控制仓库地址
scm {
url = 'https://github.com/Shouheng88/Android-uix'
connection = 'scm:git:github.com/Shouheng88/Android-uix.git'
developerConnection = 'scm:git:ssh://[email protected]/Shouheng88/Android-uix.git'
}
// 解决依赖关系
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
project.configurations.all { configuration ->
def name = configuration.name
if (name != "implementation" && name != "compile" && name != "api") {
return
}
println(configuration)
configuration.dependencies.each {
println(it)
if (it.name == "unspecified") {
// 忽略无法识别的
return
}
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (name == "api" || name == "compile") {
dependencyNode.appendNode("scope", "compile")
} else { // implementation
dependencyNode.appendNode("scope", "runtime")
}
}
}
}
}
}
}
// 定义发布到哪里
repositories {
maven {
// 发布的位置,这里根据发布的版本区分了 SNAPSHOT 和最终版本两种情况
def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
// 这里就是之前在 issues.sonatype.org 注册的账号
username ossrhUsername
password ossrhPassword
}
}
}
}
signing {
sign publishing.publications
}
总结
以上是一个简单的解决方案,经过上述操作已经可以帮助我们解决发布过程中的许多问题。当然,这里还是有可以优化的空间,比如处理项目依赖等各种情况。
希望本文对你有所帮助,如有疑问可在下方留言。