簡化Android庫上傳到Maven倉庫的gradle配置

配置 android 上傳到 maven 中心倉庫,發(fā)現(xiàn)配置的代碼有點多,而且如果有多個庫模塊需要上傳,則需要復(fù)制粘貼不少重復(fù)的配置代碼,于是編寫了一個 gradle 插件用于簡化提取這個配置過程;

插件用途及效果

本插件用于簡化Android庫上傳到Maven中心倉庫的配置,避免每個project的gradle中都放置一份重復(fù)較多的配置;

插件有如下功能:

  1. 簡化 maven-publish 插件的配置流程;
  2. 添加上傳到Maven中心倉庫的 publish 任務(wù);
  3. 支持配置上傳時是否包含 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ù)
  • publishgenerate 開頭的為 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 目錄中,有兩個子目錄 snapshotsrelease, 方便查看將要發(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 中心倉庫的地址,可以通過配置來更改;

可配置項目說明

配置字段 說明 默認值
groupId maven 的 group id,需要設(shè)置為自己申請的 無默認值-必需填寫
artifactId library 的 id 無默認值-必需填寫
mavenPomAction 用于配置 pom 信息的字段 無默認值-必需填寫
fromAndroidPubName 表示發(fā)布的 android 的 aar 的類型,releasedebug 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-SnapshotsProjectLocal-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()
            }
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容