提高Android组件化协作效率--Jenkins+Artifactory+外部gradle

本篇只针对gradle外部配置做详细说明,诸如组件化,jenkins,artifactory等细节不做阐述

以我做过的项目为例


image

每个组件都独立维护大量的android,第三方,及其他组件的依赖,项目集成时,各个组件依赖版本不同还会引起冲突。

当组件上传新的版本,相依赖的组件还要更新。比如公共库从1.0-snapshot修改到1.1-snapshot。那么交易明细,交接班,对账单就要都进行修改,非常的麻烦。这时就非常需要一个统一的配置入口。

“”修改一次到处运行“是我们这次的目标,好了,废话铺垫完,开始进入正题。

一.设计思路

  1. gradle文件部署在远端web服务器下
  2. 每个组件依赖远端gradle文件
  3. 打包aar上传至artifactory
  4. jenkins通过脚本监控artifactory中是否有新的组件aar,如果有将gradle文件中对应的版本更新为最新

优点:
解放了双手

  1. 只需要修改远端gradle,每个组件都会同时共享,只需操作一下同步
  2. 组件发布新版本,不需要手动去修改远端gradle,jenkins会自动修改(过去的方式只要有一个组件发布新版本,开发人员要通知依赖方去升级,每个依赖方都要手动修改版本,麻烦而且容易忘记修改,不能及时发现问题)
image

二.环境

本文只针对jenkins+artifactory举例,如果环境不同,可以举一反三,环境不重要,思路才是终解

  1. jenkins--持续构建,编写自动化脚本,同步外部gradle文件

  2. artifactory--本地私服,组件化后的aar发布到此处

三.提取gradle通用配置

gradle大致结构如下图

apply plugin: 'com.android.application'

android {
compileSdkVersion 28
defaultConfig {
    applicationId "com.qianbao.ipos.myapplication"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

minSdkVersion,targetSdkVersion,compileSdkVersion,dependencies中的内容都可以作为统一的配置项,如果你项目中有环境配置,譬如生产环境,测试环境,那么这些不同环境的域名或其他项都可作为配置项

四.自定义gradle

1.根节点ext

2.次节点是为了保证层次清晰,进行了分组

ext {
version = [

        compileSdk                 : 28,
        buildTools                 : "28.0.3",
        minSdk                     : 21,
        targetSdk                  : 28,
        router                     : "1.5.8.0-RELEASE",
        biznetwork : "1.5.9.0-RELEASE",
        //Android
        androidSupport             : "28.0.0",
        androidEspresso            : "3.0.2",
        //腾讯
        tencentXinge               : "4.3.1-beta",
        tencentWup                 : "1.0.0.E-release",
        tencentMid                 : "4.0.7-Release",
        tencentJg                  : "1.1",
]

androidDependencies = [
        //support,共用同一版本        
        animatedVectorDrawable: "com.android.support:animated-vector-drawable:${version.androidSupport}",
        supportVectorDrawable : "com.android.support:support-vector-drawable:${version.androidSupport}",
        supportCoreUtils      : "com.android.support:support-core-utils:${version.androidSupport}",
]

testDependencies = [
        //espresso,共用同一版本
        espressoCore   : "com.android.support.test.espresso:espresso-core:${version.androidEspresso}",
        espressoIntents: "com.android.support.test.espresso:espresso-intents:${version.androidEspresso}",
        espressoContrib: "com.android.support.test.espresso:espresso-contrib:${version.androidEspresso}",
]
tencentDependencies = [
        xinge: "com.tencent.xinge:xinge:${version.tencentXinge}",
        wup  : "com.tencent.wup:wup:${version.tencentWup}",
        mid  : "com.tencent.mid:mid:${version.tencentMid}",
        jg   : "com.tencent.jg:jg:${version.tencentJg}"
]

iposDependencies = [
        router        : "com.qianbao.ipos:router:${version.router}",
        datagramparser: "com.qianbao.ipos:datagramparser:${version.datagramparser}",
]
}

五.如何使用

1.在项目根目录下的build.gradle中添加外部gradle

我的项目中的外部gradle是部署在artifactory的tomcat下(这样就省去了单独部署服务端),artifactory安装目录/tomcat/webapps/artifactory

apply from: 'http://{url}/artifactory/dependencies_1.5.9.gradle'
buildscript {
repositories {
    jcenter()
    google()
    maven { url 'https://dl.bintray.com/umsdk/release' }
}
dependencies {
    classpath 'com.android.tools.build:gradle:3.3.0'
    classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.9.0"
}
}

2.在app/moudle下添加如下配置

看着是不是很清晰简洁

apply plugin: 'com.android.library'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'
def ext = rootProject.extensions.getByName("ext")
def versionConfig = ext.version
def testDependencies = ext.testDependencies
def androidDependencies = ext.androidDependencies
def tencentDependencies = ext.tencentDependencies
def iposDependencies = ext.iposDependencies
def pushPublishing = '1.5.9.18-SNAPSHOT'

android {
compileSdkVersion versionConfig.compileSdk
buildToolsVersion versionConfig.buildTools
defaultConfig {
    minSdkVersion versionConfig.minSdk
    targetSdkVersion versionConfig.targetSdk
}
}

dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')

//测试相关引用
androidTestImplementation testDependencies.junit
androidTestImplementation testDependencies.workTesting


//android包
implementation androidDependencies.appcompatV7
implementation androidDependencies.workRuntime

//腾讯推送
implementation tencentDependencies.xinge
implementation tencentDependencies.wup
implementation tencentDependencies.mid
implementation tencentDependencies.jg

//ipos组件
implementation iposDependencies.router
implementation iposDependencies.datagramparser
implementation iposDependencies.biznetwork
}

六.自动更新外部gradle

到第五步其实已经完成了引用外部gradle的功能,但还存在一个问题,你要不停的去远程手动修改gradle文件,如果项目的参与人多还会造成其他的状况。减少人工的参与就是降低产生bug的风险。

下面将介绍通过jenkins如何完成自动更新,如果不了解jenkins请先做一下功课,否则下面对你没有启发作用

1.jenkins为修改外部gradle文件,提供了可视化环境

2.项目的组件由脚本自动完成,其余配置由界面化配置完成

3.jenkins配置

(1)参数部分

(2)触发器部分

通过Build Authorization Token Root Plugin插件,artifactory插件,都可以满足功能,但经过实测有些需求并不能满足

这里我采用了定时触发,每周1-周五,9点-17点,每20分钟触发一次

(3)shell脚本部分

思路:
私服Artifactory分为release和snapshot版,同版本以release优先,不同版本以最新的版本为准,将所有组件的版本解析后将外部gradle对应的组件版本修改

1)先遍历Artifactory所有libs-release-local下的release组件,通过下方的url访问可以解析到每个组件下的maven-metadata文件,标签对应的值就是该组件下的最新版本,如果有当前版本的构件,就更新gradle文件,并将记录保存下来,作为下次是否更新的依据

