从零开始,注册sonatype,发布aar到Maven仓库MavenCentral,发布aar到本地仓库

1,929 阅读11分钟

一、注册sonatype账号

我们写的一些开源库(jar、aar等),发布到开源仓库,其他开发者通过一行代码就能使用。目前jcenter要关闭了,我们写的开源库可以发布到中央仓库MavenCenteral。发布到中央仓库MavenCentral,需要注册sonatype账号。

1、填写信息,注册sonatype账号

打开sonatype官网:issues.sonatype.org/

8240B7B4-4D4F-4879-AE02-0E278493F3C7.png

打开注册界面,填写注册信息:

E5D8BA4B-C2E2-479E-98CB-C109C3E7B44D.png

  • Email:邮箱账号,接收sonatype推送的邮件
  • Full name:账号名,按照自己喜欢随便填
  • Username:账号,登录sonatype和MavenCentral的账号
  • Password:密码

点击sign up账号就注册成功了。然后会让你设置一下账号头像,这里就不截图了。

2、新建issue工单

点击新建

image.png

填写新建信息:

image.png

image.png

可以参考图片上的示例填写:

  • 项目:Community Support - Open Source Project Repository Hosting (OSSRH)
  • 问题类型:New Project
  • 概要:随便填写,不重要
  • groupId:项目组织唯一的标识符。推荐个人用github账号,格式:io.github.xxxx,xxxx是你的GitHub账号路径,比如我的github主页是:github.com/jinxiyang, xxxx就是jinxiyang。也可以自建服务器,自定义路径。goupId不能随便乱填,后面sonatype会校验你是否拥有此域名。
  • Project URL:项目地址,可以填GitHub上的项目地址,如:github.com/jinxiyang/O…
  • SCM url:版本仓库的拉取地址,可以填GitHub上的项目地址加 .git,如:github.com/jinxiyang/O…
  • Alread Synced to Central:选NO,我们还没有打包aar,没有上传到中央仓库

点击右下角新建,就创建了一个issue工单,进入了这个issue详情页,大概10分钟左右,会收到comment,让我们验证是否拥有填写groupId对应的域名。我们的邮箱也会收到邮件。

3、验证groupId

多刷新这个issue工单详情页,大概10分钟左右,会收到comment,让我们验证是否拥有填写groupId对应的域名。我们的邮箱也会收到邮件。

填写的groupId:

  • 如果是:com.baidu.xxxx,就需要验证我们是否拥有域名:www.baidu.com ,对应的是自建服务器
  • 如果是:io.github.xxxx,需要我们在GitHub新建指定名称的临时空项目

image.png

  • 第一步,在GitHub新建指定名称的项目,如:github.com/jinxiyang/O…
  • 第二步,点击issue详情页的Respond按钮,告诉sonatype官方,可以验证groupId了

几分钟后我们会收到一条comment,邮箱也会收到邮件,告诉我们groupId验证通过,我们可以上传我们的库(jar、aar)到中央仓库MavenCentral了。

image.png

我们可以打开网址:central.sonatype.org/publish/pub… ,查看仓库地址,后面发布时会用到。

snapshot仓库上传地址:

https://s01.oss.sonatype.org/content/repositories/snapshots/

release仓库上传地址:

https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/

image.png

至此sonatype账号注册完成了。

二、生成GPG签名秘钥和文件

发布到中央仓库的文件,需要签名,我们需要安装gpg,并生成秘钥,然后导出秘钥文件。

1、安装Homebrew

安装GPG,需要先安装Homebrew。安装Homebrew,踩了不少坑,可以看我的这篇文章:

juejin.cn/post/710161…

最后发现,用国内大神的脚本,轻轻松松几分钟就装好了,传送门:

zhuanlan.zhihu.com/p/111014448

MacOS、Linux、Windows都支持安装。

2、安装GPG并生成秘钥

在terminal中输入命令,安装gpg:

brew install -v gpg

安装成功之后,使用gpg命令,生成秘钥:

gpg --generate-key

期间会要求输入:真实姓名、电子邮件地址、设置秘钥密码,按照提示一步一步操作:

image.png

秘钥的密码要记住,后面会用到。keyId是秘钥的后8位,后面也会用到。

把这个公钥(图片上打码的长串)上传到服务器,往这三个服务器上传,有一个成功就可以了:

