乐趣区

关于android:来开源吧发布开源组件到-MavenCentral-仓库超详细攻略

请点赞关注,你的反对对我意义重大。

🔥 Hi,我是小彭。本文已收录到 GitHub · AndroidFamily 中。这里有 Android 进阶成长常识体系,有气味相投的敌人,关注公众号 [彭旭锐] 带你建设外围竞争力。

小彭明天和群友探讨了一下学习办法的问题,感觉还挺感同身受的。有时候咱们遇到不懂的中央,潜意识会产生讨厌和恐怖,大脑会驱使咱们去学习和查看这个不懂的中央,后果有可能是陷入到另一个不懂的循环里,遗记了最后的目标。对于系统化学习和碎片化学习,你的想法是怎么的呢?评论区里通知我吧。

前言

当一个开发者的程度晋升到肯定水平时,会有由外向外输入价值的需要,包含公布开源我的项目。而要公布开源组件,则须要将组件公布到公开的近程仓库,如 Jitpack、JenCenter 和 MavenCentral。其中,MavenCentral 是最风行的地方仓库,也是 Gradle 默认应用的仓库之一。

在这篇文章里,我将手把手带你公布组件到 MavenCentral 地方仓库。本文的示例程序应用小彭的开源我的项目 ModularEventBus 有用请给 Star,谢谢。

这不仅仅是一份攻略,还带着踩过一个个坑留下的泪和挠掉一根根落的贵重发丝~~~


开发者系列文章:

  • MavenCentral 仓库(本文)
  • 还在见招拆招?先看懂 APP 个人信息爱护治理机制
  • 应用 Markdown 高效率编写文档
  • 技术周报 | 2021 年第 13 周
  • 技术周报 | 2021 年第 11 周
  • 技术周报 | 2021 年第 10 周

1. 概念梳理

1.1 什么是 POM?

POM(Project Object Model)指我的项目对象模型,用于形容我的项目构件的根本信息。一个无效的 POM 节点中次要蕴含以下参数:

参数 形容 举例
groupId 组织 / 公司名 io.github.pengxurui
artifactId 组件名 modular-eventbus-annotation
version 组件版本 1.0.0
packaging 格局 jar

1.2 什么是仓库(repository)

在我的项目中,咱们会须要依赖各种各样的二方库或三方库,这些依赖肯定会寄存在某个地位(Place),这个“地位”就叫做仓库。应用仓库能够帮忙咱们治理我的项目构件,例如 jar、aar 等等。

支流的构建工具都有 2 个档次的仓库概念:

  • 1、本地仓库: 无论应用 Linux 还是 Window,计算机中会有一个目录用来寄存从地方仓库或近程仓库下载的依赖文件;
  • 2、近程仓库: 包含地方仓库和公有仓库。地方仓库是开源社区提供的仓库,是绝大多数开源库的寄存地位。比方 Maven 社区的地方仓库 Maven Central;公有仓库是公司或组织的自定义仓库,能够了解为二方库的寄存地位。

1.3 Sonatype、Nexus 和 Maven 的关系:

  • Sonatype: 残缺名称是 Sonatype OSSRH(OSS Repository Hosting),为开源我的项目提供收费的地方存储仓库服务。其中须要用到 Nexus 作为仓库管理器;
  • Nexus: 残缺名称是 Sonatype Nexus Repository Manager,是 Sonatype 的另一款产品,用作提供仓库管理器。Sonatype 基于 Nexus 提供地方仓库,各个公司也能够应用 Nexus 搭建公有仓库;
  • Maven: 残缺名称是 Apache Maven,是一种构建零碎。除了 Maven 之外,Apache Ant 和 Gradle 都能够公布组件。

2. 新建 Sonatype 我的项目

从这一节开始,我将带你一步步实现公布组件到地方仓库的操作(带你踩坑)。

2.1 筹备 Sonatype JIRA 账号

进入 Sonatype 仪表盘界面,登录或注册新账号:https://issues.sonatype.org:

2.2 新建工单

