概览
文章目录
前言
最近在内网为Android项目配置了maven私服仓,想要把组件化的使用更进一步,即各组件发布到远端私服仓库,项目内从私服仓库依赖组件。
远端依赖组件的好处,就是提高编译效率。随着组件越来越多,每次发版时需要同时编译20+个组件,非常耗时。而有些组件是没有改动的,没必要参与编译增加编译时间,这时候使用远程依赖,可以极大的提高编译效率。
用远程依赖组件来提高编译效率,实际上是把各组件的编译流程尽量提前,放在其他合适的时机去编译发布到私服仓,待发版时直接远程依赖编译好的输出文件即可。
本文的主要目的是根据gitlab-ci持续集成流程、项目内部发版流程,设计出一套合适的自动发布/依赖远程组件的流程
, 其中涉及到持续集成相关的知识,可以参考之前发布的Android持续集成实践系列文章
系列文章
Android 组件化实践------自动发布组件、根据编译环境自动依赖远程组件
正文开始
要做什么?
首先看标题就很清晰,我们要做两件事,自动发布组件到私服仓、自动从私服仓依赖远程组件。
先说结论:
发布组件和依赖远程组件,需要发布release和debug两个版本的组件。
想象一下,假如现在有个base组件,放着所有的基础库封装,所有业务组件都依赖它。base中有这么个逻辑,根据BuildConfig#DEBUG字段的值来使用测试环境/正式环境的base_url。这时候如果只发布release的base组件,你在测试环境远程依赖base组件时,base_url就错误的使用了正式环境的url。分别在合适的时机发布release或debug版本的组件
首先明确一点,执行发布命令,会编译+发布,所以发布时的编译也是耗时的;另外一个APP版本的完整开发流程,可能是发布N多个debug版本+发布1~3个发布候选版+发布一个正式版,debug和release的发布频率是不一样的,debug更多,所以如果同时发布release和debug,必然造成其中一个版本的编译浪费。
自动发布组件到私服仓
项目里使用了gitlab持续集成流程,可以在持续集成流程再加入一个发布组件的stage,在合适的条件下分别触发debug/release组件发布
自动发布 debug 组件
debug发布的是最频繁的,通常的做法是发布快照-SNAPSHOT
版本,但是这样处理又有弊端,之前的代码管理相当于是失效了。之前合作开发,其他同事提交代码后,我需要从远端仓库拉取后,新的代码才会在我本地生效,而直接使用快照版本,不用拉取最新代码,我本地编译时就会从远端获取最新代码的组件来引用,这样使开发环境管理起来很混乱。
基于以上,合适的时机为:当提交的代码中组件版本号更改,且当前分支在开发分支,触发自动发布debug组件
-
gitlab-ci配置发布组件阶段
gitlab-ci.yml:
image: inovex/gitlab-ci-android ####################### #gitlab-runner 配置文件# ####################### variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" before_script: # 配置 jdk 17/11/8 - export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ... # 获取权限 ... - chmod +x ./publish-module stages: - publishModule #定义发布组件阶段,在所有阶段之前 - build - reinforceAndChannel - deploy # gradle.properties文件变动时,编译前自动发布debug module publish_debug_module: stage: publishModule only: changes: - gradle.properties #组件版本号在此文件定义,此文件更改就触发发布组件stage refs: - /^develop(_[A-Za-z0-9]+)?$/ #只在开发分支 script: - echo "gradle.properties文件变动,执行发布debug module逻辑" - export publishMode="debug" - ./publish-module tags: - android ...
publish-module文件:
dos#!/usr/bin/env bash ############################ #自动编译时自动发布debug module # ############################ #任何命令失败时停止执行 set -e # 修改 FORCE_USE_DEPENDENCY_MODE 为 local echo 'start exec publish-module.sh ...' echo "publishMode is ${publishMode} ..." publishExec='publishDebugPublicationToMavenRepository' if [ "$publishMode" = "release" ]; then publishExec=publishReleasePublicationToMavenRepository fi echo "publishExec is ${publishExec} ..." echo '修改 gradle.properties 中 FORCE_USE_DEPENDENCY_MODE 为 local ...' sed -i "s/FORCE_USE_DEPENDENCY_MODE=.*$/FORCE_USE_DEPENDENCY_MODE=local/;" gradle.properties echo '修改成功,发布 module ...' ./gradlew $publishExec echo '发布成功,修改 gradle.properties 中 FORCE_USE_DEPENDENCY_MODE 为 remote ...' sed -i "s/FORCE_USE_DEPENDENCY_MODE=.*$/FORCE_USE_DEPENDENCY_MODE=remote/;" gradle.properties echo '修改成功' echo 'end exec publish-module.sh ...'
发布代码
maven-publish.gradle
:javaapply plugin: "maven-publish" apply from: "$rootDir/gradle/maven-utils.gradle" afterEvaluate { publishing { publications { ... debug(MavenPublication) { if (isAndroidEnv(project)) { from project.components.debug } else { from project.components.java } pom { ... url = NEXUS_DEBUG_URL withXml { ... } } } } repositories { maven { allowInsecureProtocol true //根据编译命令区分debug/release,上传组件到不同的仓库 if (isReleaseBuild()) { url = NEXUS_URL } else { url = NEXUS_DEBUG_URL } ... } } } }
gradle.properties:
groovy#配置是否全局强制开启依赖本地组件,优先级高于module内的配置. #会影响使用 projectCompat 引入的所有依赖 #[remote]:使用远程依赖;[local]:使用本地依赖;[none]:不使用全局强制依赖模式 FORCE_USE_DEPENDENCY_MODE=none
-
发布 debug 组件
修改版本号提交代码,触发gitlab-ci发布组件stage
自动发布 release 组件
从发布候选版(rc)开始,发版都切换到融合(assemble)分支上了,融合分支只处理rc发版,这样保证发布候选版的稳定。所以发布 release的时机,就是创建merge_request
从开发分支合并到融合分支时,如果提交的代码包含有版本号的变更,自动触发持续集成的发布组件流程,发布release组件。
-
gitlab-ci配置发布组件阶段
gitlab-ci.yml:
image: inovex/gitlab-ci-android ####################### #gitlab-runner 配置文件# ####################### variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" before_script: # 配置 jdk 17/11/8 - export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ... # 获取权限 ... - chmod +x ./publish-module stages: - publishModule #定义发布组件阶段,在所有阶段之前 - build - reinforceAndChannel - deploy # gradle.properties文件变动时,编译前自动发布release module publish_release_module: stage: publishModule only: changes: - gradle.properties refs: - merge_requests variables: - $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^assemble(_[A-Za-z0-9]+)?$/ script: - echo "gradle.properties文件变动,执行发布release module逻辑" - export publishMode="release" - ./publish-module tags: - android ...
publish-module文件:
dos#!/usr/bin/env bash ############################ #自动编译时自动发布debug module # ############################ #任何命令失败时停止执行 set -e # 修改 FORCE_USE_DEPENDENCY_MODE 为 local echo 'start exec publish-module.sh ...' echo "publishMode is ${publishMode} ..." publishExec='publishDebugPublicationToMavenRepository' if [ "$publishMode" = "release" ]; then publishExec=publishReleasePublicationToMavenRepository fi echo "publishExec is ${publishExec} ..." echo '修改 gradle.properties 中 FORCE_USE_DEPENDENCY_MODE 为 local ...' sed -i "s/FORCE_USE_DEPENDENCY_MODE=.*$/FORCE_USE_DEPENDENCY_MODE=local/;" gradle.properties echo '修改成功,发布 module ...' ./gradlew $publishExec echo '发布成功,修改 gradle.properties 中 FORCE_USE_DEPENDENCY_MODE 为 remote ...' sed -i "s/FORCE_USE_DEPENDENCY_MODE=.*$/FORCE_USE_DEPENDENCY_MODE=remote/;" gradle.properties echo '修改成功' echo 'end exec publish-module.sh ...'
发布代码
maven-publish.gradle
:javaapply plugin: "maven-publish" apply from: "$rootDir/gradle/maven-utils.gradle" afterEvaluate { publishing { publications { ... release(MavenPublication) { if (isAndroidEnv(project)) { from project.components.release } else { from project.components.java } pom { ... url = NEXUS_URL withXml { ... } } } } repositories { maven { allowInsecureProtocol true //根据编译命令区分debug/release,上传组件到不同的仓库 if (isReleaseBuild()) { url = NEXUS_URL } else { url = NEXUS_DEBUG_URL } ... } } } }
gradle.properties:
#配置是否全局强制开启依赖本地组件,优先级高于module内的配置. #会影响使用 projectCompat 引入的所有依赖 #[remote]:使用远程依赖;[local]:使用本地依赖;[none]:不使用全局强制依赖模式 FORCE_USE_DEPENDENCY_MODE=none
-
发布 release 组件
修改版本号提交代码,创建merge_request,触发gitlab-ci发布组件stage
自动依赖远程组件
发布成功后,打包时自动依赖远程组件,实现方式是:
-
在gradle中封装组件依赖方法
projectCompat
,根据全局配置参数FORCE_USE_DEPENDENCY_MODE
来处理是依赖本地源码还是远程aar。
app的build.gradle依赖组件:... dependencies { ... addNewComponent projectCompat(':component:home') ... }
/gradle/ext.gradle中封装
projectCompat
方法:... //region 组件依赖工具方法,根据配置自动处理本地或远程依赖 //根据是否为远程依赖设置依赖远程库还是本地库 ext.projectCompat = { name -> def realName = name.substring(name.lastIndexOf(":") + 1) def realDependency if (isMaven(name)) { realDependency = "$NEXUS_PROJ_GROUP_ID:${realName}:${mavenVersion(name)}" } else { realDependency = project(name) } println("projectCompat>>name=${name}>>realName=${realName}>>realDependency=$realDependency") return realDependency } //判断当前组件是否为远程依赖 ext.isMaven = { name -> if (FORCE_USE_DEPENDENCY_MODE == "local") return false else if (FORCE_USE_DEPENDENCY_MODE == "remote") return true Properties properties = new Properties() def file = new File("$rootDir${name.replace(":", "/")}/gradle.properties") if (file.exists()) { InputStream inputStream = file.newDataInputStream() properties.load(inputStream) def str = properties.getProperty('USE_REMOTE_DEPENDENCY') if (str == null) { return false } else { return Boolean.parseBoolean(str) } } return false } //读取各module中gradle.property文件中库版本 ext.mavenVersion = { name -> println("mavenVersion::${name}") def str = NEXUS_PROJ_VERSION if (str == null) { throw Exception(file.path + " NEXUS_PROJ_VERSION == null") } else { return str } } //endregion ...
-
由于发布组件时,debug/release是发布到不同的仓库了,所以在
project根目录的build.gradle
文件内,需要根据编译命令动态切换debug/release仓库地址。
project根目录build.gradle:... allprojects { apply from: "$rootDir/gradle/maven-utils.gradle" repositories { ... //本地私服仓-自有组件,根据编译命令动态切换debug/release仓库 maven { allowInsecureProtocol true if (isReleaseBuild()) { url NEXUS_URL } else { url NEXUS_DEBUG_URL } } ... } } ...
-
在持续集成的打包流程中,执行编译命令前通过命令修改
FORCE_USE_DEPENDENCY_MODE
为remote
,指定发版编译时,使用远程组件依赖
gitlab-ci.yml:... # 利用标签打测试包(beta),格式:"x.x.x-beta.x", debug 包,面向测试人员,api 是测试环境 build_tag_beta: stage: build only: - /^[\d]+\.[\d]+\.[\d]+-beta\.[\d]+$/ script: - sed -i "s/FORCE_USE_DEPENDENCY_MODE=.*$/FORCE_USE_DEPENDENCY_MODE=remote/;" gradle.properties # 修改 FORCE_USE_DEPENDENCY_MODE 为 remote - ./gradlew assembleDebug artifacts: paths: - app/build/outputs/apk/debug/*.apk tags: - android ...
开发流程总结
- 开发阶段时,develop分支正常开发提交代码。
- 开发结束需要发布测试版时:
- 修改
gradle.properties
中的版本号字段NEXUS_PROJ_VERSION
提交到仓库,触发自动发布debug组件的流程 - 打标签
x.x.x-beta.x
,触发自动发版流程,自动修改FORCE_USE_DEPENDENCY_MODE
开启强制依赖远程组件,之后自动编译发版
- 修改
- 测试结束需要发布发布候选版时:
- 创建develop合并到assemble分支的merge_request,触发自动发布release组件流程
- 合并merge_request,打标签
x.x.x-rc.x
触发自动发布发布候选版
- 发布候选版测试结束需要正式发版时
- 创建assemble合并到master分支的merge_request,等待编译完成后合并
- 在master分支打标签
x.x.x
触发自动发版流程