前言
談起 CI/CD,很多同學(xué)的表情可能是這樣的?

怎么說,身為 Android 開發(fā),很多同學(xué)對 CI/CD 的了解少之又少,其實借助 CI/CD,可以做不少有趣的事。
首先,我們得了解什么是 CI/CD?

CI/CD 包括 CI 和 CD。
CI(Continuous Interaction)是持續(xù)集成的意思,它是屬于開發(fā)人員的自動化流程。在當(dāng)今的 Android 開發(fā)模式中,多人同時維護一個項目已在所難免,所以,每次提交代碼后,我們可以讓機器去做些什么去維護我們項目的安全性和穩(wěn)定性?其實上面的圖中已經(jīng)給出答案,當(dāng)我們提交一份代碼后,自動化工具就會觸發(fā)構(gòu)建、測試,這些都完成以后,再幫我們合并代碼。
CD 通常有兩層意思,一層是(Continuous Delivery),它代表著持續(xù)交付,當(dāng)開發(fā)人員提交代碼后,自動化工具會會自動進行錯誤測試并上傳到存儲庫,最后由運維團隊將其部署在實時的生產(chǎn)環(huán)境中。另一層是(Continuous Deployment),它代表著持續(xù)部署,是將存儲庫發(fā)布到生產(chǎn)環(huán)境。
聽完上面的解釋,各位 Android 開發(fā)仔能弄懂 CI/CD 嗎?
顯然不能啊,這有關(guān)系嗎,沒關(guān)系,反正是從入門到放棄, :)
CI 還比較好理解,用實際的 Android 經(jīng)驗來解釋就是,持續(xù)交付就是將更新后的 Master 分支代碼打包成各個應(yīng)用市場需要的渠道包,持續(xù)部署就是將上一步打好的包,自動上傳到對應(yīng)的應(yīng)用市場。
CI/CD對Android開發(fā)的意義
首先,CI 對于開發(fā)人員來說,可以及早的發(fā)現(xiàn)錯誤,每當(dāng)我們提交一筆代碼,自動化工具就會發(fā)起構(gòu)建、自動化測試,最終會合入代碼,保證我們項目的穩(wěn)定性。別代碼提上去了,編都編不過,還吭呲吭呲往下寫!
其次,對于運維人員來說,CD 可以提升他們的工作效率,自動化工具可以減少他們不必要的工作量。
這個意義有點籠統(tǒng),放到Android上來講:
- 標(biāo)準(zhǔn)化管理Android項目
- 自動觸發(fā)多渠道打包
- 自動上傳到應(yīng)用市場應(yīng)該也是可以的,我看到一些平臺可以把我們的app一鍵上傳到各個應(yīng)用市場。
- 我最近研究的組件化下自動上傳AAR
- 以及組內(nèi)大佬醫(yī)生分享的精準(zhǔn)化測試系列
- ...
CI/CD 工具幫了我們不少忙。

看這里,Bugly 也建議我們將符號表的上傳放入持續(xù)集成中。
最常見的持續(xù)集成工具應(yīng)該是 Jenkins,因為它開源并且免費,你想要的插件在開源社區(qū)幾乎都可以找到,起點之前也是用的 Jenkins,也就最近,才開始從 Jenkins 遷移到騰訊內(nèi)部的藍盾。
不過,本次的主角并不是 Jenkins。
Github Action 簡介
沒錯,本次的主角就是 Github Action,官方是這么介紹的:
在 GitHub Actions 的倉庫中自動化、自定義和執(zhí)行軟件開發(fā)工作流程。 您可以發(fā)現(xiàn)、創(chuàng)建和共享操作以執(zhí)行您喜歡的任何作業(yè)(包括 CI/CD),并將操作合并到完全自定義的工作流程中。
所以,Github Actions 是 Github 的持續(xù)集成服務(wù),那么它和其他的持續(xù)集成工具有什么不同呢?
所有的一系列操作例如設(shè)置 JDK、運行 Shell 命令或者是執(zhí)行自動化測試都被稱之為 Action,像設(shè)置JDK Action 肯定通用的,所以就有了免費的 Github Action 市場,允許開發(fā)者將他們封裝的 Action 上傳到市場,其他開發(fā)者需要的時候引用即可。

可以看到,當(dāng)前市場的 Action 已經(jīng)快1W個了,能夠滿足開發(fā)的日常需求。
除此以外,Github Action 也為通用的一些項目提供了模板,當(dāng)我們點擊自己項目的 Action 的時候,它會給你推薦一些模板:

也允許你自己選擇一些熱門的模板。
語法簡介
看到這兒,很多同學(xué)都已經(jīng)開始躍躍欲試了,別急,我們再介紹一下語法!

理解了這些名詞,我們再了解一下它們之間的關(guān)系:

一個 WorkFlow 由 Event 和 Jobs 組成,每個 Job 由 Runner 和 一系列 Step 組成,每個 Step 都由 Action 或者 Shell Command 組成。
有了一定的了解以后,我們看看語法。
Github Action 的配置文件叫做 WorkFlow 文件,存放在代碼倉庫的 .github/workflows 目錄,該文件采用的 yaml 語法。
我們使用一個默認(rèn)的 Android 模板,看看它是什么樣子的:
name: Android CI # 設(shè)置 WorkFlow 名稱
on: # 設(shè)置 Event,
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build: # 設(shè)置 Job 的名稱
runs-on: ubuntu-latest # 設(shè)置服務(wù)器
steps:
- uses: actions/checkout@v2 # Action 獲取當(dāng)前分支的源碼
- name: set up JDK 11 # Action 設(shè)置 JDK
uses: actions/setup-java@v2
with: # 參數(shù)由 Action 的創(chuàng)建者指定,需要查看創(chuàng)建者的文檔
java-version: '11'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew # Shell Command
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
從上往下解釋:
| 名詞 | 解釋 |
|---|---|
name |
設(shè)置 WorkFlow 的名字 |
on |
設(shè)置 Event,比如上面的 on.push.branches: [ master ],代表著觸發(fā)的時機是 master 分支發(fā)生 push 的時候 |
jobs |
聲明 Jobs,上述中 jobs 下的 build 字段,則是創(chuàng)建一個 Job,也是該 Job 的名稱 |
runs-on |
聲明 Runner,也就是服務(wù)器,支持 Windows、MacOS 和 Ubuntu,不過需要指定版本 |
steps |
聲明 Steps |
steps.- |
- 代表著當(dāng)前是一個 Step |
steps.name |
當(dāng)前 Step 的名稱 |
steps.uses |
使用市場中的 Action |
steps.run |
執(zhí)行 Shell 命令 |
有了名詞解釋,我們就能明白上面的 Android 模板的大致過程。
Android 實踐
我們有了一定的基礎(chǔ),借助這些,就可以做些有趣的東西的了!
試想一下,當(dāng)我們往 master 分支合入代碼的時候,是不是就要發(fā)版了,這個時候,持續(xù)集成可以幫我們構(gòu)建項目,之后根據(jù)需要,使用生成好的Apk進行加固、多渠道打包、上傳備份文件,最后發(fā)出成功或者失敗的消息。
那同樣的,我們可以在上面的示例中做一些修改,加入上傳APK、將結(jié)果發(fā)送給郵件和釘釘?shù)娜骸?/p>
以我的 AndroidGithubAction 為例,完整的教程是這樣的:
1. 創(chuàng)建一個Action
選中 Github 項目中的 Action 欄,我們將會見到下面的內(nèi)容:

根據(jù)需要選擇自定義方式或者模板方式,這里我們選擇 Android 模板。
2. 修改模版
模板的內(nèi)容我在上面已經(jīng)發(fā)過了,修改一下:
- 最外層的
name可改可不改。 - Event 修改為 push 的時候觸發(fā)即可。
- 修改一下 Gradle 命令。
修改完的內(nèi)容:
name: Android CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2 # 拉取源代碼
- name: set up JDK 1.8 # 設(shè)置JDK
uses: actions/setup-java@v2
with:
java-version: 8.0.232+9.1
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle # 執(zhí)行編譯命令
run: ./gradlew clean && ./gradlew app:assembleDebug
3. 上傳APK
先說明一下,編寫 WorkFlow 時,左邊是編寫 WorkFlow 的面板,右邊就可以直接在市場上搜索你想使用的Action。

選中想使用的 Action,就會跳出詳細的說明文檔。

下面這個就是我們想使用的 Action。

有了它,我們可以上傳編譯完的 apk,可以在多個 job 之間共享,或者等持續(xù)集成運行結(jié)束的時候,可以下載生成的產(chǎn)物。

