前言
最近項目進度比較輕松,閑來自己研究一些感興趣的技術(shù),恰好這兩天研究了一下Travic CI, 用于Android持續(xù)集成以及自動打包,話不多說,下面大家就跟我一起踏入Travis CI的奇妙旅行
背景
我們在開發(fā)Android項目的時候,大致的流程是這樣:

Question:這些步驟能否簡化,能否自動化?
懶是碼農(nóng)的美德,作為資深碼農(nóng)應(yīng)該善于用工具提高自己的工作效率,能自動化的要自動化。那么今天要講的Travis CI就能簡化我們的工作,上述流程如果使用Travis CI那么工作流程是這樣的:

Tag提交后,Travis CI會自動編譯代碼,生成apk文件,并發(fā)到Github和相應(yīng)地其他渠道(fir.im, 蒲公英等),分發(fā)完成后,會郵件通知參與測試的人員。如此一來,作為碼農(nóng),只要安心Coding和打Tag就好了,輕松愉快啊??,下面我們就來著重介紹Travis
什么是Travis CI
簡單來說它是用來做持續(xù)集成的工具,可以為你自動構(gòu)建、測試、打包等等,極大的簡化了工作流程。它對Github的支持特別好,鏈接到你在Github上的項目以后,每當(dāng)你把測試通過后的代碼提交到master去,它會pull你的代碼并按照你的要求構(gòu)建執(zhí)行
如何安裝Travis CI
首先需要安裝Ruby, 你可以通過運行ruby -v 檢查系統(tǒng)是否安裝Ruby:
$ ruby -v
ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15]
然后運行:
$ gem install travis -v 1.8.0 --no-rdoc --no-ri
安裝完畢后,檢驗一下:
$ travis version
1.8.0
配置Android項目,啟用Travis CI
先添加Travis CI到Github repo:

然后按照官網(wǎng)說法是大致三步走:

先選擇要開啟Travis CI的項目,將開關(guān)設(shè)為On即可:

在項目的根目錄下新建一個文件.travis.yml,如下是一個Android 項目的配置模板:
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
當(dāng)Travis CI準(zhǔn)備好我們所需要的環(huán)境后,將自動運行yml文件script部分所設(shè)置的指令,上例中運行的是./gradlew assembleRelease,運行成功的話會在項目的主模塊下生成build/outputs/apk/app-release.apk
【備注】Travis CI目前有2個網(wǎng)站:如果是開源項目,直接進入travis-ci.org即可,如果是私有付費項目,則需要進入travis-ci.com,2個網(wǎng)站除了域名外所有的界面及操作幾乎一模一樣
Android項目自動化構(gòu)建的密碼和證書安全問題
安卓項目發(fā)布需要證書文件和若干密碼,但無論是開源項目還是私有項目,任何時候都不應(yīng)該將原始證書或密碼放入代碼庫(原則上來講證書和密碼也不應(yīng)該交于開發(fā)人員,而應(yīng)該只能通過發(fā)布服務(wù)器進行編譯)。Travis CI為此提供了2種解決方案,一種是對敏感信息、密碼、證書等進行對稱加密,在CI構(gòu)建環(huán)境時解密,另一種是將密碼等通過Travis CI的控制臺(即網(wǎng)站)設(shè)置為構(gòu)建時的環(huán)境變量。
由于前者會在Travis控制臺生成一對環(huán)境變量,所以我的做法是盡量選擇后者,但由于Travis控制臺無法上傳文件,因此涉及到文件加密的部分,則只能選擇前者。
說了這么多,首先還是需要先對編譯腳本進行改造,如果不考慮安全問題,項目的build.gradle文件可能會是這樣:
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
}
}
}
而我們最終要的效果,還是希望一份編譯腳本既可以用于開發(fā)環(huán)境,也可以在CI環(huán)境下使用,在Travis CI中,可以通過點擊項目名稱 -> Settings -> Environment Variables中設(shè)置環(huán)境變量,比如我們可以針對上面的配置,分別設(shè)置KEYSTORE_PASS、ALIAS_NAME、ALIAS_PASS三個環(huán)境變量:

在Travis CI環(huán)境下可以通過
System.getenv()獲得這些環(huán)境變量本地開發(fā)環(huán)境中,我的做法是將這幾個變量加到
gradle.properties文件中,這樣就可以在build.gradle內(nèi)直接使用了。下面是開發(fā)環(huán)境的gradle.properties:
KEYSTORE_PASS=123456
ALIAS_NAME=key_alias
ALIAS_PASS=123456
這樣一來build.gradle就變成了:
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安裝:
gem install travis
安裝后進入安卓項目根目錄,嘗試對證書文件加密:
travis encrypt-file xx.keystore --add
travis encrypt-file指令會做幾件事情:
1.在Travis CI控制臺自動生成一對密鑰: encrypted_59c5087c0788_key和encrypted_59c5087c0788_iv

2.基于密鑰通過openssl對文件進行加密,上例中會項目根目錄生成
xx.keystore.enc文件3.在.travis.yml中自動生成Travis CI環(huán)境下解密文件的配置,上例運行后可以看到.travis.yml中多了幾行:
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自動發(fā)布安裝apk文件到Github Release
Travis CI的script部分運行成功后,可以通過配置文件進入到發(fā)布階段。下面是一個Travis CI發(fā)布的示例:
deploy:
provider: releases
user: "GITHUB USERNAME"
password: "GITHUB PASSWORD"
file: app/build/outputs/apk/app-release.apk
skip_cleanup: true
on:
tags: true
-
provider:發(fā)布目標(biāo)為Github Release - Github用戶名和密碼,因為Travis CI要上傳APK文件,因此需要有Github項目的寫入權(quán)限
-
file: 發(fā)布文件,輸入文件路徑即可 -
skip_cleanup: 默認(rèn)情況下Travis CI在完成編譯后會清除所有生成的文件,因此需要將skip_cleanup設(shè)置為true來忽略此操作。 -
on: 發(fā)布的時機,這里配置為tags: true,即只在有tag的情況下才發(fā)布。
這邊直接暴露了Github密碼是我們更加不能接受的。更好的做法是在Github -> settings -> Personal access tokens 生成一個只能訪問當(dāng)前項目并只有讀取權(quán)限的Github Access Token,并通過Travis CI將Access Token加密。好在Travis CLI中已經(jīng)可以通過一行指令做好這一切:
travis setup release
根據(jù)提示填寫上述配置項目的信息后,Travis CLI會自動在.travis.yml文件中生成好所有的配置項:
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。
自動發(fā)布APK到fir.im
自動發(fā)布到Github對于開發(fā)人員已經(jīng)足夠,但是考慮到項目實際需要以及國情,還是有必要選擇一個國內(nèi)的App分發(fā)服務(wù),fir.im是不錯的選擇,不但允許游客下載,還提供了二維碼等更適合對接手機的功能,國內(nèi)下載速度也很快。由于fir.im提供了比較方便的CLI工具,因此本文以fir.im為例,在.travis.yml中添加以下幾行:
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`"
即在環(huán)境構(gòu)建階段安裝fir-cli,在發(fā)布成功后通過fir命令行工具將apk上傳到fir。
其中$FIR_TOKEN可以在fir.im的用戶->API Token中找到,然后在Travis CI控制臺中創(chuàng)建環(huán)境變量FIR_TOKEN并粘貼即可。

總結(jié)
其實所有的yml文件配置不到30行,就能利用Travis CI進行自動化持續(xù)集成和打包。最后我們回顧一下Travis CI的工作流:
提交代碼:
git add .
git commit -m "注釋"
git push origin
打Tag:
git tag -a v0.0.1-alpha.1 -m "Tag注釋,說清楚這個版本的主要改動,也可以省略-m參數(shù)直接寫長文本"
git push origin --tags
這個是我集成了Travis CI的項目地址,供參考:
https://github.com/archmages/DYWeather