点击仪表盘面板右上角的 ”新建“ 按钮,依照以下步骤向 Sonotype 提交新建我的项目的工单:

填写办法总结如下:

  • 我的项目: 应用默认选项 Community Support – Open Source Project Repository Hosting (OSSRH);
  • 问题类型: 应用默认选项 New Project;
  • 概要: 填写 Github 仓库雷同的名称,以不便查找;
  • GroupId 组织名: 填写公布组件时应用的 groupId,后续步骤中会查看你是否实在领有该 groupId,所以不能够轻易填写,有 2 种填写形式:

    • 应用 Github 账号: 依照 io.github.[Github 用户名] 的格局填写,后续步骤中 Sonatype 通过要求咱们在集体 Github 仓库中新建指定名称的长期代码库的形式来做身份验证;
    • 应用集体域名: 依照逆序域名的格局填写,例如集体域名为 oss.sonotype.org,则填写 org.sonotype.oss
  • Project URL 我的项目地址: 填写 Github 我的项目地址,例如:https://github.com/pengxurui/ModularEventBus
  • SCM url 版本控制地址: 在 Github 我的项目地址后加 .git,例如 https://github.com/pengxurui/ModularEventBus.git

2.3 验证 GroupId 所有权

点击弹出的音讯进入工单详情页,刚新建的工单要期待 Sonotype 机器人回复,期待大略十几分钟后,在工单底部的评论区会通知咱们怎么操作。

至此,Sonotype 我的项目筹备结束。


3. 新建 GPG 密钥对

GPG(GNU Privacy Guard) 是基于 OpenPGP 规范实现的加密软件,它提供了对文件的非对称加密和签名验证性能。所有公布到 Maven 仓库的文件都须要进行 GPG 签名,以验证文件的合法性。

3.1 装置 GPG 软件

装置 GPG 软件有两种形式:

  • 形式 1 – 下载安装包: 通过 GPG 官网 下载安装包,这个我没试过;
  • 形式 2 – 通过 Homebrew 装置: 应用 Homebrew 执行以下命令:

命令行

# 通过 Homebrew 装置 gpg
brew install gpg