gpg --keyserver hkp://keyserver.ubuntu.com:11371 --send-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA 

gpg --keyserver hkp://keys.openpgp.org:11371 --send-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA 

gpg --keyserver hkp://pool.sks-keyservers.net:11371 --send-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA

验证是否上传成功了,和上面命令对应:

gpg --keyserver hkp://keyserver.ubuntu.com:11371 --recv-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA 

gpg --keyserver hkp://keys.openpgp.org:11371 --recv-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA 

gpg --keyserver hkp://pool.sks-keyservers.net:11371 --recv-keys 24B6CXXXXXXXXXXXXXXXXXXXXXXXXXX00BA0DDDA

导出签名文件,后面发布aar用到。命令:

gpg --export-secret-keys -o my.gpg

在你的电脑的根目录下,会生成一个文件my.gpg,我的文件的路径:

/Users/jinxiyang/my.gpg

三、编写发布aar到maven脚本

1、配置gradle.properties文件

image.png


#maveCentral账号密码
sonatypeUsername=yangjinxi
sonatypePassword=xxxxxxxx

#秘钥的后8位
signing.keyId=0BA0DDDA
signing.password=xxxxxxxx
signing.secretKeyRingFile=/Users/jinxiyang/my.gpg

这些配置,建议写在local.properties文件,发布aar时,从local.properties复制到gradle.properties,避免代码上传到GitHub泄漏密码。平时gradle.properties里的都是空值。

2、编写发布aar脚本文件

工程下新建文件uploadMavenCentral.gradle

以下是uploadMavenCentral.gradle内容和相关注释:

//引入maven-publish和签名插件
apply plugin: 'maven-publish'
apply plugin: 'signing'



//发布aar的版本号,有-SNAPSHOT代表发布snapshot,没有则发release
def libVersion='0.1.3'
//def libVersion='0.1.3-SNAPSHOT'

task androidSourcesJar(type: Jar) {
    archiveClassifier.set("sources")
    // 源码路径,包含Java、kotlin代码
    from android.sourceSets.main.java.getSrcDirs()
    exclude "**/R.class"
    exclude "**/BuildConfig.class"
}

