行動應用|自動化開發階段的更版流程|fastlane|conventional-changelog
Developers love writing React Native code but no one likes deploying React Native app or distributing beta builds. - RNBoilerplate Docs
(那些喜愛寫 React Native 的開發者,不會有人喜歡做建置、發布或上架的事情)
前言
當專案開發到了雛型階段時,會需要讓開發人員以外的人使用或測試。在 Web 開發中,只需要更新 server 中的靜態檔就能完成;但是在行動應用上,就需要建置出安裝檔,當使用者下載安裝後,才完成更版的動作。
如果完全不靠工具和指令,每次建置、上傳檔案等都親力而為,其實花費不少時間。舉例來說,在 XCode 中產出安裝檔 ipa,需要經過以下步驟:
- 選擇裝置
Any iOS Device
Product > Archive
,通常會等 2~3 分- 在 Archives 列表視窗中點選
Distribute app
- 選擇部署方式。例如
Enterprise
- 選擇
Manually manage signing
- 選擇
Distribution Certificate
&Provision Profile
,這時候會再等 1~2 分 - 點選
Export
,選擇輸出目錄
到這裡大概就花了約 5 分鐘了,接著還需要打開 Dropbox,或是自家的空間上傳剛剛建置出的 ipa。為了給測試人員查看有哪些更新,還要再維護安裝頁中的變更紀錄。最後,需要通知有安裝 App 的人已經有新版釋出。
做完一輪這些事情就花了至少 10 分鐘(還不包含建置前要做的版本管理)。如果在開發階段需要頻繁的釋出版本,可能就要比平常晚半小時,甚至一小時才能下班啊 😖
規劃更版流程
當你發現同一件事情做了三次以上,未來也會繼續做時,就先停下來吧!
手動做這些事情,除了浪費時間以外,也很容易增加出錯的機率。例如上傳到錯的檔案、版本標錯等。因此,我們需要仰賴工具,幫我們做這些千篇一律的任務,讓省下來的時間,去做有實際產出、有創造力的事 💪
首先,必須先整理出要需要哪些任務,這些任務的前後關係,以及思考每個任務的執行內容。這部分會根據開發習慣、專案性質、產品開發階段的不同而會有不一樣的流程。只要有想清楚就好,沒有一定的標準。
以我目前接手的專案來說,需要做的是建置雙系統的開發版 App 給內部人員。所以就規劃了以上流程。工具上,我主要會用以下兩種:
Conventional Changelog: 為 commit 增加分類、描述、關聯的 issue 等格式化訊息,並產出 CHANGELOG.md
前置準備-
- 在全域安裝 commitizen 跟 conventional-changelog-cli
npm i -g commitizen conventional-changelog-cli
- 在專案目錄下安裝 cz-conventional-changelog
npm i -D cz-conventional-changelog
安裝完成後,package.json 會自動加上一段設定package.json 1
2
3
4
5"config": {
"commitizen": {
"path": "./App4/node_modules/cz-conventional-changelog"
}
}
- 在全域安裝 commitizen 跟 conventional-changelog-cli
fastlane: 提供大量實用的 actions 和 plugins,以 ruby code 和 cli 做到任務的自動化
前置準備-
- 可以參考 React Native Boilerplate Docs 的Installing Fastlane。重點是要先有 MacOS😅
- 在 ios 跟 android 的目錄下執行
fastlane init
。兩個系統在 setup 時有些許差異,同樣可以參考上面連結。 - 需要安裝某個 plugin 時,執行
fastlane add_plugin xxx
就可以囉! - 想查詢某個內建 action 或是 plugin 時,可以執行
fastlane action xxx
,就可以在command line上看到很清楚的說明文件。
接下來就針對每個任務來分享我的實作方式吧!
任務實作
提交 commit
寫一份好的的commit,會幫助我們完成以下事情:
- 更順暢地 code review
- 團隊合作的版本控管,讓大家一看就知道那段時間在做什麼
- 方便準備 release 相關文件(e.g. 產生CHANGELOG.md)
- 提供 trace code 的線索
為了方便操作,我們可以建立npm指令
1 | "script": { |
將想變更的檔案加到stage,執行 npm run cm
後就可以顯示以下畫面:
接著按照每個步驟填入分類、簡述、issue序號等,就可以完成一個有完整資訊的commit囉!
更新版本
無論是 Android 還是 iOS,都會有兩個值去描述版本。由於兩邊的名稱都不太一樣,我這裡就取一致的中文名稱。
版名:建議採Semantic Versioning(major.minor.patch)。 主要用途是在上架商店中會顯示給使用者看。
iOS Android 名稱 Version Number Version Name 格式 建議 Semantic Versioning 同左 版號:建置號碼,主要用途是給商店的上架系統識別使用,並不會給使用者知道。此外,在兩個作業系統中,建置號碼的規則也不太一樣。
iOS Android 格式 建議 Semantic Versioning 須為數字 規則 同版名中不能重複,不同版名間可以重複 無論版名必須往上遞增 限制 無特定格式限制 不能超過2100000000
為了管理方便,我這裡採雙系統皆取一致的版名跟版號。版名的話就依 package.json
的 version
而定;版號的話因 Android 的限制比較多,所以就以 Android 的命名規則來取版號。
版號邏輯可以根據喜好或維護方便而定。而我訂的版號規則是:
- 固定八位數,格式為 yymmddnn
- yy:年份的後兩位數
- mm:月份,固定兩位數
- dd:日期,固定兩位數
- nn:建置序號,固定兩位數
- 同個日期就把建置序號從 00 往上遞增
- 不同日期就把建置序號歸為 00
在 fastlane 中,iOS 的版名跟版號都有內建的 action 可以讀寫;不過 Android 的部分就要另外安裝 plugin。
版名 | iOS | Android |
---|---|---|
讀 | get_version_number |
get_version_name |
寫 | increment_version_number |
increment_version_name |
版號 | iOS | Android |
---|---|---|
讀 | get_build_number |
get_version_code |
寫 | increment_build_number |
increment_version_code |
版名因為需要取得package.json
,所以我們需要安裝 plugin load_json
。
1 | package = load_json(json_path: "../package.json") |
打版本 tag
以我的需求來說,希望每次的更版在 git 上留下紀錄。因此我會把版名加上版號組成一個完整的版本名稱,接著在打到 git tag 上。
需要使用的 action 有 add_git_tag。
1 | package = load_json(json_path: "../package.json") |
建置安裝檔
Android 中的建置階段非常簡單,不需要任何的驗證或設定,只需要知道這次要建置的是 Debug 版或是 Release 版就好。
1 | # 相當於執行 gradlew clean && gradlew assembleDebug |
iOS 的就比較麻煩,好在 fastlane 有提供介面相對友善的套件-gym。我們可以使用 build_app
這個 action 來完成建置的設定。
1 | build_app( |
上傳至 Dropbox
產生安裝檔後,我們是選擇以 OTA 方式,將安裝檔放在雲空間供測試人員下載。如何建立 OTA 的安裝機制,可以參考這篇文章。
以 Dropbox 當作雲空間的話,設定的重點有以下:
在 Dropbox Developers 建立應用,然後取得 token。要注意的地方有以下:
- token 的期限要設為
No expiration
,不然預設 15 分後就會到期。
- 需要打開檔案的編輯權限
file.content.write
- token 的期限要設為
參考 fastlane 的這份文件,將 token 這種機敏資訊妥善管理。我這裡是使用 dotenv,好處是不用更改本機的環境變數,透過
gitignore
方式將機敏檔案過濾掉。在專案根目錄下執行
gem install dotenv
安裝在專案根目錄下新增
.env.secret
.env.secret 1
2DROPBOX_OAUTH_TOKEN="xxx..."
在 Fastfile 中引入 dotenv 跟 剛剛建立的
.env.secret
Fastfile 1
2
3
4
5
6fastlane_require 'dotenv'
before_all do
Dotenv.overload '../../.env.secret'
end
安裝 fastlane-plugin-dropbox。欄位的注意事項有以下:
write_mode
-寫入的模式,文件上有寫比較理想的方式是update
,不過還沒弄清楚update_rev
要如何設定,所以目前測試OK的是overwrite
file_path
-要使用安裝檔的絕對路徑dropbox_path
-中文名稱也OK。沒有這個欄位的話預設是上傳到根目錄access_token
-以ENV[name]
來取得剛剛在.env.secret
建立的 token 值1
2
3
4
5
6dropbox(
write_mode: 'overwrite',
file_path: '/Users/sss/xxx/ooo/ios/app.ipa',
dropbox_path: '第一層目錄名稱/第二層目錄名稱',
access_token: ENV['DROPBOX_OAUTH_TOKEN']
)
通知工作群組
當安裝檔建置完成、上傳到雲空間後,相關人員就可以前往更新版本了。如果團隊中有使用會話群組,像是 slack 、 google chat 等,fastlane 有提供相關的 plugin 來幫我們做到主動通知的事情。
- 首先安裝 fastlane-plugin-google_chat
fastlane add_plugin google_chat
- 在會話群組的聊天室名稱,右邊有個小三角形,點選後選擇「管理 Webhook」
- 一開始如果沒有設定 webhook,會直接讓你輸入名稱跟圖片網址。
名稱可以輸入 fastlane,之後新增其他的服務連動,會比較清楚是哪個來源打的。
圖片網址可以不設,也可以取找對應的icon網址填入。 - 點選「儲存」後就可以產生並取得 Webhook 的連結囉!
最後,參考作者的範例修改就OK了。
1 | package = load_json(json_path: "../package.json") |
執行 fastlane 任務
上面這些 fastlane 的任務,要怎麼串連起來呢?
通常我們會把這些單一任務分別用 private_lane
包覆起來,表示這是不能單獨執行的。接著再新增一個用 lane
包覆起來的任務,工作內容就是依序執行這些私有任務。
private_lane 的寫法:
1 | desc "Android Debug: Build a Debug App" |
在 Android 裡的 lane
1 | desc "Android Debug: Do all jobs" |
在 iOS 裡的 lane
1 | desc "iOS Enterprise: Do all jobs" |
為了更方便執行指令,可以新增npm指令。
有個題外話,我在切到 ios 的目錄後,執行 fastlane 的前綴有加 arch -x86_64
,原因是我目前使用 Mac m1,因為硬體上的限制才需要加。如果之後在安裝什麼或執行什麼時發生意外錯誤,可以試著加加看。
1 | "scripts": { |
更新 CHANGELOG.md
最後,就是產出 CHANGELOG.md,並更新到 git 上囉!
同樣我們可以為冗長的 cli 新增 npm 指令:
1 | "scripts": { |
結論
在導入這些工具時,其實不到半天的時間就可以完成了。學習門檻來說,也不用太高,頂多只需要查詢一些 ruby 的基本語法等,算是很值得上手的工具。
比較重要的是要思考任務的安排合不合理,在自動化和後續維護上是不是可以更順暢;另外在部署上架版的流程和跟開發階段的流程勢必會不太一樣。到時候有什麼新想法,再跟大家分享囉 👋
行動應用|自動化開發階段的更版流程|fastlane|conventional-changelog