如果本地没有 Homebrew 环境则须要先装置,这里也容易踩坑。小彭本地本来就有 Homebrew 环境,然而装置 gpg 的过程中各种报错,最初还是用了最暴力的解法才解决 —— 卸载重装 Homebrew:(

参考资料: MacOS 下开发环境配置 –homebrew 的装置

3.2 生成 GPG 密钥对

应用 --generate-key 参数,依照指引填写相干信息和 passphrase 私钥口令。另外,应用 --list-keys 参数能够查看以后零碎中生成过的密钥。

命令行

# 密钥生成命令
gpg --generate-key

# 密钥查看命令
gpg --list-keys

命令行演示

GPG 在生成密钥对时,会要求开发者做一些随机的行动,以给随机数退出足够多的扰动,稍等片刻就会生成实现了。实现后能够随时应用 —list-keys 参数查看密钥对信息:

命令行演示

解释一下其中的信息:

/Users/pengxurui/.gnupg/pubring.kbx
-----------------------------------
pub   ed25519 2022-08-23 [SC] [expires: 2024-08-22]
      D8BCD08568BE5D2D634DD99EFD4ECE3B54DE73AA
uid           [ultimate] test <test@gmail.com>
sub   cv25519 2022-08-23 [E] [expires: 2024-08-22]

# pubring.kbx:本地存储公钥的文件
# 2022-08-23 [SC] [expires: 2024-08-22]:示意密钥对的创立工夫和生效工夫
# test <test@gmail.com>:用户名和邮箱
# ed25519:示意生成公钥的算法
# cv25519:示意生成私钥的算法
# D8BCD08568BE5D2D634DD99EFD4ECE3B54DE73AA:密钥指纹 / KeyId

至此,你曾经在本地生成一串新的密钥对,当初你手上有:

  • 密钥指纹 / KeyId: 密钥指纹是密钥对的惟一标识,即下面 D8BCD08568BE5D2D634DD99EFD4ECE3B54DE73AA 这一串。有时也能够应用较短的格局,取其最初 8 个字符,即 B54DE73AA 这一串;
  • 公钥: 该密钥指纹对应的公钥;
  • 私钥: 该密钥指纹对应的私钥;
  • passphrase 密钥口令: 生成密钥对时输出的口令,私钥与密钥口令独特组成密钥对的公有信息。

3.3 删除密钥对

有时候须要删除密钥对,能够应用以下命令:

# 先删除私钥后,能力删除公钥

# 删除私钥
gpg --delete-secret-keys  [密钥指纹]
# 删除公钥
gpg --delete-keys  [密钥指纹]

3.4 上传公钥

密钥对中的公钥信息须要公开,其他人能力拿到公钥来验证你签名的数据,公开的办法就是上传到公钥服务器。公钥服务器是专门贮存用户公钥的服务器,并且会用替换机制将数据同步给其它公钥服务器,因而你只有上传到其中一个服务器即可。我最初是上传到 hkp://keyserver.ubuntu.com 服务器的。以下服务器都能够尝试:

  • pool.sks-keyservers.net
  • keys.openpgp.org
  • keyserver.ubuntu.com
  • pgp.mit.edu

命令行

// 上传公钥
gpg --keyserver【服务器地址】:11371 --send-keys【密钥指纹】// 验证公钥
gpg --keyserver【服务器地址】:11371 --recv-keys【密钥指纹】

3.5 导出密钥文件

后文公布组件的时候须要用到密钥口令和私钥文件,能够应用以下参数导出

命令行

# 默认导出到本地目录 /User/[用户名]/

# 导出公钥
gpg --export【密钥指纹】> xiaopeng_pub.gpg
# 导出私钥
gpg --export-secret-keys【密钥指纹】> xiaopeng_pri.gpg

3.6 踩坑:PGPException: unknown public key algorithm encountered

我在公布组件时遇到 PGPException: unknown public key algorithm encountered 报错,最初排查下来是应用了 Gradle signing 插件不反对 EDDSA 算法,须要应用 RSA 算法。

能够看到上文 3.1 节生成的公钥,能够看到是 ed 结尾的,示意应用的是 EDDSA 算法,应该是不同版本中的 --generate-key 参数应用的默认算法不一样。

3.1 节生成的公钥信息

pub   ed25519 2022-08-23 [SC] [expires: 2024-08-22]

解决办法是应用 --full-generate-key 参数抉择应用 RSA 算法生成密钥对:

命令行演示

至此,密钥对筹备结束。


4. 配置公布脚本

实现 Sonatype 我的项目和密钥对的筹备工作后,当初着手配置我的项目的 Gradle 脚本了。Gradle 提供了两个 Maven 插件:

  • maven 插件: 旧版公布插件,从 Gradle 7.0 开始无奈应用;
  • maven-publish 插件: 新版公布插件。

我最后的想法是别离整顿出这两个插件的通用脚本,一开始是参考 ARouter 我的项目里的 publish.gradle 脚本,过程中也遇到各种问题,例如 Javadoc generation failed,可能是因为 ARouter 是纯 Java 实现的,所以裸露的问题较少。耽误了一周后,刚好这两天在看 LeakCanary 源码,果然在 LeakCanary 里发现宝藏 —— vanniktech 的公布插件!

报错

Execution failed for task ':eventbus_api:androidJavadocs'.
> Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting): '/Users/pengxurui/workspace/public/ModularEventBus/eventbus_api/build/tmp/androidJavadocs/javadoc.options'

4.1 应用 maven 插件公布

这块脚本是参考 ARouter 我的项目中 publish.gradle 脚本的,我在此基础上减少了正文和大量改变,如果遇到生成 Javadoc 呈现问题,能够把 archives androidJavadocsJar 这一行正文掉。

maven_sonatype.gradle

// 在 ARouter 我的项目的 publish.gradle 上批改

