基于Travis CI搭建Android持续集成以及自动打包发布流程

前言

最近项目进度比较轻松,闲来自己研究一些感兴趣的技术,恰好这两天研究了一下Travic CI, 用于Android持续集成以及自动打包,话不多说,下面大家就跟我一起踏入Travis CI的奇妙旅行

背景

我们在开发Android项目的时候,大致的流程是这样:
image

Question:这些步骤能否简化,能否自动化?
懒是码农的美德,作为资深码农应该善于用工具提高自己的工作效率,能自动化的要自动化。那么今天要讲的Travis CI就能简化我们的工作,上述流程如果使用Travis CI那么工作流程是这样的:
image
Tag提交后,Travis CI会自动编译代码,生成apk文件,并发到Github和相应地其他渠道(fir.im, 蒲公英等),分发完成后,会邮件通知参与测试的人员。如此一来,作为码农,只要安心Coding和打Tag就好了,轻松愉快啊😄,下面我们就来着重介绍Travis

什么是Travis CI

简单来说它是用来做持续集成的工具,可以为你自动构建、测试、打包等等,极大的简化了工作流程。它对Github的支持特别好,链接到你在Github上的项目以后,每当你把测试通过后的代码提交到master去,它会pull你的代码并按照你的要求构建执行

如何安装Travis CI

首先需要安装Ruby, 你可以通过运行ruby -v 检查系统是否安装Ruby:

1
2
$ ruby -v
ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15]

然后运行:

1
$ gem install travis -v 1.8.0 --no-rdoc --no-ri

安装完毕后,检验一下:

1
2
$ travis version
1.8.0

配置Android项目,启用Travis CI

先添加Travis CI到Github repo:

image

然后按照官网说法是大致三步走:

image

先选择要开启Travis CI的项目,将开关设为On即可:

image

在项目的根目录下新建一个文件.travis.yml,如下是一个Android 项目的配置模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
language: android
android:
components:
# Uncomment the lines below if you want to
# use the latest revision of Android SDK Tools
# - platform-tools
# - tools
# The BuildTools version used by your project
- build-tools-19.1.0
# The SDK version used to compile your project
- android-19
# Additional components
- extra-google-google_play_services
- extra-google-m2repository
- extra-android-m2repository
- addon-google_apis-google-19
# Specify at least one system image,
# if you need to run emulator(s) during your tests
- sys-img-armeabi-v7a-android-19
- sys-img-x86-android-17

当Travis CI准备好我们所需要的环境后,将自动运行yml文件script部分所设置的指令,上例中运行的是./gradlew assembleRelease,运行成功的话会在项目的主模块下生成build/outputs/apk/app-release.apk

【备注】Travis CI目前有2个网站:如果是开源项目,直接进入travis-ci.org即可,如果是私有付费项目,则需要进入travis-ci.com,2个网站除了域名外所有的界面及操作几乎一模一样

Android项目自动化构建的密码和证书安全问题

安卓项目发布需要证书文件和若干密码,但无论是开源项目还是私有项目,任何时候都不应该将原始证书或密码放入代码库(原则上来讲证书和密码也不应该交于开发人员,而应该只能通过发布服务器进行编译)。Travis CI为此提供了2种解决方案,一种是对敏感信息、密码、证书等进行对称加密,在CI构建环境时解密,另一种是将密码等通过Travis CI的控制台(即网站)设置为构建时的环境变量。

由于前者会在Travis控制台生成一对环境变量,所以我的做法是尽量选择后者,但由于Travis控制台无法上传文件,因此涉及到文件加密的部分,则只能选择前者。
说了这么多,首先还是需要先对编译脚本进行改造,如果不考虑安全问题,项目的build.gradle文件可能会是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
android {
signingConfigs {
releaseConfig {
storeFile file("../xx.keystore")
storePassword "123456"
keyAlias "key_alias"
keyPassword "123456"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.releaseConfig
}
}
}

而我们最终要的效果,还是希望一份编译脚本既可以用于开发环境,也可以在CI环境下使用,在Travis CI中,可以通过点击项目名称 -> Settings -> Environment Variables中设置环境变量,比如我们可以针对上面的配置,分别设置KEYSTORE_PASS、ALIAS_NAME、ALIAS_PASS三个环境变量:
image
在Travis CI环境下可以通过System.getenv()获得这些环境变量
本地开发环境中,我的做法是将这几个变量加到gradle.properties文件中,这样就可以在build.gradle内直接使用了。下面是开发环境的gradle.properties:

1
2
3
KEYSTORE_PASS=123456
ALIAS_NAME=key_alias
ALIAS_PASS=123456

