行動應用|自動化開發階段的更版流程|fastlane|conventional-changelog

行動應用|自動化開發階段的更版流程|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,需要經過以下步驟:

  1. 選擇裝置 Any iOS Device
  2. Product > Archive,通常會等 2~3 分
  3. 在 Archives 列表視窗中點選 Distribute app
  4. 選擇部署方式。例如 Enterprise
  5. 選擇 Manually manage signing
  6. 選擇 Distribution Certificate & Provision Profile,這時候會再等 1~2 分
  7. 點選 Export,選擇輸出目錄

到這裡大概就花了約 5 分鐘了,接著還需要打開 Dropbox,或是自家的空間上傳剛剛建置出的 ipa。為了給測試人員查看有哪些更新,還要再維護安裝頁中的變更紀錄。最後,需要通知有安裝 App 的人已經有新版釋出。

做完一輪這些事情就花了至少 10 分鐘(還不包含建置前要做的版本管理)。如果在開發階段需要頻繁的釋出版本,可能就要比平常晚半小時,甚至一小時才能下班啊 😖

規劃更版流程

當你發現同一件事情做了三次以上,未來也會繼續做時,就先停下來吧!

手動做這些事情,除了浪費時間以外,也很容易增加出錯的機率。例如上傳到錯的檔案、版本標錯等。因此,我們需要仰賴工具,幫我們做這些千篇一律的任務,讓省下來的時間,去做有實際產出、有創造力的事 💪

首先,必須先整理出要需要哪些任務,這些任務的前後關係,以及思考每個任務的執行內容。這部分會根據開發習慣、專案性質、產品開發階段的不同而會有不一樣的流程。只要有想清楚就好,沒有一定的標準。

規劃更版的流程

以我目前接手的專案來說,需要做的是建置雙系統的開發版 App 給內部人員。所以就規劃了以上流程。工具上,我主要會用以下兩種:

  • Conventional Changelog: 為 commit 增加分類、描述、關聯的 issue 等格式化訊息,並產出 CHANGELOG.md

    前置準備-

    1. 在全域安裝 commitizen 跟 conventional-changelog-cli
      npm i -g commitizen conventional-changelog-cli
    2. 在專案目錄下安裝 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"
      }
      }
  • fastlane: 提供大量實用的 actions 和 plugins,以 ruby code 和 cli 做到任務的自動化

    前置準備-

    1. 可以參考 React Native Boilerplate Docs 的Installing Fastlane。重點是要先有 MacOS😅
    2. 在 ios 跟 android 的目錄下執行 fastlane init。兩個系統在 setup 時有些許差異,同樣可以參考上面連結。
    3. 需要安裝某個 plugin 時,執行 fastlane add_plugin xxx 就可以囉!
    4. 想查詢某個內建 action 或是 plugin 時,可以執行 fastlane action xxx,就可以在command line上看到很清楚的說明文件。

接下來就針對每個任務來分享我的實作方式吧!

任務實作

提交 commit

寫一份好的的commit,會幫助我們完成以下事情:

  • 更順暢地 code review
  • 團隊合作的版本控管,讓大家一看就知道那段時間在做什麼
  • 方便準備 release 相關文件(e.g. 產生CHANGELOG.md)
  • 提供 trace code 的線索

為了方便操作,我們可以建立npm指令

package.json
1
2
3
"script": {
"cm": "npx cz",
}

將想變更的檔案加到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.jsonversion 而定;版號的話因 Android 的限制比較多,所以就以 Android 的命名規則來取版號。

版號邏輯可以根據喜好或維護方便而定。而我訂的版號規則是:

  1. 固定八位數,格式為 yymmddnn
    • yy:年份的後兩位數
    • mm:月份,固定兩位數
    • dd:日期,固定兩位數
    • nn:建置序號,固定兩位數
  2. 同個日期就把建置序號從 00 往上遞增
  3. 不同日期就把建置序號歸為 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
2
3
4
5
6
7
8
9
10
11
12
13
package = load_json(json_path: "../package.json")
curr_build_number = get_build_number().to_i
datetime_number = Time.new.strftime('%Y%m%d')[2, 6].to_i * 100
diff = curr_build_number - datetime_number
new_build_number = 0;
if diff >= 0 # same day
new_build_number = datetime_number + diff + 1
else # another day
new_build_number = datetime_number
end
increment_build_number(
build_number: new_build_number
)

打版本 tag

以我的需求來說,希望每次的更版在 git 上留下紀錄。因此我會把版名加上版號組成一個完整的版本名稱,接著在打到 git tag 上。

需要使用的 action 有 add_git_tag

1
2
3
4
5
6
package = load_json(json_path: "../package.json")
version_name = package['version']
version_code = get_version_code()
add_git_tag(
tag: "v" + version_name + "-" + version_code
)

建置安裝檔

Android 中的建置階段非常簡單,不需要任何的驗證或設定,只需要知道這次要建置的是 Debug 版或是 Release 版就好。

Android
1
2
# 相當於執行 gradlew clean && gradlew assembleDebug
gradle(task: "clean assembleDebug")

iOS 的就比較麻煩,好在 fastlane 有提供介面相對友善的套件-gym。我們可以使用 build_app 這個 action 來完成建置的設定。

iOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
build_app(
silent: true,
clean: true,
configuration: "Release",
scheme: "AppName",
output_name: "app.ipa",
export_options: {
# 指定部署方式
method: "enterprise",
# 指定 provision profile
provisioningProfiles: {
"app.bundle.indentifier" => "ProvisionProfileName",
}
}
)

上傳至 Dropbox

產生安裝檔後,我們是選擇以 OTA 方式,將安裝檔放在雲空間供測試人員下載。如何建立 OTA 的安裝機制,可以參考這篇文章

以 Dropbox 當作雲空間的話,設定的重點有以下:

  1. Dropbox Developers 建立應用,然後取得 token。要注意的地方有以下:

    • token 的期限要設為 No expiration,不然預設 15 分後就會到期。
      Settings > OAuth2 設定 token
    • 需要打開檔案的編輯權限 file.content.write
      Permissions > Individual Scopes > Files and folders
  2. 參考 fastlane 的這份文件,將 token 這種機敏資訊妥善管理。我這裡是使用 dotenv,好處是不用更改本機的環境變數,透過 gitignore 方式將機敏檔案過濾掉。

    • 在專案根目錄下執行 gem install dotenv 安裝

    • 在專案根目錄下新增 .env.secret

      .env.secret
      1
      2
      DROPBOX_OAUTH_TOKEN="xxx..."

    • 在 Fastfile 中引入 dotenv 跟 剛剛建立的 .env.secret

      Fastfile
      1
      2
      3
      4
      5
      6
      fastlane_require 'dotenv'

      before_all do
      Dotenv.overload '../../.env.secret'
      end

  3. 安裝 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
      6
      dropbox(
      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 來幫我們做到主動通知的事情。

  1. 首先安裝 fastlane-plugin-google_chat
    fastlane add_plugin google_chat
  2. 在會話群組的聊天室名稱,右邊有個小三角形,點選後選擇「管理 Webhook」
  3. 一開始如果沒有設定 webhook,會直接讓你輸入名稱跟圖片網址。
    名稱可以輸入 fastlane,之後新增其他的服務連動,會比較清楚是哪個來源打的。
    圖片網址可以不設,也可以取找對應的icon網址填入。
  4. 點選「儲存」後就可以產生並取得 Webhook 的連結囉!

在 Google Chat 設定並取得 Webhook

最後,參考作者的範例修改就OK了。

1
2
3
4
5
6
7
8
9
10
11
12
13
package = load_json(json_path: "../package.json")
curr_build_number = get_build_number()

google_chat(
imageUrl: 'https://xxx.ooo.png',
webhook: 'https://chat.googleapis.com/v1/spaces/xxx/messages?key=ooo&token=jjj',
title: 'ZZZ App 開發版',
description: 'Android Debug / iOS Enterprise 已更新,請前往下載安裝!',
section1Title: 'Version',
section1Description: "v" + package["version"] + "-" + curr_build_number,
buttonTitle: "開啟下載安裝頁",
buttonUrl: "https://fff.com"
)

訊息通知的顯示

執行 fastlane 任務

上面這些 fastlane 的任務,要怎麼串連起來呢?

通常我們會把這些單一任務分別用 private_lane包覆起來,表示這是不能單獨執行的。接著再新增一個用 lane包覆起來的任務,工作內容就是依序執行這些私有任務。

private_lane 的寫法:

1
2
3
4
desc "Android Debug: Build a Debug App"
private_lane :debug_build do
gradle(task: "clean assembleDebug")
end

在 Android 裡的 lane

Android
1
2
3
4
5
6
7
8
desc "Android Debug: Do all jobs"
lane :debug_jobs do
version_code
version_name
git_version_tag
debug_build
debug_upload
end

在 iOS 裡的 lane

iOS
1
2
3
4
5
6
7
8
desc "iOS Enterprise: Do all jobs"
lane :enterprise_jobs do
build_number
version
enterprise_archive
enterprise_upload
notify_chat
end

為了更方便執行指令,可以新增npm指令。

有個題外話,我在切到 ios 的目錄後,執行 fastlane 的前綴有加 arch -x86_64,原因是我目前使用 Mac m1,因為硬體上的限制才需要加。如果之後在安裝什麼或執行什麼時發生意外錯誤,可以試著加加看。

1
2
3
"scripts": {
"dist-dev-app": "cd android && fastlane debug_jobs && cd .. && cd ios && arch -x86_64 fastlane enterprise_jobs && cd ..",
}

更新 CHANGELOG.md

最後,就是產出 CHANGELOG.md,並更新到 git 上囉!

同樣我們可以為冗長的 cli 新增 npm 指令:

1
2
3
"scripts": {
"cg": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git commit -am CHANGELOG.md"
}

CHANGELOG.md

結論

在導入這些工具時,其實不到半天的時間就可以完成了。學習門檻來說,也不用太高,頂多只需要查詢一些 ruby 的基本語法等,算是很值得上手的工具。
比較重要的是要思考任務的安排合不合理,在自動化和後續維護上是不是可以更順暢;另外在部署上架版的流程和跟開發階段的流程勢必會不太一樣。到時候有什麼新想法,再跟大家分享囉 👋

行動應用|自動化開發階段的更版流程|fastlane|conventional-changelog

https://yuri-journal.me/軟體開發/2021041514/

作者

Yuri Tsai

發表於

2021-04-15

更新於

2022-06-20

許可協議

評論