apply plugin: 'maven'
apply plugin: 'signing'

version = VERSION_NAME
group = GROUP

// 是否 Release 公布(依据是否蕴含 SNAPSHOT 判断)def isReleaseBuild() {return VERSION_NAME.contains("SNAPSHOT") == false
}

// Central Repository: https://central.sonatype.org/publish/publish-guide/
// Release 仓库地址(默认先公布到 staging 暂存库,须要手动公布到地方仓库)def getReleaseRepositoryUrl() {return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
}

// Snapshot 仓库地址
def getSnapshotRepositoryUrl() {return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : "https://s01.oss.sonatype.org/content/repositories/snapshots/"
}

// 仓库账号
def getRepositoryUsername() {return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : ""
}

// 仓库明码
def getRepositoryPassword() {return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : ""
}

// 组件配置
def configurePom(pom) {
    // 组织名
    pom.groupId = GROUP
    // 组件名
    pom.artifactId = POM_ARTIFACT_ID
    // 组件版本
    pom.version = VERSION_NAME

    pom.project {
        // 名称
        name POM_NAME
        // 公布格局
        packaging POM_PACKAGING
        // 形容信息
        description POM_DESCRIPTION
        // 主页
        url POM_URL

        scm {
            url POM_SCM_URL
            connection POM_SCM_CONNECTION
            developerConnection POM_SCM_DEV_CONNECTION
        }

        // Licenses 信息
        licenses {
            license {
                name POM_LICENCE_NAME
                url POM_LICENCE_URL
                distribution POM_LICENCE_DIST
            }
        }

        // 开发者信息
        developers {
            developer {
                id POM_DEVELOPER_ID
                name POM_DEVELOPER_NAME
            }
        }
    }
}

afterEvaluate { project ->
    // 配置 Maven 插件的 uploadArchives 工作
    uploadArchives {
        repositories {
            mavenDeployer {
                // 配置公布前须要签名
                beforeDeployment {MavenDeployment deployment -> signing.signPom(deployment) }
                // 配置 Release 仓库地址与账号密码
                repository(url: getReleaseRepositoryUrl()) {authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
                }
                // 配置 Snapshot 仓库地址与账号密码
                snapshotRepository(url: getSnapshotRepositoryUrl()) {authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
                }
                // 配置 POM 信息
                configurePom(pom)
            }
        }
    }
    // 配置 Maven 本地公布工作
    tasks.create("installLocally", Upload) {
        configuration = configurations.archives

        repositories {
            mavenDeployer {
                // 本地仓库地址
                repository(url: "file://${rootProject.buildDir}/localMaven")
                // 配置 POM 信息
                configurePom(pom)
            }
        }
    }

    // 配置签名参数,局部须要在 local.properties 中配置
    signing {required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
        sign configurations.archives
    }

    if (project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library')) {
        // Android 类型组件
        task install(type: Upload, dependsOn: assemble) { // 依赖于 AGP assemble 工作
            repositories.mavenInstaller {
                configuration = configurations.archives

                configurePom(pom)
            }
        }

        task androidJavadocs(type: Javadoc) {
            source = android.sourceSets.main.java.source
            classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
        }

        task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
            classifier = 'javadoc'
            from androidJavadocs.destinationDir
        }

        // 生成源码产物
        task androidSourcesJar(type: Jar) {
            classifier = 'sources'
            from android.sourceSets.main.java.source
        }
    } else {
        // 纯 Java / Kotlin 类型组件(如 Gradle 插件、APT 组件)install {
            repositories.mavenInstaller {configurePom(pom)
            }
        }

        // 生成源码产物
        task sourcesJar(type: Jar, dependsOn: classes) {
            classifier = 'sources'
            from sourceSets.main.allSource
        }

        // 生成 javadoc 产物
        task javadocJar(type: Jar, dependsOn: javadoc) {
            classifier = 'javadoc'
            from javadoc.destinationDir
        }
    }

    // Java8 适配
    if (JavaVersion.current().isJava8Compatible()) {
        allprojects {tasks.withType(Javadoc) {options.addStringOption('Xdoclint:none', '-quiet')
            }
        }
    }

    // 配置源码和 Javadoc 公布产物
    if (!isReleaseBuild()) {
        // 快照版本跳过,提高效率
        artifacts {if (project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library')) {
                // Android 类型组件
                archives androidSourcesJar // 源码
                archives androidJavadocsJar // Javadoc,如果报错须要把这一行正文掉
            } else {
                // 纯 Java / Kotlin 类型组件(如 Gradle 插件、APT 组件)archives sourcesJar // 源码
                archives javadocJar // Javadoc
            }
        }
    }
}

