是的, 这是另一本关于如何将 Android artifacts发布到 Maven Central 的指南. 与其他指南不同的是, 它还包含了一些额外的"内容":
- 使用 Kotlin DSL 编写 Gradle 文件.
- 发布AAR文件artifacts.
- 发布包含一个Source Jar 文件(hello
withSourcesJar
). - 发布包含一个 Javadoc Jar 文件(hello
withJavadocJar
). - 该发布与CI/CD管道协同工作.
- 我提到过这是Android专用吗?
对于那些不喜欢阅读的人, 以下是我使用所解释的发布机制的两个软件仓库:
Sonatype OSSRH
Maven Central 实际上只是多个仓库的门面. Sonatype OSSRH 就是这样一个仓库. 为了发布你的artifacts, 你需要在他们(或其他Release 仓库提供商)那里注册一个账户. 关于如何开设账户和正确配置账户, 有很多不错的文章. 我将在此引用这些文章, 然后就结束了(这是一个繁琐的过程, 所以请确保你有时间...):
- central.sonatype.org/publish/pub...
- mrcurious.medium.com/publishing-...
- getstream.io/blog/publis...
- dev.to/madhead/no-...
- www.waseefakhtar.com/android/pub...
所需插件
此时的假设是:
- 你在 Sonatype OSSRH 有一个账户.
- 你在 Sonatype OSSRH 上创建了一个仓库, 并已验证了你对仓库的所有权.
- 你创建了用于签署artifacts的GPG 密钥对, 并已发布公钥和导出私钥.
- 你已经有了一个带有 Kotlin DSL 构建文件(与 Groovy 构建文件相比)的应用程序 -> build.gradle.kts.
现在第一步是在构建文件中添加两个插件:
bash
plugins {
id("com.android.library")
id("maven-publish")
id("signing")
}
- maven-publish : 提供将构建artifacts发布到Apache Maven仓库的功能
- signing : 用于签署构成发布的所有artifacts和元数据文件
发布
第二步是创建并配置 publishing 扩展, 将其添加到 gradle 构建文件中:
arduino
afterEvaluate {
publishing {
publications {
// here goes your configuration
}
}
}
afterEvaluate
确保项目已被评估, 并可在配置块中访问.
配置包括三个步骤:
- 配置仓库
- 配置发布
- 签署artifacts
arduino
afterEvaluate {
publishing {
publications {
// 1. configure repositories
// 2. configure publication
// 3. sign the artifacts
}
}
}
仓库
这部分配置定义了目标仓库. 在我们的例子中, 它是 Sonatype OSSRH, 可以是Release库, 也可以是SNAPSHOT库.
目标仓库的确定基于版本名称. 后缀名为-SNAPSHOT
的版本将上传到SNAPSHOT仓库.
发布
配置发布非常简单.
首先, 我们定义要发布的artifacts:
scss
from(project.components["release"])
artifact(tasks.named<Jar>("withJavadocJar"))
artifact(tasks.named<Jar>("withSourcesJar"))
project.components["release"]
是 Android 项目生成的artifacts, 对于 Android 库来说, 应该是一个 aar 文件. 另外两行将 Javadoc 和源码jar 文件声明为要发布的artifacts. 本文稍后将解释如何生成这两个artifacts.
配置的其余部分定义了 Maven POM 文件, 它是 Maven 项目(项目元信息)的 XML 表示形式.
需要配置的最重要属性是groupId , artifactId 和version (例如, 对于 com.android.tools.build:gradle:7.0.2
, groupId 是com.android.tools.build , artifactId 是gradle , version 显然是7.0.2). 所有其他属性都是可选参数, 用于定义软件许可证, 识别公司/开发者, 源代码等.
正如你所看到的, 这些属性都是从 project.properties
中读取的, 而 project.properties
是在其中一个 gradle.properties
文件(项目或库的特定属性文件)中定义的属性. 下面是这样一个属性文件的例子: github.com/1gravity/An....
请注意, 仓库的用户名和密码不是在项目属性文件中定义的, 而是应该在你的 ~/.gradle/gradle.properties
文件(~ 代表你的主目录)中定义, 这样凭据才不会提交到源代码控制中. 如果没有该文件, 则需要创建并添加凭据:
ini
ossUsername=<your OSS user>
ossPassword=<your OSS password>
签名
对artifacts进行签名非常简单:
scss
signing {
sign(publishing.publications.getByName(publicationName))
}
签名插件使用你之前创建的密钥(请参阅 "你创建了用于签署artifacts的GPG 密钥对 , 并已发布公钥和导出私钥"). 该插件需要 private key 文件, keyId 和 password , 这些都是在~/.gradle/gradle.properties
文件中定义的. 将此添加到文件中:
ini
signing.secretKeyRingFile=/<user>/.gnupg/secring.gp
g
signing.keyId=<last 8 characters of your key id>
signing.password=<the password>
注意, ~/.gnupg/secring.gpg
将不起作用, 因为 Android 无法将~
解析为你的主目录.
就是这样. 如果你在项目目录下运行 ./gradlew publish
, 它就会构建并向 Sonatype OSSRH 发布你的库. 你仍然需要手动关闭和发布库, 虽然这也可以自动完成, 但你应该熟悉手动过程: central.sonatype.org/publish/rel....
使用 CI/CD 签名
大多数构建管道以字符串形式读取秘密, 而不是从文件(如上文使用的 secretKeyRingFile
)中读取. 虽然有一些方法可以将文件送入管道(关于 BitBucket 管道的描述可参见这里), 但这是一个相当繁琐的过程. 最好还是不要使用文件.
在本例中, 我们可以在创建签名任务前调用 useInMemoryPgpKeys
来使用内存中的 PGP 密钥和密码:
如果在本地编译, 这三个参数将从属性文件中读取, 因此请将这些值放入~/.gradle/gradle.properties
文件中:
ini
signingKeyId=<last 8 characters of your key id>
signingKeyPassword=<the password>
signingKey=<the key>
密钥需要装上铠甲
, 即转换为完全由文本模式/ASCII 字符组成的文件加密表示. 它还需要去掉 gpg 对实际密钥的典型包装(- - -BEGIN PGP PRIVATE KEY BLOCK - - -
部分).
使用此单行命令获取密钥值:
perl
gpg --export-secret-keys --armor <KEY_ID> |grep -v '--' |grep -v '=.' |tr -d '\n'
要在构建管道中运行此功能, 需要在相应的秘密管理器中配置秘密. 对于 GitHub 来说是这样的:
在定义管道时, 你需要把秘密作为参数传递给 gradle/gradlew
.
对于 GitHub Actions, 语法为:
ini
./gradlew -PossUsername=${{ secrets.OSSRH_USERNAME }}
对于 BitBucket Pipeline, 语法为:
ini
./gradlew -PossUsername=$OSSRH_USERNAME
withSourcesJar
在 Java 项目中, 有一种简单的方法可以为源代码和 Javadoc 创建 Jar 文件:
scss
java {
withSourcesJar()
withJavadocJar()
}
遗憾的是, 这在 Android 项目中行不通. 不过, 复制 withSourcesJar 非常简单:
c
tasks {
register<Jar>("withSourcesJar") {
archiveClassifier.set("sources")
from(android.sourceSets.getByName("main").java.srcDirs)
}
}
这将注册一个名为 withSourcesJar
的任务. 它会创建一个 Jar 文件(因此定义了 Jar
类型, 并使用 Android 源代码目录(android.sourceSets.getByName("main").java.srcDirs
)作为 jar 文件的输入.
withJavadocJar
为 Javadoc 创建 jar 文件同样简单:
scss
tasks {
archiveClassifier.set("javadoc")
dependsOn(named("withJavadoc"))
val destination = named<Javadoc>("withJavadoc").get().destinationDir
from(destination)
}
唯一的问题是, 我们需要先创建 Javadoc, 然后才能创建 jar 文件. 如上所示, 我们引用了名为 withJavadoc
的任务来创建实际文档.
总结一下
这就是全部代码:
该代码将在这里使用:
1gravity/Android-RTEditor/build.gradle.kts
以及这里:
1gravity/Android-ColorPicker/build.gradle.kts
Happy Coding! Stay GOLDEN!