前言:
随着公司项目的不断增大,开发人员的不断增加,仅用git来进行代码管理、在开发的机器上手工打apk包通过IM工具提交给QA进行测试的开发流程已不再合适,效率及规范性急需提高。
经讨论,决定在公司android项目中使用MAVEN + JENKINS + ARTIFACTORY组合来进行开发流程优化
本文主要介绍在android studio上使用maven私服的实践过程及踩过的一些坑,最终配置在 另一篇文章 中贴出。
相关工具的配置不是本文的重点,需要的同学可以通过下面的传送门了解更多:
maven : 一个项目构建管理工具
公共库的module全部发布到公司maven私服中,app通过添加maven依赖的方式使用
maven的更多介绍请看 这里 , Maven官方文档jenkins: 一种开源的持续集成系统
自动构建打包,通过配置让QA在jenkins上通过点击按钮打各app不同环境的apk包进行测试
jenkins的安装与配置可以参考 这里 , Jenkins官网artifactory: 一款maven私服构建&管理工具
用来搭建并管理我们的maven私服仓库
artifactory的使用方式参考文档: 30分钟搭建一个android的私有Maven仓库 , 英文原文
第一步:使用jenkins
问题:开发电脑上可以正常打包的命令在jenkins无法正常工作
可以通过很少的配置让jenkins拉取git上的代码并执行打包的工作,但在实际的配置过程中,发现我们的代码结构并不适合这种方式
原因:
我们的代码结构如下如:
---------------------------------------------------------------------
| CommonLib | business1 | business2 |
| lib_module_1 | app_1 | app_1 |
| lib_module_... | app_... | app_... |
| lib_module_n | app_n | app_n |
---------------------------------------------------------------------
公共lib库及不同的业务线分别在不同android studio工程中,git地址不同,module采用相对地址引用的方式进行依赖,jenkins无法进行配置
解决方式
module的依赖方式改为maven,搭建maven私服
第二步:使用artifactory搭建maven私服
参考的实现方式是: 30分钟搭建一个android的私有Maven仓库
问题1. 每个module的build.gradle中都要进行配置
设置packageName、libraryVersion、artifactory配置等
publishing {
publications {
aar(MavenPublication) {
groupId packageName
version = libraryVersion
artifactId project.getName()
// Tell maven to prepare the generated "* .aar" file for publishing
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
}
}
解决方式
做到以下配置后,升级module版本只需维护gradle.properties中的版本号,并在命令行调用./deploy.sh lib_...
即可
- 同一个工程下的module使用相同的groupId,如:com.xiwei.commonlib
- 在gradle.properties中统一配置每个module的maven版本号,module之间的依赖,版本号直接引用gradle.properties中的变量
- 在module的build.gradle中,defaultConfig下添加versionName变量,值为gradle.properties中定义的变量
- 新建artifactory.gradle
subprojects {
//artifactory相关
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'
def myGroupId = 'com.xiwei.commonlib'
publishing {
publications {
aar(MavenPublication) {
if (!project.hasProperty('android')){
return
}
def libVersion = "${project.android.defaultConfig.versionName}"
groupId myGroupId
version = libVersion
artifactId project.getName()
// Tell maven to prepare the generated "*.aar" file for publishing
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
}
}
}
- 在工程根目录的build.gradle中添加一行
apply from: "artifactory.gradle"
- 新建deploy脚本:
deploy.sh
#!/bin/sh
./gradlew :$1:clean :$1:assembleRelease :$1:artifactoryPublish
deploy.bat
@echo off
gradlew :%1:clean :%1:assembleRelease :%1:artifactoryPublish
问题2. module的依赖关系没有传递
例如:当B依赖A,C在依赖B时要同时添加A的依赖,否则会报ClassNotFoundException
原因
使用artifactory进行的deploy时,生成的pom文件([module]/build/publications/aar/pom-default.xml)中未包含依赖信息:dependencies
<?xmlversion="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiwei.commonlib</groupId>
<artifactId>lib_xiwei_common</artifactId>
<version>1.0.5</version>
<packaging>aar</packaging>
</project>
我们需要的pom文件应该长成这样:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiwei.commonlib</groupId>
<artifactId>lib_xiwei_common</artifactId>
<version>1.0.5</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.xiwei.commonlib</groupId>
<artifactId>lib_statistics</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.xiwei.commonlib</groupId>
<artifactId>lib_framework_image</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
解决方式
修改build.gradle中的artifactory配置,原来的配置是
publishing {
publications {
aar(MavenPublication) {
if (!project.hasProperty('android')){
return
}
def libVersion = "${project.android.defaultConfig.versionName}"
groupId myGroupId
version = libVersion
artifactId project.getName()
// Tell maven to prepare the generated "* .aar" file for publishing
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
}
}
修改为(修改方式参考 这里 ):
publishing {
publications {
aar(MavenPublication) {
if (!project.hasProperty('android')){
return
}
def libVersion = "${project.android.defaultConfig.versionName}"
groupId myGroupId
version = libVersion
artifactId project.getName()
// Tell maven to prepare the generated "* .aar" file for publishing
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
}
//The publication doesn't know about our dependencies, so we have to manually add them to the pom
pom.withXml {
//Creating additional node for dependencies
def dependenciesNode = asNode().appendNode('dependencies')
//Defining configuration names from which dependencies will be taken (debugCompile or releaseCompile and compile)
def configurationNames = ["releaseCompile", 'compile']
configurationNames.each { configurationName ->
project.configurations.getByName(configurationName).allDependencies.each {
if (it.group != null && it.name != null) {
println it.group + ":" + it.name + "" + it.version + "," + v_lib_framework_http
println("${it.group}:${it.name}:${it.version}:v_lib_framework_http:${v_lib_framework_http}")
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
//If there are any exclusions in dependency
if (it.excludeRules.size() > 0) {
def exclusionsNode = dependencyNode.appendNode('exclusions')
it.excludeRules.each { rule ->
def exclusionNode = exclusionsNode.appendNode('exclusion')
exclusionNode.appendNode('groupId', rule.group)
exclusionNode.appendNode('artifactId', rule.module)
}
}
}
}
}
}
}
}
问题3. 在windows上deploy时,报错如下:
Execution failed for task ':lib_framework_http:artifactoryPublish'.
> File 'E:\work\CommonLib\lib_framework_http\build\publications\aar\pom-default.xml' does not exist, and need to be published from publication aar
原因
artifactory没有自动调用构建pom的task
解决方式
deploy脚本: deploy.bat
@echo off
gradlew :%1:clean :%1:assembleRelease :%1:artifactoryPublish
修改后的脚本deploy.bat:
@echo off
gradlew :%1:clean :%1:assembleRelease :%1:generatePomFileForAarPublication :%1:artifactoryPublish
问题4. 使用maven方式依赖的lib升级过于频繁
被依赖的lib修改代码后需要升级或强制刷新dependencies才能生效,添加新功能或修改bug时十分不便
原因
使用指定版本或动态版本(如:1.1.+)时,均会产生maven缓存,lib修改后新发布的版本不能及时生效
解决方式
开发阶段使用SNAPSHOT版进行发布及依赖
问题5. 升级版本,发布时报错
Error:Failed to resolve: com.xiwei.commonlib:lib_statistics:1.0.2
原因
为了维护方便,正在发布的module版本号与其它module依赖该module指定的版本号在gradle.properties中为同一个变量,升级发布时,修改了这个变量,执行gradlew命令会检查所有module的dependencies
由于发布尚未开始,maven仓库中并无此版本的module,故而报错
解决方式
- 新建artifactory_version.properties,用来配置发布的module的版本号,build.gradle在发布时,版本信息从该properties文件中读取(为了方便,key为module的name)
- 新建modify.sh/modify.bat, 在module发布结束后,读取artifactory_version.properties中该module的版本并写入到gradle.properties文件中,更新被依赖的版本号
- 修改deploy.sh, 发布后调用modify.sh修改被依赖的版本号
artifactory_version.properties
#发布的类型:snapshot/release
maven_type=snapshot
lib_db=1.0.0
lib_framework_image=1.0.0
...
lib_statistics=1.0.1
lib_xiwei_common=1.0.5
modify.sh
#!/bin/sh
#project的版本定义文件
SOURCE_FILE_NAME="artifactory_version.properties"
#dependencies的版本定义文件
GRADLE_FILE_NAME="gradle.properties"
#从SOURCE_FILE_NAME的key到GRADLE_FILE_NAME的key需要添加的前缀:比如:lib_db -> v_lib_db 前缀为 v_
KEY_PRE_FIX="v_"
function contains
{
STRING_A=$1
STRING_B=$2
if [[ ${STRING_A/${STRING_B}//} == $STRING_A ]]
then
return 0
else
return 1
fi
}
function replace_prop() {
KEY=$1
VALUE=$2
SUFFIX=''
if [[ ${maven_type} = 'snapshot' ]]; then
SUFFIX='-SNAPSHOT'
fi
cat $GRADLE_FILE_NAME | while read LINE
do
contains $LINE "$KEY_PRE_FIX$KEY="
result=$?
if [[ $result = 1 ]]; then
#包含$KEY_PRE_FIX$2=的行内容替换为v_$2=$3
sed -ig "s/$LINE/$KEY_PRE_FIX$KEY=$VALUE$SUFFIX/g" ${GRADLE_FILE_NAME}
rm -f ${GRADLE_FILE_NAME}g
return
fi
done
}
#读取需要拷贝的properties文件
. ${SOURCE_FILE_NAME}
#todo check params
#替换GRADLE_FILE_NAME文件中key为$1的行内容:例如v_lib_db=1.0.0 -> v_lib_db=1.0.1
replace_prop $1 ${!1}
总结
通过一步步踩坑、优化,目前实现的效果为:
- module采用maven的方式进行依赖
- 使用jenkins进行CI,QA可自助打不同环境的apk包
- 只需维护artifactory_version.properties这一个文件进行版本管理即可
- 修改版本信息后,通过调用命令可一键发布所有包,且互相之间的依赖生效
涉及到的文件
整个配置过程中涉及到的文件有:
- artifactory.gradle
- artifactory_version.properties
- build.gradle
- deploy.sh
- modify.sh
- deploy.bat
- modify.bat
使用方式
- module修改后,artifactory_version.properties中对应module及传递依赖的module要进行升级,并将maven_type改为snapshot进行发布
- 测试验收结束后,maven_type改为release进行发布(调用)
- 发布命令
- 发布单个module:
- mac:
./deploy.sh moduleName
- windows:
deploy.bat moduleName
- mac:
- 发布当前工程中的所有module:
- mac:
./deploy.sh -a
- windows:
deploy.bat -a
- mac:
- 发布单个module:
原文地址:http://blog.csdn.net/cdecde111/article/details/51777170