在须要公布的组件里利用这个脚本后,在 gradle.properties 里配置相干参数后就能够公布了。具体能够参考示例程序 ModularEventBus 中被正文掉的参数,也能够参考 ARouter 我的项目,这里就不开展了,倡议用 4.2 节 vanniktech 的公布插件。

我的项目级 gradle.properties

######################################################################
# for maven_sonatype.gradle
######################################################################
# GROUP=io.github.pengxurui
#
# POM_URL=https://github.com/pengxurui/ModularEventBus/
# POM_SCM_URL=https://github.com/pengxurui/ModularEventBus/
# POM_SCM_CONNECTION=scm:git:git:github.com/pengxurui/ModularEventBus.git
# POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/pengxurui/ModularEventBus.git
#
# POM_LICENCE_NAME=The Apache Software License, Version 2.0
# POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
# POM_LICENCE_DIST=repo
#
# POM_DEVELOPER_ID=pengxurui
# POM_DEVELOPER_NAME=Peng Xurui
#
# SONATYPE_NEXUS_USERNAME=[provide your Sonatype user name]
# SONATYPE_NEXUS_PASSWORD=[provide your Sonatype password]
#
# signing.keyId=[provide you gpg key]
# signing.password=[provide you gpg passphrase]
# signing.secretKeyRingFile=[provide you gpg secret file]

模块级 gradle.properties

######################################################################
# for maven_sonatype.gradle
######################################################################
# POM_NAME=ModularEventBus Annotations
# POM_ARTIFACT_ID=modular-eventbus-annotation
# POM_PACKAGING=jar
# POM_DESCRIPTION=The annotation used in ModularEventBus api
# VERSION_NAME=1.0.0

4.2 应用 vanniktech 的公布插件(举荐)

gradle-maven-publish-plugin 是一个外国大佬 vanniktech 开源的 Gradle 插件,须要应用 Gradle 7.2.0 以上的 Gradle 环境。它会创立一个 publish Task,反对将 Java、Kotlin 或 Android 组件公布到任何 Maven 仓库,同时也反对公布携带 Java / Kotlin 代码的 Javadoc 产物和 Sources 产物。尽管目前(2022/08/24)这个我的项目的最新版本只是 0.21.0,不过既然曾经在 LeakCanary 上验证过,大胆用起来吧。

以下为配置步骤:在我的项目级 build.gradle 中增加插件地址,在模块级 build.gradle 中利用插件:

我的项目级 build.gradle

