配置 android 上傳到 maven 中心倉庫,發(fā)現(xiàn)配置的代碼有點多,而且如果有多個庫模塊需要上傳,則需要復(fù)制粘貼不少重復(fù)的配置代碼,于是編寫了一個 gradle 插件用于簡化提取這個配置過程;
插件用途及效果
本插件用于簡化Android庫上傳到Maven中心倉庫的配置,避免每個project的gradle中都放置一份重復(fù)較多的配置;
插件有如下功能:
- 簡化 maven-publish 插件的配置流程;
- 添加上傳到Maven中心倉庫的 publish 任務(wù);
- 支持配置上傳時是否包含 javadoc 及源碼;
直接使用maven-publish插件的配置:
import org.gradle.api.publish.maven.MavenPom
plugins {
id("com.android.library")
id("signing")
`maven-publish`
}
android {
defaultConfig {
versionName("1.0.0-SNAPSHOT")
}
}
dependencies {
//
}
tasks.register("javadoc", Javadoc::class.java) {
group = "publishing"
dependsOn("assemble")
source = android.sourceSets["main"].java.getSourceFiles()
classpath += project.files(android.bootClasspath + File.pathSeparator)
if (JavaVersion.current().isJava9Compatible) {
(options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
}
android.libraryVariants.forEach { libraryVariant ->
classpath += libraryVariant.javaCompileProvider.get().classpath
}
options.apply {
encoding("UTF-8")
charset("UTF-8")
isFailOnError = false
(this as StandardJavadocDocletOptions).apply {
// addStringOption("Xdoclint:none")
links?.add("https://developer.android.google.cn/reference/")
links?.add("http://docs.oracle.com/javase/8/docs/api/")
}
}
}
tasks.register("jarSource", Jar::class.java) {
group = "publishing"
from(android.sourceSets["main"].java.srcDirs)
archiveClassifier.set("sources")
}
tasks.register("jarJavadoc", Jar::class.java) {
group = "publishing"
dependsOn("javadoc")
val javadoc: Javadoc = tasks.getByName("javadoc") as Javadoc
from(javadoc.destinationDir)
archiveClassifier.set("javadoc")
}
fun getMyPom(): Action<in MavenPom> {
return Action<MavenPom> {
name.set("Android Common Utils Lib")
description.set("Android Common Utils Library For HJ")
url.set("https://github.com/hanlyjiang/lib_common_utils")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("hanlyjiang")
name.set("Hanly Jiang")
email.set("hanlyjiang@outlook.com")
}
}
scm {
connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
url.set("https://github.com/hanlyjiang/lib_common_utils")
}
}
}
afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
from(components.getByName("release"))
groupId = "com.github.hanlyjiang"
artifactId = "android_common_utils"
version = android.defaultConfig.versionName
pom(getMyPom())
// 添加javadoc
artifact(tasks.getByName("jarJavadoc") as Jar)
// 添加source
artifact(tasks.getByName("jarSource") as Jar)
}
}
repositories {
val ossrhCredentials = Action<PasswordCredentials> {
username = properties["ossrhUsername"].toString()
password = properties["ossrhPassword"].toString()
}
// sonar的倉庫,地址根據(jù)項目的版本號來確定是snapshot還是正式倉庫
maven {
name = "Sonartype"
val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")
val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/")
url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials(ossrhCredentials)
// snapshot的地址:
// https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/android_common_utils/
}
// 項目本地的倉庫
maven {
name = "ProjectLocal"
val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases"))
val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots"))
url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
}
}
}
signing {
sign(publishing.publications.getByName("release"))
}
}
使用AndroidMavenPubPlugin簡化后的配置:
import org.gradle.api.publish.maven.MavenPom
plugins {
id("com.android.library")
id("signing")
`maven-publish`
// 引入我們本地倉庫中的gradle插件
id("com.github.hanlyjiang.android_maven_pub") version ("0.0.5") apply (false)
}
android {
defaultConfig {
versionName("1.0.1-SNAPSHOT")
}
}
dependencies {
//
}
apply(plugin = "com.github.hanlyjiang.android_maven_pub")
configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
groupId.set("com.github.hanlyjiang")
artifactId.set("android-common-utils")
projectLocalRepoPath.set("local-maven-repo")
mavenPomAction.set(Action<MavenPom> {
name.set("Android Common Utils Lib")
description.set("Android Common Utils Library For HJ")
url.set("https://github.com/hanlyjiang/lib_common_utils")
properties.set(
mapOf(
"myProp" to "value",
"prop.with.dots" to "anotherValue"
)
)
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("hanlyjiang")
name.set("Hanly Jiang")
email.set("hanlyjiang@outlook.com")
}
}
scm {
connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
url.set("https://github.com/hanlyjiang/lib_common_utils")
}
})
}
使用步驟
引入插件
在需要使用的模塊的build腳本中,引入我們的插件,同時引入 maven-publish 插件和signing 插件。
kotlin.kts 腳本寫法
plugins {
id("com.android.library")
// 引入signing插件
id("signing")
// 引入maven-publish插件
`maven-publish`
// 引入 android_maven_pub 插件,注意這里設(shè)置 apply 為 false,表示引入但是不應(yīng)用,我們需要放在android配置段定義之后再應(yīng)用
id("com.github.hanlyjiang.android_maven_pub") version ("0.0.5") apply (false)
}
groovy 的寫法
plugins {
id 'com.android.library'
id 'signing'
id 'maven-publish'
id("com.github.hanlyjiang.android_maven_pub") version("0.0.5") apply(false)
}
配置 gradle
在引入 AndroidMavenPubPlugin 插件之后,我們可以對插件進行配置。 建議將腳本轉(zhuǎn)換為 kotlin dsl 的寫法,能有對應(yīng)的自動提示;
kotlin dsl 寫法
android {
}
// 需要先應(yīng)用插件,在android配置完成之后,建議放在腳本最下方
apply(plugin = "com.github.hanlyjiang.android_maven_pub")
configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
groupId.set("com.github.hanlyjiang")
artifactId.set("android_common_utils")
mavenPomAction.set(Action<MavenPom> {
name.set("Android Common Utils Lib")
description.set("Android Common Utils Library For HJ")
url.set("https://github.com/hanlyjiang/lib_common_utils")
properties.set(
mapOf(
"myProp" to "value",
"prop.with.dots" to "anotherValue"
)
)
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("hanlyjiang")
name.set("Hanly Jiang")
email.set("hanlyjiang@outlook.com")
}
}
scm {
connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git")
developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git")
url.set("https://github.com/hanlyjiang/lib_common_utils")
}
})
}
groovy 腳本寫法
apply plugin: "com.github.hanlyjiang.android_maven_pub"
android_maven_pub {
groupId.set("com.github.hanlyjiang")
artifactId.set("android-common-utils")
mavenPomAction.set({ pom ->
pom.with {
name.set('HJ Android Plugin Framework')
description.set("A Android Plugin Framework")
url.set("https://github.com/hanlyjiang/apf-library")
licenses {
license {
name = 'The Apache Software License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'hanlyjiang'
name = 'hanly jiang'
email = 'hanlyjiang@outlook.com'
}
}
scm {
connection = 'https://github.com/hanlyjiang/apf-library'
developerConnection = 'https://github.com/hanlyjiang/apf-library.git'
url = 'https://github.com/hanlyjiang/apf-library'
}
}
} as Action<MavenPom>)
}
??注意:
可以看到我們這里并沒有定義版本號,版本號從
android.defaultConfig.versionName中取 ;同時,如果版本號結(jié)尾為
-SNAPSHOT,則會發(fā)布到 snapshot 倉庫,如果沒有,則發(fā)布到 release 倉庫;
maven倉庫的屬性文件配置
上傳到maven center中心倉庫需要進行一些賬號申請和key的生成操作,可以參考Jcenter 停止服務(wù),說一說我們的遷移方案 - InfoQ 寫作平臺 來完成,我們需要將最后獲取到的認證信息:
按如下配置,將對應(yīng)的 value 的值更改為自己的賬號及 key 的對應(yīng)值,然后填入到 ~/.gradle/gradle.properties 中即可
# 配置maven中心倉庫訪問賬號
ossrhUsername=sonatype jira 賬號的用戶名
ossrhPassword=sonatype jira 賬號的密碼
# 配置簽名信息
signing.keyId=公鑰 ID 的后 8 位
signing.password=鑰匙串的密碼
signing.secretKeyRingFile=導(dǎo)出的 gpg 文件路徑 如: /Users/hanlyjiang/.gnupg/secring.gpg
??注意: 這里的 key 是固定的,不要修改
執(zhí)行上傳任務(wù)
經(jīng)過上面的配置,我們同步下 gradle ,對應(yīng)引入了插件的項目中會生成若干任務(wù):
其中:
-
jarJavadoc,jarSource,javadoc為我們生成的輔助任務(wù) -
publish及generate開頭的為mave-publish插件生成的任務(wù),我們執(zhí)行publish相關(guān)的任務(wù)即可發(fā)布 maven 庫;
publish 相關(guān)的任務(wù)有如下幾個:
| Task Name | Description |
|---|---|
| publish | 等于執(zhí)行了下面的所有任務(wù) |
| publishAllPublicationsToProjectLocalRepository | 將由此項目產(chǎn)生的所有Maven庫發(fā)布到 ProjectLocal 存儲庫。 ProjectLocal 定義為當(dāng)前項目的 build 目錄的 mavenRepos 目錄中,有兩個子目錄 snapshots及 release, 方便查看將要發(fā)布的生成物及進行本地測試; |
| publishAllPublicationsToSonartypeRepository | 將由此項目產(chǎn)生的所有 Maven 庫發(fā)布到 Sonartype 存儲庫。 |
| publishReleasePublicationToMavenLocal | 將由此項目產(chǎn)生的名為release的 Maven庫發(fā)布到本機 maven 緩存庫。 |
| publishReleasePublicationToProjectLocalRepository | 將由此項目產(chǎn)生的名為release的 Maven庫發(fā)布到本機 ProjectLocal 庫。 |
| publishReleasePublicationToSonartypeRepository | 將由此項目產(chǎn)生的名為release的 Maven庫發(fā)布到本機 Sonartype 庫。 |
| publishToMavenLocal | 將由此項目產(chǎn)生的所有 Maven 庫發(fā)布到 本機 maven 緩存庫。 |
說明:
上面的任務(wù)由 maven-publish 的插件生成,該插件生成任務(wù)的規(guī)則使用 Publications 和 Repo 來組合實現(xiàn),其中:
- publication 是我們定義的發(fā)布庫;
android_maven_pub插件中,我們定義了一個名為release的配置; - repo 是我們定義的 maven 倉庫的位置,
android_maven_pub定義了兩個倉庫的位置,分別名為 ProjectLocal 和 Sonartype ,另外maven-publish會給我們加上一個 MavenLocal 的配置(指向本機 maven 倉庫緩存目錄)- 其中 ProjectLocal 指向引入了該插件的項目(模塊)的
build/repos目錄 -
Sonartype則默認指向 maven 中心倉庫的地址,可以通過配置來更改;
- 其中 ProjectLocal 指向引入了該插件的項目(模塊)的
可配置項目說明
| 配置字段 | 說明 | 默認值 |
|---|---|---|
| groupId | maven 的 group id,需要設(shè)置為自己申請的 | 無默認值-必需填寫 |
| artifactId | library 的 id | 無默認值-必需填寫 |
| mavenPomAction | 用于配置 pom 信息的字段 | 無默認值-必需填寫 |
| fromAndroidPubName | 表示發(fā)布的 android 的 aar 的類型,release 或 debug
|
release |
| releasesRepoUrl | maven release 倉庫的上傳地址 | https://oss.sonatype.org/service/local/staging/deploy/maven2 |
| snapshotsRepoUrl | maven snapshots 倉庫的上傳地址 | https://oss.sonatype.org/content/repositories/snapshots/ |
| includeSourceJar | 是否上傳源碼 | true |
| includeJavadocJar | 是否上傳 javadoc | true |
| projectLocalRepoPath | 本地倉庫的目錄,相對于rootProject的目錄,如:local-maven-repo
|
默認位于根項目的 build/mavenRepos 目錄中 |
常見問題
發(fā)布到 snapshot
我們根據(jù)版本號來選擇發(fā)布到的是 snapshot 還是 release 倉庫,版本號從 android->defaultConfig - versionName 中讀取,如:
android {
compileSdkVersion(30)
buildToolsVersion("30.0.3")
defaultConfig {
minSdkVersion(22)
targetSdkVersion(30)
versionCode(1)
versionName("1.0.0-SNAPSHOT")
}
}
發(fā)布到本地并進行測試
首先我們通過 projectLocalRepoPath 來改變 ProjectLocal 倉庫的目錄,下面的示例中將其設(shè)置為 rootProject 的 local-maven-repo 目錄中
apply(plugin = "com.github.hanlyjiang.android_maven_pub")
configure<io.hanlyjiang.gradle.android.AndroidMavenPubPluginExtension> {
groupId.set("com.github.hanlyjiang")
artifactId.set("android-common-utils")
projectLocalRepoPath.set("local-maven-repo")
// ...
}
接下來我們定義本地的 Repo ,見下方名為 ProjectLocal-Snapshots 及 ProjectLocal-Release 的倉庫。
allprojects {
repositories {
maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
maven { setUrl("https://maven.aliyun.com/repository/google") }
maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
maven { setUrl("https://maven.aliyun.com/repository/public") }
// 定義本地repo路徑
maven {
name = "ProjectLocal-Snapshots"
setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}snapshots"))
}
maven {
name = "ProjectLocal-Release"
setUrl(File(rootProject.rootDir, "local-maven-repo${File.separator}release"))
}
maven {
name = "Sonatype-Snapshots"
setUrl("https://oss.sonatype.org/content/repositories/snapshots")
// setUrl("https://s01.oss.sonatype.org/content/repositories/snapshots")
// snapshot可以不用用戶名密碼
// 查看自己的snapshot版本: https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/
credentials(PasswordCredentials::class.java) {
username = property("ossrhUsername").toString()
password = property("ossrhPassword").toString()
}
}
maven {
name = "Sonatype-Staging"
setUrl("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
// setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials(PasswordCredentials::class.java) {
username = property("ossrhUsername").toString()
password = property("ossrhPassword").toString()
}
}
}
}