引入代碼:
- name: Upload Apk
uses: actions/upload-artifact@v1
with:
name: qd-info
path: app/build/outputs/apk/debug/app-debug.apk
4. 上傳APK到第三方平臺
很多情況下需要將產(chǎn)物上傳到第三方平臺,以蒲公英為例:
- name: Upload File to Pgyer
run: curl -F 'file=@app/build/outputs/apk/debug/app-debug.apk' -F "_api_key=${{ secrets.API_KEY }}" -F "uKey=${{ secrets.PGYER_USER_KEY }}" -F "buildInstallType=3" https://www.pgyer.com/apiv2/app/upload
這里面涉及了兩個知識點,一是 curl 工具的使用,二是 Github Action 中隱私信息的用法。
curl 是用來請求Web服務(wù)器的命令行工具,curl 使用可參考阮一峰的文章:
對于一些關(guān)鍵的 key,我們可能不太想將 key 暴露在 Github Action 的 workflow 文件中,Github 推薦我們將這些 key 存在 Secrets 中,添加入口:

5. 獲取構(gòu)建人
上傳完成后,我們會將編譯信息、版本信息、Commit日志發(fā)送到企業(yè)微信、釘釘或者郵箱。
首先是獲取構(gòu)建人信息,默認(rèn)的環(huán)境變量提供如下:

但是我發(fā)現(xiàn)在 curl 命令中有點問題,報了識別不了的錯誤:

于是就把觸發(fā)人寫在了全局變量中:
# 獲取發(fā)送者
- name: Sender
run: echo "sender=$GITHUB_ACTOR" >> $GITHUB_ENV
之后通過${{ env.sender }} 獲取。
6. 獲取版本信息
版本信息我是通過 grep 命令在 app 目錄下的 build.gradle 文件獲取

如果你有更好的方法,歡迎評論區(qū)交流~
同樣是寫到全局變量中:
# step: 獲取apk版本號
- name: APK Version
id: apk_version
run: |
versionCode=`grep "versionCode" app/build.gradle | awk -F " " '{print $2}'`
versionName=`grep "versionName" app/build.gradle | awk -F ''\"'' '{print $2}'`
echo "versionResult=$versionName.$versionCode" >> $GITHUB_ENV
在 Github Action 中,如果需要運行多條命令,需要在 run: 后面加上一個 |。
7. 獲取提交日志
獲取提交日志就比較簡單了,通過 git log 就能獲?。?/p>
# 獲取git log
- name: Get git log
id: git_log
run: |
updateLog=`git log --pretty=format:"%s" -1`
echo "updateLog=$updateLog" >> $GITHUB_ENV
該命令是獲取最近的提交記錄的日志,以指定格式輸出。
8. 發(fā)送到釘釘群中的小機器人
釘釘小機器人的使用說明就不介紹了,直接看官網(wǎng),命令如下:
# 向釘釘發(fā)送消息
- name: dingtalk
run: |
curl '${{ secrets.DINGDING_WEBHOOK }}' -H 'Content-Type: application/json' -d '{"msgtype": "text", "text": {"content":"吃飯! \n觸發(fā)人:${{ env.sender }} \n版本:${{ env.versionResult }} \n提交內(nèi)容:${{ env.updateLog }} \n下載地址:${{ secrets.PGY_URL }}"}}'
構(gòu)建成功后發(fā)送到群里的截圖:

9. 發(fā)送郵件
通常情況下,觸發(fā)人編譯失敗后是會收到失敗郵件的,如果不夠,我們可以再寫一個發(fā)送郵件的 Action。
我們這里引用其他人開源的 Action:
# 發(fā)送郵件
- name: Send mail
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.qq.com
server_port: 465
username: ${{ secrets.MAILUSERNAME }}
password: ${{ secrets.MAILPASSWORD }}
subject: Github Actions job result
to: 2556536414@qq.com
from: TeaOf
secure: true
body: "吃飯! \n觸發(fā)人:${{ env.sender }} \n版本:${{ env.versionResult }} \n提交內(nèi)容:${{ env.updateLog }} \n下載地址:${{ secrets.PGY_URL }}"
這一步做完,差不多就結(jié)束了。
完整代碼地址:https://github.com/mCyp/AndroidGithubAction/blob/main/.github/workflows/android.yml
總結(jié)
對于開源用戶來說,Github Action 還有一些比較常見的使用場景。
比如利用 Github Action 自動部署 Hexo 博客,還記得以前使用 Github Page 建立個人博客的時候,每次新增一篇文章以后,要輸入一串命令行,有了 Github Action,這些都可以自動執(zhí)行。
還有,很多人的 Github Porfile 是依賴 Github Action 自動更新的,像這位外國老哥:

他的 Profile 每三個小時會自動獲取一下最新的個人文章和社交媒體,展示在自己的個人簡介上,這個效果確實很贊!最近有時間,打算也來搞一套。
參考
《Github Actions 使用指南和Android 持續(xù)集成示例》
《GitHub Actions 入門教程》