buildscript {
    repositories {mavenCentral()
    }
    dependencies {
        // vanniktech 公布插件
        classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
        // Kotlin Javadoc,非必须
        classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.20"
        // 最新版 1.7.10 和 0.21.0 组合有问题,应该是没兼容好。下面两个版本组合我验证过是能够的。}
}

模块级 build.gradle

apply plugin: "com.vanniktech.maven.publish"
// Kotlin Javadoc,非必须。如果有这个插件,公布时会生成 Javadoc,会缩短公布工夫。倡议在 snapshot 阶段敞开
apply plugin: "org.jetbrains.dokka"

Sync 我的项目后,插件会为模块减少两个 Task 工作:

  • publish: 公布到近程 Maven 仓库,默认是 Sonatype 地方仓库;
  • publishToMavenLocal: 公布到以后机器的本地 Maven 仓库,即 ~/.m2/repository

Gradle 面板

4.3 配置 vanniktech 插件的公布参数

别离在我的项目级 gradle.properties 和模块级 gradle.properties 中配置以下参数:

我的项目级 gradle.properties

######################################################################
# for vanniktech
######################################################################
# 服务器地址
SONATYPE_HOST=S01

# 公布 release 组件时是否签名
RELEASE_SIGNING_ENABLED=true

# 组织名
GROUP=io.github.pengxurui

# 主页
POM_URL=https://github.com/pengxurui/ModularEventBus/

# 版本控制信息
POM_SCM_URL=https://github.com/pengxurui/ModularEventBus/
POM_SCM_CONNECTION=scm:git:git:github.com/pengxurui/ModularEventBus.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/pengxurui/ModularEventBus.git

# Licenses 信息
POM_LICENSE_NAME=The Apache Software License, Version 2.0
POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENSE_DIST=repo

# 开发者信息
POM_DEVELOPER_ID=pengxurui
POM_DEVELOPER_NAME=Peng Xurui
POM_DEVELOPER_URL=https://github.com/pengxurui/

mavenCentralUsername=[填 Sonatype 账号名]
mavenCentralPassword=[填 Sonatype 明码]

signing.keyId=[密钥指纹,取后 8 位即可]
signing.password=[passphrase 密钥口令]
signing.secretKeyRingFile=[导出的私钥文件门路,如 /Users/pengxurui/xxx.gpg]

模块级 gradle.properties

POM_NAME=ModularEventBus Annotations
POM_ARTIFACT_ID=modular-eventbus-annotation
POM_PACKAGING=jar
POM_DESCRIPTION=The annotation used in ModularEventBus api
VERSION_NAME=1.0.0

特地留神:公有信息不要提交到 git 版本治理中,能够写在 local.properties 中,等到要公布组件时再复制到 gradle.properties 中。而私钥文件也不要保留在以后工程的目录里,能够对立放到工程外的一个目录。

至此,所有筹备工作实现。

4.4 浅尝一下 vanniktech 插件的源码

毕竟公布逻辑都被人家封装在插件里了,有必要晓得它背地的工作,浅尝一下。

  • 反对的 Snoatype 服务器:

SonatypeHost.kt

enum class SonatypeHost(internal val rootUrl: String) {DEFAULT("https://oss.sonatype.org"),
    S01("https://s01.oss.sonatype.org"),
}
  • 反对 Dokka 插件,须要手动依赖:

MavenPublishPlugin.kt

private fun Project.defaultJavaDocOption(): JavadocJar? {return if (plugins.hasPlugin("org.jetbrains.dokka") || plugins.hasPlugin("org.jetbrains.dokka-android")) {JavadocJar.Dokka(findDokkaTask())
    } else {null}
}
  • 反对多种模块类型:

MavenPublishPlugin.kt

afterEvaluate {
    when {plugins.hasPlugin("org.jetbrains.kotlin.multiplatform") -> {} // Handled above.
        plugins.hasPlugin("com.android.library") -> {} // Handled above.
        plugins.hasPlugin("java-gradle-plugin") ->
            baseExtension.configure(GradlePlugin(defaultJavaDocOption() ?: javadoc()))
        plugins.hasPlugin("org.jetbrains.kotlin.jvm") ->
            baseExtension.configure(KotlinJvm(defaultJavaDocOption() ?: javadoc()))
        plugins.hasPlugin("org.jetbrains.kotlin.js") ->
            baseExtension.configure(KotlinJs(defaultJavaDocOption() ?: JavadocJar.Empty()))
        plugins.hasPlugin("java-library") ->
            baseExtension.configure(JavaLibrary(defaultJavaDocOption() ?: javadoc()))
        plugins.hasPlugin("java") ->
            baseExtension.configure(JavaLibrary(defaultJavaDocOption() ?: javadoc()))
        else -> logger.warn("No compatible plugin found in project $name for publishing")
    }
}

5. 公布组件到 MavenCentral 仓库

终于终于,所有筹备和配置工作都实现了!在公布之前,有必要先解释下 Sonatype 中用到的仓库地址:

5.1 仓库地址

如果你没有自定义公布的 Maven 仓库,vanniktech 插件默认会公布到 Sonatype 治理的地方仓库中。因为历史起因,Sonatype 地方仓库有 2 个域名:

  • https://s01.oss.sonatype.org/
  • https://oss.sonatype.org/

依照 官网的说法,oss.sonatype.org 是过期的,从 2021 年 2 月开始启用 s01.oss.sonatype.org/

截图

官网也会提醒目前最新的仓库地址:

截图

5.2 Staging 暂存库

仔细的敌人会发现官网提供的 snapshot 仓库和 release 仓库的格局不一样,为什么呢?—— 这是因为公布 release 组件是敏感操作,一旦组件公布 release 版本到地方仓库,就永远无奈批改或删除这个版本的组件内容(这个规定是出于稳定性和可靠性思考,如果能够批改,那些本地曾经下载过组件的用户就得不到最新内容了)。所以 Sonatype 对公布 snapshot 组件和 release 组件采取了不同策略:

  • snapshot 组件: 间接公布到 snapshot 地方仓库;
  • release 组件: 应用 Staging 暂存策略,release 组件须要先公布到暂存库,通过测试验证通过后,再由开发者手动晋升到 release 地方仓库。
 地方 release 仓库:"https://s01.oss.sonatype.org/content/repositories/releases"
地方 snapshot 仓库:"https://s01.oss.sonatype.org/content/repositories/snapshots"
暂存库:"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2"

vanniktech 插件默认也是依照 Sonatype 的策略走的,浅看一下源码:

MavenPublishBaseExtension.kt

// 暂存库:if (stagingRepositoryId != null) {repo.setUrl("${host.rootUrl}/service/local/staging/deployByRepositoryId/$stagingRepositoryId/")
} else {repo.setUrl("${host.rootUrl}/service/local/staging/deploy/maven2/")
}