publishing {
    publications {
        //这里的maven是闭包名,可以随便定义的,也可以叫aar、release等等
        maven(MavenPublication) {
            // group id,发布后引用的依赖的 group id
            groupId 'io.github.jinxiyang'
            // 发布后引用的依赖的 artifact id
            artifactId 'overscrolllayout'
            // 发布的版本
            version libVersion
            // 发布的aar,依赖bundleReleaseAar task,module要先构建出aar
            afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }
            //发布的源码文件,从上面定义的task androidSourcesJar获取
            artifact androidSourcesJar
            pom {
                // 构件名称,可以自定义
                name = 'OverScrollLayout'
                // 构件描述
                description = 'Android自定义控件,支持RecyclerView、NestedScrollView过度滚动并回弹'
                // 构件主页
                url = 'https://github.com/jinxiyang/OverScrollLayout'
                // 许可证名称和地址
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                // 开发者信息
                developers {
                    developer {
                        name = 'yangjinxi'
                        email = '1137501600@qq.com'
                    }
                }
                // 版本控制仓库地址
                scm {
                    url = 'https://github.com/jinxiyang/OverScrollLayout'
                    connection = 'scm:git:github.com/jinxiyang/OverScrollLayout.git'
                    developerConnection = 'scm:git:ssh://git@github.com/jinxiyang/OverScrollLayout.git'
                }
            }

            // pom文件中声明依赖,从而传递到使用方
            pom.withXml {
                def dependenciesNode = asNode().appendNode('dependencies')
                configurations.implementation.allDependencies.each {
                    // 避免出现空节点或 artifactId=unspecified 的节点
                    if (it.group != null && (it.name != null && "unspecified" != it.name) && it.version != null) {
                        println "dependency=${it.toString()}"
                        def dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', it.group)
                        dependencyNode.appendNode('artifactId', it.name)
                        dependencyNode.appendNode('version', it.version)
                        dependencyNode.appendNode('scope', 'implementation')
                    }
                }
            }
        }
    }
    repositories {
        maven {
            // 发布的仓库地址,这里根据发布的版本区分了release和snapshot版本两种情况
            def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
            def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
            url = libVersion.endsWith('-SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl

            credentials {
                // 这里就是之前在 issues.sonatype.org 注册的账号
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

//签名
signing {
    sign publishing.publications
}

3、库module引入发布脚本

在我们的库module引入发布脚本 uploadMavenCentral.gradle

apply from: '../uploadMavenCentral.gradle'

image.png

四、发布aar到仓库

1、点击task,发布aar

配置后发布脚本后,同步项目,在Android Studio的右边栏,点击Gradle,如下图:

image.png

  • 我们可以点击红框中的 publish 或者 publishMavenPublicationToMavenResposity,发布aar到中央仓库 MavenCentral

  • 我们可以点击红框中的 publishToMavenLocal 或者 publishMavenPublicationToMavenLocal,发布aar到本地仓库

本地仓库的地址,格式:~/.m2/repository,比如我的:

/Users/jinxiyang/.m2/repository

publishMavenPublicationToMavenRespositypublishMavenPublicationToMavenLocal 这个task名称中的第一maven,是我们定义的闭包名,如果我们定义的闭包名是:aar,则:

image.png

2、terminal执行命令,发布aar

在工程的terminal下,执行命令:

./gradlew :<moduleName>:<taskName>

如下:

//发布到中央仓库mavenCentral
./gradlew :overscrolllayout:publish

//发布aar到本地仓库
./gradlew :overscrolllayout:publishToMavenLocal

如果遇到terminal报错:

image.png

这是因为terminal在执行命令时,构建工具gradle用到了Java,调用了系统的Java,系统的Java版本是Java8,但我们需要用Java11。

gradle.properties文件配置变量org.gradle.java.home为Java11的路径就可以了。

org.gradle.java.home=/Applications/Android Studio.app/Contents/jre/Contents/Home

具体的地址,可以看Android Studio的Preferences

image.png

五、查看和使用中央仓库MavenCentral的aar

aar已经发布出去了,到底有没有成功?如何查看呢?

打开网址:s01.oss.sonatype.org/ ,输入账号名与密码,就是我们前面注册的sonatype账号。

image.png

在上面第三步,编写发布aar脚本中,我们脚本配置的仓库地址是:


repositories {
    maven {
        // 发布的仓库地址,这里根据发布的版本区分了release和snapshot版本两种情况
        def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
        def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
        
        
        ...
    }
}


release和snapshot仓库地址,有很大不同的,其实release仓库地址可以下面这样配置,这样两个仓库地址就相似了:


repositories {
    maven {
        // 发布的仓库地址,这里根据发布的版本区分了release和snapshot版本两种情况
        def releasesRepoUrl = "https://s01.oss.sonatype.org/content/repositories/releases"
        def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
        
        
        ...
    }
}


具体的区别后面详细介绍。

1、暂存库Staging Reposities

我们发布release aar时,如果用的是这个地址:

def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"

aar将会被发布到暂存库Staging Reposities,如下图,可以看到我们发布的aar:

image.png

暂存库Staging Reposities,从名称上就能看出来,Staging:正在登台,暂存的意思。

那暂存库里aar如何登台呢?也就是如何发布到中央仓库呢?

根据官方提供的相关文档:# Managing Staging Repositories,以下步骤:

  1. 选中我们发布的暂存仓库,io.github.jinxiyang-1001, 随着以后发布次数越多,后面四位数字越来越大
  2. 点击close
  3. 点击release

如下:

image.png

close的弹窗,点击confirm

截屏2022-05-27 上午9.17.11.png

close动作会执行一系列校验,没问题后,release按钮就亮起来了

截屏2022-05-27 上午9.18.37.png

点击release按钮,默认勾上drop,表示发布成功之后,自动删除暂存库,如图:

截屏2022-05-27 上午9.36.56.png

自动drop后,暂存库就没有了,我们的aar就被发布到了Repositories的Releases。

image.png

image.png

讲了这么多,都是release aar的发布,而snapshot aar则直接被Repositories的Snapshots。

反应快的小伙伴,已经知道原因了,是因为我们发布脚本填的仓库地址:


repositories {
    maven {
         // release 先发布到 Staging Repositories 暂存库
         def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
    
    
        // release 直接发布到 Repositories的Releases,个人开发者可以直接用这个仓库地址
        def releasesRepoUrl = "https://s01.oss.sonatype.org/content/repositories/releases"
        
        
        // snapshot 直接发布到 Repositories的Snapshots
        def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
        
        
        ...
    }
}


为什么要弄个暂存库Staging Repositories?这么麻烦

当项目很大时,不只一个人开发,开源库也是,最后真正发布之前,需要多次修改和层层测试,有问题打回来修改,然后再提交一个版本,再测试,再打回,……,最后没问题了,aar从暂存库发布到release,到了中央仓库MavenCentral。个人开发者可以直接发布到release仓库地址,不用暂存库。

image.png

官方提供的暂存库管理文档:# Managing Staging Repositories

2、仓库Repositories

在仓库Repositories中,查看发布的release/snapshot aar,可以实时看到:

image.png

  • Releases:release仓库,仓库地址:https://s01.oss.sonatype.org/content/repositories/releases

  • Snapshots:snapshot仓库库,仓库地址:https://s01.oss.sonatype.org/content/repositories/snapshots/

如果我们是第一次发布库到maven,这时在我们issue的工单详情页,会收到sonatype官方一条comment:

image.png

官方告诉我们,这个io.github.jinxiyang已经激活了,过4个小时就能搜索到我们库了,就能引用库。

据我实践,可能不只4个小时,7个小时以上也可能,真苦逼!

如果等了很久,在MavenCentral上就是出不来,可以在sonatype上提issue工单,请求官方支持。文章第六部分,有提工单请求帮助的示例,可以参考。

3、官方方式使用开源库

经历了千难万苦,等待了好长时间(7小时以上),库终于可以用了。

在项目build.gradle,依赖mavenCentral,Android默认有了


buildscript {
    repositories {
        mavenCentral()
        ....
    }
    ....
}

实际只需要一行代码,在主工程中添加依赖库:

implementation 'io.github.jinxiyang:overscrolllayout:0.1.1'

4、立即使用开源库

有没有办法,发布之后提前使用了,答案是当然有。

在项目根目录下的build.gradle,配置maven地址为上面Releases的地址,snapshot配置类似:

buildscript {

    repositories {
        mavenCentral()
        google()
        maven {
            //release仓库地址
            url 'https://s01.oss.sonatype.org/content/repositories/releases'
            
            //snapshot仓库地址
            //url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.1'
    }
}


//注意allprojects有的项目是在setting.gralde中配置的
allprojects {
    repositories {
        mavenCentral()
        google()
        maven {
            //release仓库地址
            url 'https://s01.oss.sonatype.org/content/repositories/releases'
            
            //snapshot仓库地址
//          url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

5、本地仓库mavenLocal的使用

在实际生成中,我们常常发布到本地maven仓库,来开发测试,简单又方便,没问题在发布到mavenCentral。怎么使用本地仓库呢?

在第四章节:发布aar到maven,提到发布aar到本地, publishToMavenLocal 或者 publishMavenPublicationToMavenLocal,发布aar到本地仓库。

image.png

build.gradle加入本地仓库:mavenLoca(),如下:

buildscript {

    repositories {
        //本地maven仓库,本地仓库的地址,`~/.m2/repository`
        mavenLoca()
        
        ...
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.0.1'
    }
}


//注意allprojects有的项目是在setting.gralde中配置的
allprojects {
    repositories {
       mavenLoca()
       
       
       ...
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

6、在sonatype查看我们的库

千难万险已经过去了,如何高大又时尚的分享我们的开源库呢?

打开网址:search.maven.org/ ,查找我们的库:

image.png

六、提issue工单,请求官方帮助

如果按照本文一步一步操作,还是无法以mavenCentral()同步下来aar,可以在sonatype提Publish Support工单,请求帮助查找原因。

如:

截屏2022-05-28 上午12.33.08.png

sonatype帮我同步了一下,问题解决了:

截屏2022-05-28 上午12.33.22.png

七、本文示例代码GitHub

Android自定义控件,支持RecyclerView、NestedScrollView过度滚动并回弹

github.com/jinxiyang/O…

八、致谢

发布aar到mavenCentral,从零开始,踩了不少坑,参考了不少文章,在这里特别感谢!如有错误请大家多多留言!

# 建议收藏,从 jCenter 迁移到 MavenCentral 完整方案

# Android:发布aar包到maven仓库以及 maven插件 和 maven-publish 插件的区别

# SDK发布到maven Central,实现远程依赖

# Homebrew国内如何自动安装(国内地址)(Mac & Linux)