常规上传方式
为了避免重复功能的编码工作,我们通常会把一些公共组件打包上传到Maven仓库,而上传的功能通常是使用
Gradle提供的maven
或maven-publish
来实现,如下:
maven插件(已废弃)
plugins {
id 'maven'
}
//配置仓库和版本信息
ext {
libGroup = "com.yumdao.daydayup"
libArtifactId = "demo01"
libVersion = "1.0.0"
mavenUrl = libVersion.endsWith("SNAPSHOT") ? "http://localhost:8081/repository/maven-snapshots/" : "http://localhost:8081/repository/maven-app/"
mavenAccount = "admin"
mavenPassword = "admin123"
}
//定义上传方法
uploadArchives {
repositories {
mavenDeployer {
repository(url: mavenUrl) {
authentication(userName: mavenAccount, password: mavenPassword)
}
pom.groupId = libGroup
pom.artifactId = libArtifactId
pom.version = libVersion
}
}
}
然后就可以通过该模块Tasks
下upload
分组里的uploadArchives
完成上传到maven仓库的功能。不过maven
插件在Gradle 7.x版本已经被移除,不建议使用该方式。
maven-publish插件
plugins {
id 'maven-publish'
}
ext {
libGroup = "com.yumdao.daydayup"
libArtifactId = "demo01"
libVersion = "1.0.1"
mavenUrl = libVersion.endsWith("SNAPSHOT") ? "http://localhost:8081/repository/maven-snapshots/" : "http://localhost:8081/repository/maven-app/"
mavenAccount = "admin"
mavenPassword = "admin123"
}
//在afterEvaluate后才能获取到buildTypes里的release和debug
afterEvaluate {
publishing {
publications {
//配置生成上传release节点的task
release(MavenPublication) {
from components.release
groupId = libGroup
artifactId = libArtifactId
version = libVersion
}
//如果有需要,可以配置上传debug版本,也可以只上传release版本
debug(MavenPublication) {
from components.debug
groupId = libGroup
artifactId = libArtifactId
version = libVersion
}
}
repositories {
maven {
url = mavenUrl
credentials {
username = mavenAccount
password = mavenPassword
}
}
}
}
}
配置完成之后可以在模块的Tasks
下publishing
分组下生成两个新的task,分别为publishReleasePublicationToMavenRepository
和publishDebugPublicationToMavenRepository
,对应的是打包release或debug后上传到maven仓库。
常规方式的不足
虽然能够通过上面的配置,很容易实现组件上传功能,但是确有以下一些问题:
问题1:如果维护的组件数量比较多,每个都需要这么配置,会比较繁琐。
问题2:上传到Maven之前没有进行版本校验,如果一不小心忘记升级版本号,会导致原版本被覆盖,而且在使用时也会有缓存问题。
问题3:无法有效管理版本更新日志,通常是在发布版本之后手动修改README来实现。
解决方案
方案1:可以使用自定义gradle脚本或插件,把核心上传功能封装起来,只需要对外暴露必要的参数,最大程度上降低配置的繁琐度,为了避免自定义gradle脚本到处copy的问题,可以把脚本托管到服务器端,使用远程依赖的方式完成,例如:
//使用文件服务器托管脚本
apply from: "http://localhost:8080/gradle/upload.gradle"
远程依赖的方式适合简单的脚本,但如果想要对功能现实增强的话,会在单个文件中编写大量的代码,不利于后期脚本的维护迭代,而且在开发时,gradle脚本的api对于开发者来说并不友好,相信写过脚本的同学都深有体会。
所以,我选择用插件的方式来对上传功能进行封装。
方案2:在打包上传Maven仓库之前,需要做一些前置操作,判断当前上传配置项是否规范以及线上是否存在相同的版本号。
方案3:在原始的打包上传中,我们升级万类库之后一般需要手动修改README对当前版本更新的内容进行描述,这样做起来相当繁琐,而且也不能直观的查看各个历史版本信息,如果把更新信息放到配置里去,在上传Maven仓库后上传到服务器端,统一管理起来,然后用页面的形式展现,无疑会更直观。
脚本实现
1、创建自定义参数类
//该类要是open的才可以
open class Publisher {
companion object {
const val NAME = "publisher"
}
//配置仓库信息
var repoRelease: String = ""
var repoSnapshot: String = ""
//配置账户名和密码
var repoAccount: String = ""
var repoPassword: String = ""
//配置library版本信息
var libGroup: String = ""
var libArtifact: String = ""
var libVersion: String = ""
//当前版本的更新描述
var libUpdateDesc: ArrayList<String> = arrayListOf()
//版本更新描述
var pomDesc: String = ""
var pomName: String = ""
var pomUrl: String = ""
//发布人
var publisher: String = ""
}
2、创建自定义Task
//该类要是open的才可以
open class PublisherTask : DefaultTask() {
//是否完成执行任务
private var executeFinishFlag: AtomicBoolean = AtomicBoolean(false)
//检验状态是否通过
private var checkStatus = false
private lateinit var publisher: Publisher
init {
group = "upload"
project.run {
publisher = extensions.getByName(Publisher.NAME) as Publisher
//动态为该模块引入上传插件
apply(hashMapOf<String, String>(Pair("plugin", "maven-publish")))
val publishing = project.extensions.getByType(PublishingExtension::class.java)
afterEvaluate {
components.forEach {
if (it.name == "release") {
publishing.publications { publications ->
//注册上传task
publications.create("release",
MavenPublication::class.java) { publication ->
publication.groupId = publisher.libGroup
publication.artifactId = publisher.libArtifact
publication.version = publisher.libVersion
publication.from(it)
}
}
publishing.repositories { artifactRepositories ->
artifactRepositories.maven { mavenArtifactRepository ->
mavenArtifactRepository.url =
if (publisher.libVersion.endsWith("SNAPSHOT")) {
URI(publisher.repoSnapshot)
} else {
URI(publisher.repoRelease)
}
mavenArtifactRepository.credentials { credentials ->
credentials.username = publisher.repoAccount
credentials.password = publisher.repoPassword
}
}
}
}
}
}
}
}
@TaskAction
fun doTask() {
executeTask()
//开启线程守护,防止子线程任务还没执行完毕,task就已经结束了
while (!executeFinishFlag.get()) {
Thread.sleep(500)
}
}
private fun executeTask() {
//1、对publisher配置的信息进行基础校验
//2、把publisher上传到服务器端,做版本重复性校验
checkStatus = requestCheckVersion()
//如果前两步都校验通过了,checkStatus设置为true
if (checkStatus) {
val out = ByteArrayOutputStream()
//通过命令行的方式进行调用上传maven的task
project.exec { exec ->
exec.standardOutput = out
exec.isIgnoreExitValue = true
exec.commandLine(
"${project.rootDir}/gradlew",
"publishReleasePublicationToMavenRepository"
)
}
val result = out.toString()
if (result.contains("UP-TO-DATE")) {
//上传maven仓库成功,上报到服务器
val isSuccess = requestUploadVersion()
if (isSuccess) {
//提示成功信息
} else {
//提示错误信息
}
executeFinish()
} else {
throw Exception("上传Maven仓库失败,请检查配置!")
}
}
}
private fun requestCheckVersion(): Boolean {
//TODO 上报服务器进行版本检查,这里直接模拟返回成功
return true
}
private fun requestUploadVersion(): Boolean {
//TODO 上报服务器进行版本更新操作,这里直接模拟返回成功
return true
}
/**
* 任务执行完毕
*/
private fun executeFinish() {
executeFinishFlag.set(true)
}
}
3、创建自定义Plugin并注册参数及Task
class DDPlugin : Plugin<Project> {
override fun apply(project: Project) {
//注册自定义参数
project.extensions.create(Publisher.NAME, Publisher::class.java)
val currProjectName = project.displayName
//在afterProject生命周期之后,才可以从build.gradle文件中获取到配置的参数
project.gradle.afterProject { currProject ->
//如果是当前模块,才进行task的注册,避免冗余注册
if (currProjectName == currProject.displayName) {
//注册上传task,这里不要dependsOn
project.tasks.create("publishToMaven", PublisherTask::class.java)
}
}
}
}
4、在module中配置插件
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'com.yumdao.daydayup' //声明插件
}
//配置参数
publisher {
//配置release版本的仓库地址
repoRelease = "http://localhost:8081/repository/maven-app/"
//配置快照版本的仓库地址
repoSnapshot = "http://localhost:8081/repository/maven-snapshots/"
//账号
repoAccount = "admin"
//免密
repoPassword = "admin123"
//上报组件信息三件套
libGroup = "com.yumdao.test"
libArtifact = "daydayup"
libVersion = "1.0.0-SNAPSHOT"
//更新日志
libUpdateDesc = [
"1.发布新版本",
]
//配置项目信息
pomName = "DayDayUp"
pomUrl = "http://daydayup.com"
pomDesc = "这仅仅是一个测试项目!"
//发布人
publisher = "wangyumdao"
}
Sync项目之后,会在该模块Tasks
下出现upload
分组,里面有一个名为publishToMaven
的Task,运行Task即可完成上传功能。
打包完成之后配置信息需要上传到服务器端,由服务器记录,虽然需要服务器端的支持才能完成组件的上报功能,不过问题不大,这年头谁还不会搭一个服务器呢(手动狗头)。
最后,献上Demo地址