// snapshot 库:if (it.version.toString().endsWith("SNAPSHOT")) {if (stagingRepositoryId != null) {throw IllegalArgumentException("Staging repositories are not supported for SNAPSHOT versions.")
    }
    repo.setUrl("${host.rootUrl}/content/repositories/snapshots/")
}

5.3 公布 snapshot 组件

版本号中带 SNAPSHOT 将被视为 snapshot 组件,会间接公布到 snapshot 地方仓库。通过小彭验证,的确在前端公布后,立马能够在 snapshot 地方仓库搜寻到,例如 小彭的组件。

验证截图

5.4 公布 release 组件到 Staging 暂存库

版本号中未带 SNAPSHOT 将视为 release 组件,公布 release 组件后,进入 Nexus 面板查看暂存库(右上角 Log in 登录):

操作截图

5.5 公布 release 组件到地方仓库

确认要公布组件后,先点击 Close,再点击 Release 即可公布:

操作截图

Close 的过程会对组件进行验证,验证失败的话就会报错了。你能够间接从 Activity 面板中查看报错提醒,我遇到的几次问题都是参数缺失的小问题。

报错提醒

点击 Drop 按钮删除有问题的组件:

操作截图

如果验证通过,Release 按钮就会高亮,点击按钮就终于终于公布了。

操作截图

5.6 查看已公布的 release 组件

公布胜利后,有 3 种形式查看本人的组件:

  • 办法 1 – 在 Sonatype Nexus 面板上查看:

操作截图

  • 办法 2 – 在 release 地方仓库的文件目录中查看,例如 小彭的 release 组件:

操作截图

  • 形式 3 – 在 MavenCentral 搜寻栏 查找: 这是最正式的形式,毛病是不实时更新,大略有 的提早,而前两种形式在公布后立刻更新:

操作截图

依照 官网的说法,公布后的组件会在 30 分钟内同步到地方仓库,但搜寻性能须要达到 4 个小时:

Upon release, your component will be published to Central: 
this typically occurs within 30 minutes,
though updates to search can take up to four hours.

