打 debug 包流程:
- git pull 分支最新代碼
- Android Studio:Build - Generate Signed APK
從 IDE 里可以看到,實際上該操作是執行了 assembleDebug,在打包完成后再將編譯目錄下的 apk 包安裝到調試的手機上并運行。
- 拷貝了工作目錄下 app/build/outputs/apk 文件夾下的 apk 安裝包交付測試
顯而易見這些是很枯燥、重復且浪費時間的工作,而一切重復的工作皆可自動化。我們選擇使用 Jenkins 這一已經很成熟的持續集成方案來實現自動化打包。
初識 Jenkins
What is Jenkins?
Jenkins is a self-contained, open source automation server which can be used to automate all sorts of tasks related to building, testing, and deploying software.
Jenkins 說白了其實就是后臺服務加上 web 管理配置頁面。Jenkins 里,每個任務稱為 Job,所以簡單的說,持續集成的實質就是通過 web 頁面構建一個或多個任務,然后通過后臺服務持續、自動地執行這些任務。
安裝教程
因為 Jenkins 是基于 Java 開發的(但不僅限于構建基于 Java 開發的軟件),所以運行 Jenkins 需要配置 Java 環境。
- 下載 Jenkins 安裝包。Jenkins 下載地址。雖然官方也提供了各個平臺的安裝包(例如 Mac 端 的 pkg 文件,但個人認為直接下載 war 包自行使用命令安裝更為方便)
- 在安裝包所在目錄運行命令:
//默認運行在8080端口
java -jar jenkins.war
//如果報錯誤為端口號已被占用,先查看端口號(例如8081端口)被什么程序占用
lsof -i tcp:8081
//記住服務的 PID 號(例如8123),強制 kill 相應服務
sudo kill 8123
//或直接指定未被占用端口運行
java -jar jenkins.war --httpPort=2333
Jenkins 就啟動成功了。
- 瀏覽器中進入 Jenkins web 配置頁面:http://localhost:8081。
第一次啟動時在控制臺會輸出一串密鑰用于首次登陸驗證。當然,如果懶得翻控制臺打印出的命令,也可以直接到本地的 Jenkins 安裝目錄去找(Mac 上一般為 用戶目錄下的/.jenkins/secrets/initialAdminPassword)。該密鑰同時也是 Jenkins 初始賬號 admin 的密碼。
輸入密鑰之后會出現如下讓用戶選擇安裝插件的選項。如果是第一次接觸的同學可能會有點懵。所以這里建議點擊右上角關閉,直接進入 Jenkins。后續再根據需求安裝插件。
構建 Job
準備工作
成功進入 Jenkins 后,我們就可以開始構建任務啦。但是在此之前,還需要為 Jenkins 配置相應的環境,以及安裝需要用到的插件。
安裝插件
左側選擇系統管理 - 插件管理 - 可選插件,搜索并安裝插件。
必裝插件:
- Gradle Plugin
- GitLab Plugin
建議安裝的插件:
- build-name-setter (用于自定義每次構建的名字)
- Build Timestamp Plugin (比較好用的時間戳插件)
tips:如果插件管理里顯示可更新、可選插件都為空,前往插件管理 - 高級 - 滑動到最下方的升級站點,將地址替換為http://mirror.xmission.com/jenkins/updates/update-center.json
。
配置環境
還需要配置一下環境變量,在系統設置 - 全局變量里添加一個 Android Sdk 的目錄。
- ANDROID_HOME = android sdk 所在目錄
配置 GitLab 服務器
在配置服務器之前,需要先配置一下證書。
左側菜單 Credentials - System - Add domain 添加一個證書,Domain Name
輸入名字后點擊 OK,再點擊 adding some credentials 添加有訪問代碼權限的賬戶的 token,Kind 欄選擇 GitLab API Token,Api token 一欄填入賬戶的 token,token 的獲取方式為 GitLab - User Settings - Private Token。
接著配置一下服務器。左側 系統管理 - 系統設置,安裝 GitLab 插件之后會看到新增了一欄 GitLab,在這里配置一下源碼服務器。
Host Url 填寫服務器地址:http://xxx.xx.xx.xxx:9000/。
Credentials 選擇剛才添加的證書。然后點擊右下方的 Test Connection,不出意外的話就可以連接成功了。
添加 SSH
配置好服務器后,還需要配置用于訪問服務器的私鑰 。SSH 協議規定遠程主機在用戶發起請求后,會發送一串隨機字符串回來,用戶使用本地存儲的私鑰對字符串進行加密后又發送回來,遠程主機再使用用戶事先配置的公鑰比對加密后的字符串,以此來鑒定本次訪問是否可信。因此,用于獲取源碼的服務器上必須配置用于遠程登錄服務器的 SSH 私鑰。
左側菜單 Credentials - System - Add domain 添加一個證書,Domain Name
輸入名字后點擊 OK,再點擊 adding some credentials 添加有訪問代碼權限的賬戶的私鑰,Kind 欄選擇 SSH username with private key,Private Key 欄有多種方式可選,這里選擇直接輸入私鑰,選擇 Enter directly,直接復制私鑰內容到輸入框中(私鑰位置:用戶目錄/.ssh/ 下的 id_rsa 文件,直接復制文件的所有內容),其他都不用填,點擊保存。
構建第一個任務
點擊左側菜單欄 - 新建,新建一個任務
這里看到了兩個選項。多配置項目,顧名思義,更適用于有上下游任務、多個項目聯動的任務,該項目相比自由風格的軟件項目新增了些可選配置,例如上游項目正在構建時阻止該項目構建、使用自定義工作空間等。
我們先選擇自由風格的軟件項目來試試手。輸入項目名后可以看到整個配置頁面了。最上方 GitLab connection 一欄選擇剛配置的 GitLab。源碼管理一欄選擇 Git,配置一下項目地址。
Repository URL 填寫項目地址:git@git.xxxx.com:xxx/xxxx.。
Credentials 一欄選擇之前添加的 SSH。
Branches to build 一欄填寫從哪個分支獲取,例如從主分支:*/master
配置好之后,滑到最下方應有并保存。之后點擊左側菜單 - 立刻構建試一下吧。如果看到任務被成功構建那就說明已經成功從服務器拉取源碼啦。
編譯項目
進入 Job,左側菜單 - 配置,在構建一欄增加構建步驟,選擇 Invoke Gradle Script - 選擇 Invoke Gradle,并在下方的 Tasks 中配置要執行的命令。比如打測試包命令:
clean
assembleDebug //如果項目中配置了 productFlavors,打包命令需要相應地更換為你需要打包的版本
配置完成后,一樣的進入到項目的 app/build/outputs/apk 目錄下就可以看到打好的 debug 包。項目位置位于:用戶目錄/.jenkins/workspace/xxx 。
當然,你也可以自己執行項目的工作目錄。在 Job 的編輯頁,滑動到最上方, General 選項卡最頂端點擊 高級 按鈕,可以看到選項:使用自定義的工作空間,例如:
目錄:workspace/work/testProject
顯示名稱:testProject
實現自動化
常見的自動化執行配置方法:進入 Job,左側菜單 - 配置 - 構建觸發器:
定時執行
有兩個比較相似的行為:
- Build periodically :周期性地執行構建
- Poll SCM :檢查源碼變更,如果有變更則拉取源碼并執行構建
二者都需要在下方的日程表中填寫定時規則。
日程表編寫規則
簡要介紹一下定時編寫規則:
規則由5個參數組成,每個參數之間以空格間隔(單個參數里不可以包含空格),例如:* * * * * 。* 號代表忽略該參數。
五個參數從左起分別代表:分(范圍0 ~ 59)、時(范圍0 ~ 23)、天(范圍1 ~ 31)、月(范圍1 ~ 12)、星期(范圍0 ~ 7,0/7均代表周日)
同個參數可以指定多個數,以英文逗號分隔(注意不要帶空格),例如:?2,3,5 ;可以指定連續數:2-5(等同于 2,3,4,5)
舉幾個例子直觀地看一下:
H 10 * * * //每天10點整點。等同于 0 10 * * * ,如果分的參數輸入0,會建議你使用 H 替代。
20 8,10,18 * 11,12 1-4 //11、12兩個月的周一到周四的每天8點20、10點20、18點20。
GitLab Hook
構建觸發器選擇:Build when a change is pushed to GitLab. GitLab CI Service URL: http://localhost:8081/xxx/xxx
這個需要在 GitLab 的源碼項目里配置一下上面提到的地址,這樣源碼在接收到 push 事件時會通知 Jenkins 執行構建。因為沒有將 Jenkins 發布到外網,所以可以看到上面提供的 hook 的地址也只是個本地的 localhost 的地址,填寫這個地址外網是無法通知到的。這里暫時沒有測試該特性。
基于上游項目的構建
構建觸發器選擇:Build after other projects are built。并填寫上游項目的 Job 名稱。還可以基于上游項目的構建情況,是否成功、失敗來構建自身。
基本上這些操作已經可以使整個自動化打包流程運行起來了。
關閉服務
采用 Jenkins CLI的方式來關閉Jenkins。
左側菜單 - 系統設置 - Jenkins CLI。下載 jenkins-cli.jar 之后,通過使用命令控制 Jenkins,例如:
java -jar jenkins-cli.jar -s http://localhost:8081/ shutdown //關閉 Jenkins 服務
如果執行該命令時報沒有權限的錯誤,則需要為 admin 賬號配置一下權限。
另外網絡上很多提到直接在鏈接后加上相應的操作,例如 exit、restart、reload等,例如訪問 http://localhost:8081/exit 代表退出 Jenkins,但估計很多人會遇到一下問題:
此類跳轉失敗的錯誤原因是 Jenkins 在 http 請求頭部中放置了一個名為.crumb的token,在使用了反向代理,并且在設置中勾選了“防止跨站點請求偽造”之后此
token 會被轉發服務器認為是不合法頭部而去掉,導致跳轉失敗。解決方法就是左側菜單 - 系統管理 - Configure Global Security 中取消勾選 防止跨站點請求偽造
權限配置問題
如果因為設置錯誤而收回了配置權限,導致 admin 賬號連主頁都無法進入,解決的方法就是直接到 Jenkins 安裝目錄里修改配置文件。
進入安裝目錄(用戶目錄/.jenkins)/config.xml,修改 useSecurity 欄目,修改為任何用戶可以做任何事:
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
<disableSignup>true</disableSignup>
<enableCaptcha>false</enableCaptcha>
</securityRealm>
參數化構建
可以在打包時指定打包分支。
安裝插件 Git Parameter
安裝后,在項目的設置頁,選擇參數化構建 - Git-Parameter,Name 隨意填,例如mybranch,Parameter Type 選擇 Branch。在下方的源碼管理一欄中的 Branches to build 之前我們填的是指定的分支,例如 */devel。現在有了分支參數之后,我們只需要映射分支參數即可,這里修改為 $mybranch。保存設置。
現在在項目的右邊可以看到 Build with Parameters 的選項。點擊該選項就可以看到剛剛設置的 mybranch 里羅列出了 git 項目上所有的分支。打包時就可以選取指定的分支打包了。
Gradle 根據參數構架不同的版本,例如正式包、測試包
這里就要取消掉 invoke Gradle script,選擇 Execute shell,使用 shell 腳本來實現參數化構建。
chomd +x gradlew
case $build_type in
debug)
./gradlew assembleDebug
;;
release)
./gradlew assembleRelease
;;
*)
exit
;;
esac
展望
以上的自動化打包流程實際上還有可以改進的地方,例如每次測試人員都需要從 Jenkins 服務器上拷貝 apk 安裝包,這一流程也應該自動化實現,比如使用腳本自動將 apk 安裝包上傳至公司 FTP 服務器,或者上傳至第三方軟件分發服務商,例如蒲公英、Fir 等,還可以將下載地址轉換為二維碼顯示在相應的 Job 頁,測試人員直接掃碼安裝即可。
以及 Jenkins 的其他功能的擴展,例如構建發生錯誤時自動將日志發送至相關開發人員的郵箱,使開發人員可以更快更及時地響應問題。
以及如果項目有編寫單元測試,可以使用 Jenkins 周期性地自動運行單元測試,及早發現新引入的問題。
Jenkins 是一個工具,同時也代表著一種思想,它告訴了我們:
懶是第一生產力 :P。
以上。