这样一来build.gradle就变成了:

1
2
3
4
5
6
releaseConfig {
storeFile file("../xx.keystore")
storePassword project.hasProperty("KEYSTORE_PASS") ? KEYSTORE_PASS : System.getenv("KEYSTORE_PASS")
keyAlias project.hasProperty("ALIAS_NAME") ? ALIAS_NAME : System.getenv("ALIAS_NAME")
keyPassword project.hasProperty("ALIAS_PASS") ? ALIAS_PASS : System.getenv("ALIAS_PASS")
}

接下来处理证书文件,为了方便文件加密等功能,Travis CI提供了一个基于ruby的CLI命令行工具,可以直接使用gem安装:

1
gem install travis

安装后进入安卓项目根目录,尝试对证书文件加密:

1
travis encrypt-file xx.keystore --add

travis encrypt-file指令会做几件事情:
1.在Travis CI控制台自动生成一对密钥: encrypted_59c5087c0788_keyencrypted_59c5087c0788_iv
image
2.基于密钥通过openssl对文件进行加密,上例中会项目根目录生成xx.keystore.enc文件
3.在.travis.yml中自动生成Travis CI环境下解密文件的配置,上例运行后可以看到.travis.yml中多了几行:

1
2
3
4
before_install:
- gem install fir-cli
- openssl aes-256-cbc -K $encrypted_59c5087c0788_key -iv $encrypted_59c5087c0788_iv
-in xx.keystore.enc -out xx.keystore -d

最后在.gitignore中忽略xx.keystore以及gradle.properties

Travis Ci自动发布安装apk文件到Github Release

Travis CI的script部分运行成功后,可以通过配置文件进入到发布阶段。下面是一个Travis CI发布的示例:

1
2
3
4
5
6
7
8
deploy:
provider: releases
user: "GITHUB USERNAME"
password: "GITHUB PASSWORD"
file: app/build/outputs/apk/app-release.apk
skip_cleanup: true
on:
tags: true
  • provider:发布目标为Github Release
  • Github用户名和密码,因为Travis CI要上传APK文件,因此需要有Github项目的写入权限
  • file: 发布文件,输入文件路径即可
  • skip_cleanup: 默认情况下Travis CI在完成编译后会清除所有生成的文件,因此需要将skip_cleanup设置为true来忽略此操作。
  • on: 发布的时机,这里配置为tags: true,即只在有tag的情况下才发布。

这边直接暴露了Github密码是我们更加不能接受的。更好的做法是在Github -> settings -> Personal access tokens 生成一个只能访问当前项目并只有读取权限的Github Access Token,并通过Travis CI将Access Token加密。好在Travis CLI中已经可以通过一行指令做好这一切:

1
travis setup release

根据提示填写上述配置项目的信息后,Travis CLI会自动在.travis.yml文件中生成好所有的配置项:

1
2
3
4
5
6
7
8
9
deploy:
provider: releases
api_key:
secure: XXX
file: app/build/outputs/apk/app-release.apk
skip_cleanup: true
on:
tags: true
all_branches: true

其中api_key下的secure就是加密后的Access Token。

自动发布APK到fir.im

自动发布到Github对于开发人员已经足够,但是考虑到项目实际需要以及国情,还是有必要选择一个国内的App分发服务,fir.im是不错的选择,不但允许游客下载,还提供了二维码等更适合对接手机的功能,国内下载速度也很快。由于fir.im提供了比较方便的CLI工具,因此本文以fir.im为例,在.travis.yml中添加以下几行:

1
2
3
4
before_install:
- gem install fir-cli
after_deploy:
- fir p app/build/outputs/apk/app-release.apk -T $FIR_TOKEN -c "`git cat-file tag $TRAVIS_TAG`"

即在环境构建阶段安装fir-cli,在发布成功后通过fir命令行工具将apk上传到fir。
其中$FIR_TOKEN可以在fir.im的用户->API Token中找到,然后在Travis CI控制台中创建环境变量FIR_TOKEN并粘贴即可。
image

总结

其实所有的yml文件配置不到30行,就能利用Travis CI进行自动化持续集成和打包。最后我们回顾一下Travis CI的工作流:
提交代码:

1
2
3
git add .
git commit -m "注释"
git push origin

打Tag:

1
2
git tag -a v0.0.1-alpha.1 -m "Tag注释,说清楚这个版本的主要改动,也可以省略-m参数直接写长文本"
git push origin --tags

这个是我集成了Travis CI的项目地址,供参考:
https://github.com/archmages/DYWeather