5.6 依赖已公布的组件

怎么依赖大家都懂。讲一下仓库吧,如果是曾经公布到 release 地方仓库,你的工程只有蕴含 mavenCentral() 这个仓库地址就能够了。

示例程序

repositories {
    // 地方仓库(不蕴含 snapshot 地方仓库)mavenCentral()
    // release 地方仓库
    maven {url 'https://s01.oss.sonatype.org/content/repositories/releases'}
    // snapshot 地方仓库
    maven {url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'}
    // 暂存库,用于验证
    maven {url "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2"}        
}

6. 报错记录

  • Sonatype 账号密码谬误:
Failed to publish publication 'maven' to repository 'mavenCentral'
   > Could not PUT 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/io/github/pengxurui/modular-eventbus-annotation/1.0.2/modular-eventbus-annotation-1.0.2.jar'. Received status code 401 from server: Unauthorized
  • GPG 密钥谬误:
Execution failed for task ':eventbus_annotation:signMavenPublication'.
> Error while evaluating property 'signatory' of task ':eventbus_annotation:signMavenPublication'
   > org.bouncycastle.openpgp.PGPException: checksum mismatch at 0 of 20
  • GPG 密钥算法谬误:
Execution failed for task ':eventbus_annotation:signMavenPublication'.
> Error while evaluating property 'signatory' of task ':eventbus_annotation:signMavenPublication'
   > org.bouncycastle.openpgp.PGPException: unknown public key algorithm encountered 
  • Javadoc 生成报错:
Execution failed for task ':eventbus_api:androidJavadocs'.
> Javadoc generation failed. Generated Javadoc options file (useful for troubleshooting): '/Users/pengxurui/workspace/public/ModularEventBus/eventbus_api/build/tmp/androidJavadocs/javadoc.options'
  • vanniktech 插件与 Dokka 插件兼容问题:
Execution failed for task ':eventbus_api:javaDocReleaseGeneration'.
> 'void org.jetbrains.dokka.DokkaSourceSetImpl.<init>(java.lang.String, org.jetbrains.dokka.DokkaSourceSetID,
...
  • POM 验证谬误:

7. 寻求 Sonatype 官网帮忙

如果你在应用 Sonatype 的过程中遇到任何问题,能够尝试向官网发问。我试过一次,10 分钟后就收到回复了,还是很 Nice 的。

操作截图

操作截图


8. 总结

祝贺,到这里,咱们曾经可能实现公布开源我的项目到 MavenCentral 地方仓库。还没完,引出两个问题:

  • Github Action: 每次公布都须要咱们手动执行 upload 工作,Github 仓库中的 Releases 面板也不会同步显示手动公布的版本记录。 咱们冀望的成果是在 Github 仓库上公布一个 Release 版本时,主动触发将该版本公布到 MavenCentral 地方仓库。 这须要用到 Github 提供的 CI/CD 服务 —— Github Action;
  • ModularEventBus: 本文的示例程序,它是做什么的呢?

关注我,带你理解更多。


参考资料

  • Sonotype · 常见问题 Q&A —— Sonotype 官网文档
  • Sonatype · GPG —— Sonatype 官网文档
  • Sonatype · Gradle —— Sonatype 官网文档
  • Sonatype · Managing Staging Repositories —— Sonatype 官网文档
  • Sonatype · Release —— Sonatype 官网文档
  • Github · 生成新 GPG 密钥 —— Github 官网文档
  • Github · Adding a GPG key to your GitHub account —— Github 官网文档
  • Github · gradle-maven-publish-plugin —— vanniktech 著
  • Dokka · Using the Gradle plugin —— Dokka 官网文档
  • GPG 入门教程 —— 阮一峰 著
  • PGPException: unknown public key algorithm encountered 问题 —— Java 侠 著

你的点赞对我意义重大!微信搜寻公众号 [彭旭锐],心愿大家能够一起探讨技术,找到气味相投的敌人,咱们下次见!

退出移动版