2)以同样的方式遍历snapshot

#!/bin/sh -x

currentVersion=`echo ${JOB_NAME#*-}`
gradleFile=/opt/jfrog/artifactory/tomcat/webapps/artifactory/dependencies_${currentVersion}.gradle
artifactoryUrl="http://192.168.1.138:8081/artifactory"
group="com/qianbao/ipos"
backupFile=$WORKSPACE/${JOB_NAME}.backup

function syncRelease(){
releasePath=/var/opt/jfrog/artifactory/backup/backup-daily/current/repositories/libs-release-local/com/qianbao/ipos
releaseLibPath="libs-release-local"
releaseDirectory=$( ls -l  $releasePath | grep -v "metadata" | grep "^d" | awk '{print $9}')
count=0

if [ ! -f "$backupFile" ];then
 touch $backupFile
fi

echo "==========================================Begin loop the Release lib of Artifactory=========================================="
for direcotryName in $releaseDirectory
  do
  checkUrl=$artifactoryUrl/$releaseLibPath/$group/$direcotryName/maven-metadata.xml
   metadataXml=$(curl -s $checkUrl)
   versionName=$(echo "$metadataXml" | awk -F '[<>]' '//{print $3}')
  last=$(awk -F '[<>]' '/<${direcotryName}>/{print $3}' $backupFile)
   versionCode=$(expr $versionName : "\([0-9]\{1,2\}\(\.[0-9]\{1,2\}\)\{2\}\).*")
   if [ $currentVersion == $versionCode ] && [ "$versionName" != "$last" ];then
  sed -i "s/$direcotryName\s*:\s*\"[0-9].*/$direcotryName : \"$versionName\",/" $gradleFile
  sed -i "/<$direcotryName>.*<\/$direcotryName>/d" $backupFile
   echo "<$direcotryName>$versionName" >> $backupFile
   echo "==========================================${direcotryName} has been update to version:${versionName}=========================================="
   updateArray[count]=$direcotryName;
    count=`expr $count + 1`
  else
    echo "==========================================The release lib of Artifactory is unchanged =========================================="
 fi
 done

}


function syncSnapshot(){
snapshotPath=/var/opt/jfrog/artifactory/backup/backup-daily/current/repositories/libs-snapshot-local/com/qianbao/ipos
 snapshotDirectory=$( ls -l  $snapshotPath | grep -v "metadata" | grep "^d" | awk '{print $9}')
 snapshotLibPath="libs-snapshot-local"
 echo "==========================================Begin loop the Snapshot lib of Artifactory=========================================="
 for direcotryName in $snapshotDirectory
  do
   checkUrl=$artifactoryUrl/$snapshotLibPath/$group/$direcotryName/maven-metadata.xml
   metadataXml=$(curl -s $checkUrl)
   versionName=$(echo "$metadataXml" | awk -F '[<>]' '//{print $3}')
  versionCode=$(expr $versionName : "\([0-9]\{1,2\}\(\.[0-9]\{1,2\}\)\{2\}\).*")
  last=$(awk -F '[<>]' '/<$direcotryName>/{print $3}' $backupFile)
  if [ "${updateArray[*]}" != "" ];then
 exsit=$(echo "${updateArray[*]}" | grep -w "$direcotryName")
 if [ "$exsit" != "" ] || [ "$versionName" == "$last" ];then
 echo "==========================================The snapshot lib of Artifactory is unchanged =========================================="
 continue
 fi
 fi

  if [ $currentVersion == $versionCode ];then
 sed -i "s/$direcotryName\s*:\s*\"[0-9].*/$direcotryName : \"$versionName\",/" $gradleFile
 sed -i "/<$direcotryName>.*<\/$direcotryName>/d" $backupFile
 echo "<$direcotryName>$versionName" >> $backupFile
 echo "==========================================${direcotryName} has been update to version:${versionName} =========================================="
 fi
 done

}

#########通过Aritifactory上最新版本,同步配置文件
#########规则:条件1:release有与当前版本一致的包优先替换,条件2:snapshot有与当前版本一致的包替换,如果release与snapshot冲突,release优先,条件3:两者都不满足则不替换

    #########同步release库
syncRelease
#########同步snapshot库
syncSnapshot

你可能感兴趣的:(提高Android组件化协作效率--Jenkins+Artifactory+外部gradle)