用Jenkins自動(dòng)化構(gòu)建Android和iOS應(yīng)用
背景
隨著業(yè)務(wù)需求的演進(jìn),工程的復(fù)雜度會(huì)逐漸增加,自動(dòng)化的踐行日益強(qiáng)烈。事實(shí)上,工程的自動(dòng)化一直是我們努力的目標(biāo),能有效提高我們的生產(chǎn)效率,***化減少人為出錯(cuò)的概率,實(shí)現(xiàn)一些復(fù)雜的業(yè)務(wù)需求應(yīng)變。
場(chǎng)景如下,公司現(xiàn)在的測(cè)試人員每次需要測(cè)試新版本,都需要開發(fā)人員打包,放到ftp,測(cè)試人員然后從ftp上拷貝到本地(或者用手機(jī)的ES文件管理器),再安裝。尤其臨近發(fā)版的一周,幾乎每天都要新版本。這樣的話,有兩方面的影響:***,打斷了開發(fā)人員的開發(fā)進(jìn)度;第二,開發(fā)人員打包效率低下,尤其是ios,不順的話,總是打的不對(duì)(可能是證書的問題)。
要解決這個(gè)問題,必須實(shí)現(xiàn)移動(dòng)端應(yīng)用的自動(dòng)化構(gòu)建。具體說來就是,使用持續(xù)集成(CI)系統(tǒng)jenkins,自動(dòng)檢測(cè)并拉取***代碼,自動(dòng)打包android的apk和ios的ipa,自動(dòng)上傳到內(nèi)測(cè)分發(fā)平臺(tái)蒲公英上。(接下來,測(cè)試人員只要打開一個(gè)(或多個(gè))固定的網(wǎng)址,掃描一下二維碼,就能下載***的版本了…)
環(huán)境
因?yàn)橐幾gios,所以選擇Mac OSX 10.11.1。
無論是哪個(gè)操作系統(tǒng),jenkins的配置是一樣的。
安裝Jenkins
官網(wǎng)地址:http://jenkins-ci.org/
- // 使用brew安裝
- brew install jenkins
- // 啟動(dòng),直接運(yùn)行jenkins即可啟動(dòng)服務(wù)
- jenkins
默認(rèn)訪問http://localhost:8080/, 可進(jìn)入jenkins配置頁面。
安裝Jenkins相關(guān)插件
點(diǎn)擊系統(tǒng)管理>管理插件>可選插件,可搜索以下插件安裝
- git插件(GIT plugin)
- ssh插件(SSH Credentials Plugin)
- Gradle插件(Gradle plugin) – android專用
- Xcode插件(Xcode integration) – ios專用
新建Job
主頁面,新建 -> 構(gòu)建一個(gè)自由風(fēng)格的軟件項(xiàng)目即可。
對(duì)于類似的項(xiàng)目,可以選擇 -> 復(fù)制已有的Item,要復(fù)制的任務(wù)名稱里輸入其他job的首字符會(huì)有智能提示。
配置git倉庫
如果安裝了git插件,在源碼管理會(huì)出現(xiàn)Git,選中之后:
Repositories -> https://github.com/openproject/ganchai, 如果是ssh還要配置Credentials。
Branch -> */master,選定一個(gè)要編譯的分支代碼。
如下:

如果是私有的倉庫(比如git://xxxxx.git),點(diǎn)擊Credentials – Add,彈出對(duì)話框,配置sshkey最簡(jiǎn)單了:
配置自動(dòng)拉取***代碼
在構(gòu)建觸發(fā)器中,有兩種自動(dòng)拉取代碼并編譯的策略:
1. 設(shè)置Poll SCM,設(shè)置定時(shí)器,定時(shí)檢查代碼更新,有更新則編譯,否則不編譯(我暫時(shí)用的是這個(gè))。
2.也可以設(shè)置Build periodically,周期性的執(zhí)行編譯任務(wù)。
關(guān)于定時(shí)器的格式,我只能從網(wǎng)上摘抄一段稍微靠譜一點(diǎn)的說明:
This field follows the syntax of cron (with minor differences). Specifically, each line consists of 5 fields separated by TABor whitespace: MINUTE HOUR DOM MONTH DOW MINUTE Minutes within the hour (0-59) HOUR The hour of the day (0-23) DOM The day of the month (1-31) MONTH The month (1-12) DOW The day of the week (0-7) where 0 and 7 are Sunday. To specify multiple values for one field, the following operators are available. In the order of precedence, * '*' can be used to specify all valid values. * 'M-N' can be used to specify a range, such as "1-5" * 'M-N/X' or '*/X' can be used to specify skips of X''s value through the range, such as "*/15" in the MINUTE field for"0,15,30,45" and "1-6/2" for "1,3,5" * 'A,B,...,Z' can be used to specify multiple values, such as "0,30" or "1,3,5" Empty lines and lines that start with '#' will be ignored as comments. In addition, @yearly, @annually, @monthly, @weekly, @daily, @midnight, @hourly are supported.
舉兩個(gè)例子:
- // every minute
- * * * * *
- // every 5 mins past the hour
- 5 * * * *
配置gradle – android專用
請(qǐng)ios的朋友們請(qǐng)飄過.
如果安裝gradle插件成功的話,應(yīng)該會(huì)出現(xiàn)下圖的Invoke Gradle script,配置一下:
${WORKSPACE}表示當(dāng)前job下的workspace目錄,主要是存放代碼。更多的環(huán)境變量請(qǐng)參考文末附錄。
這樣,就能自動(dòng)在project下的app的build/outputs/apk下生成相應(yīng)的apk.
編譯失敗?可能要解決以下2個(gè)問題:
1.gradle沒配置環(huán)境變量。
比如我在/etc/profile中配置一下GRADLE_HOME:
- export GRADLE_HOME='/home/jay/.gradle/wrapper/dists/gradle-2.2.1-all/c64ydeuardnfqctvr1gm30w53/gradle-2.2.1'
- export PATH=$GRADLE_HOME/bin:$PATH
2.找不到local.properties中sdk定義。
因?yàn)橐话銇碚flocal.properties不會(huì)添加到版本庫。所以需要手動(dòng)copy到${WORKSPACE}下的Project目錄下(可參考自己Android Studio工程結(jié)構(gòu))。
關(guān)于local.properties的定義,這里記錄一下,做個(gè)備份:
- sdk.dir=xx/xx/android-sdk
再編譯一般就會(huì)編譯成功,當(dāng)然當(dāng)那些第三方庫需要重新下載的話,編譯可能會(huì)很慢。
配置xcode – ios專用
請(qǐng)android的同學(xué)們飄過。
安裝Xcode插件后,可看到如下圖界面,并配置:
這里有兩個(gè)地方需要注意。
- 簽名
- 需要Shared Schema文件.
上傳到蒲公英平臺(tái)
在官網(wǎng)文檔里有說明,通過linux平臺(tái)上傳app的關(guān)鍵代碼
- curl -F "file=@/tmp/example.ipa" -F "uKey=" -F "_api_key=" http://www.pgyer.com/apiv1/app/upload
具體來說,
- # 先把${version}看成v1.0吧
- curl -F "file=@/home/xxx/release/ganchai-release-${version}-0101-dev.apk" -F "uKey=231xxxxe6" -F"_api_key=0xxxx499" -F "publishRange=2" http://www.pgyer.com/apiv1/app/upload
這樣就完成一個(gè)app上傳到蒲公英了。
實(shí)際上,我們可能會(huì)面對(duì)更復(fù)雜的場(chǎng)景,比如上面的${version}, 而version定義于build.gradle如下:
- ext {
- compileSdkVersion = 22
- buildToolsVersion = "23.0.1"
- minSdkVersion = 10
- targetSdkVersion = 22
- versionCode = 1111
- versionName = "v1.2.0.0"
- }
得想辦法讀到versionName, 然后拼出最終的文件名,這樣下次版本升級(jí)了之后也能動(dòng)態(tài)的上傳app到蒲公英了。
- # 使用sed命令讀取,使用cut切割,最終動(dòng)態(tài)讀取到versionName
- version=`sed -n '21,1p' ${WORKSPACE}/xxx/build.gradle | cut -c20-27`
這是android的apk上傳過程,相應(yīng)的,ios是上傳ipa,方法是一樣的,不再贅述。
小結(jié)
把開發(fā)人員發(fā)布版本的工作自動(dòng)化之后,如此一來,方便了測(cè)試人員隨時(shí)拉取并構(gòu)建***版本,更解放了開發(fā)人員自己的發(fā)版本的工作,一個(gè)字,善!
附錄
jenkins中定義的那些環(huán)境變量:
- The following variables are available to shell scripts
- BUILD_NUMBER
- The current build number, such as "153"
- BUILD_ID
- The current build id, such as "2005-08-22_23-59-59" (YYYY-MM-DD_hh-mm-ss)
- BUILD_DISPLAY_NAME
- The display name of the current build, which is something like "#153" by default.
- JOB_NAME
- Name of the project of this build, such as "foo" or "foo/bar". (To strip off folder paths from a Bourne shell script, try:${JOB_NAME##*/})
- BUILD_TAG
- String of "jenkins-${JOB_NAME}-${BUILD_NUMBER}". Convenient to put into a resource file, a jar file, etc for easier identification.
- EXECUTOR_NUMBER
- The unique number that identifies the current executor (among executors of the same machine) that’s carrying out thisbuild. This is the number you see in the "build executor status", except that the number starts from 0, not 1.
- NODE_NAME
- Name of the slave if the build is on a slave, or "master" if run on master
- NODE_LABELS
- Whitespace-separated list of labels that the node is assigned.
- WORKSPACE
- The absolute path of the directory assigned to the build as a workspace.
- JENKINS_HOME
- The absolute path of the directory assigned on the master node for Jenkins to store data.
- JENKINS_URL
- Full URL of Jenkins, like http://server:port/jenkins/ (note: only available if Jenkins URL set in system configuration)
- BUILD_URL
- Full URL of this build, like http://server:port/jenkins/job/foo/15/ (Jenkins URL must be set)
- JOB_URL
- Full URL of this job, like http://server:port/jenkins/job/foo/ (Jenkins URL must be set)
- SVN_REVISION
- Subversion revision number that's currently checked out to the workspace, such as "12345"
- SVN_URL
- Subversion URL that's currently checked out to the workspace.































