嗨大家好,我是 yu,幾個月前我寫下了 iOS CI/CD 的自動化詳細解析(iOS App 環境管理 : 靈活運用 Xcode Scheme、GitLab 和 Fastlane 設置不同的開發環境)。在那篇文章中,我們模擬了各種實戰中會遇到的環境狀況,以及如何針對這些狀況分配自動化工具,完成我們期望而且環境乾淨的技巧。時至今日,Flutter 開始大放異彩,是時候來把這套核心邏輯放置到 Flutter 上了!
首先,這套腳本與理論已經在我進行的專案中經過無數次的驗證,而且前置作業與腳本較為複雜。不過沒有關係,我已經為大家準備好起始的開源模板,這套模板甚至能用在各個 flutter
起始專案上哦!
在我們開始之前,請先確認一下環境中已經裝有以下需要的指令集與開發軟體!環境需求列表:
npx
oryarn
Xcode 12
VS Code
或習慣使用的編輯器Android SDK
Flutter SDK
Cocoapods
Fastlane
Gitlab CI Runner
(機器上需要可以運行 iOS 以及 Flutter SDK,並且需要帶有 flutter 與 ios 標籤,如下圖所示)
- 如果不太會設定
Gitlab CI
的朋友可以參考之前這篇文章:GitLab 初學者指南: 輕鬆為 iOS 專案設定持續集成(CI/CD)。如果要集成在其他平台上也沒問題,只要可以執行iOS
,Andorid SDK
,Flutter SDK
,Cocoapods
,Fastlane
即可,同時需要撰寫相關平台的定義檔及腳本。
啟動專案
好的!環境設置好之後,就先讓我們啟用開始模板吧!
npx @klearthinkk/create-ktnest-temp
或
yarn create @klearthinkk/ktnest-temp
執行完成後,讓我們選擇 flutter-gitlab
:
然後輸入專案名稱:
執行並且等待完成:
讓我們打開專案觀察一下,可以發現指令產生的模板已經有許多預設的檔案設置:
其中 fastlane
是已經開發完成的腳本,包含測試、部署功能;main_prod.dart
代表正式環境的入口;main_staging.dart
代表測試環境的入口;main.dart
代表開發環境入口;而 .gitlab-ci.yml
代表 Gitlab CI
的設定檔。
Flutter Flavor 設定
Flutter Flavor 可以幫助我們建構不同環境的 App,詳細資料可以參考這份文件。好的!讓我們先來設置 Xcode 的 Flavor!
首先,直接用快捷方式打開 Flutter
Xcode
專案:
新增兩個 Scheme
Staging
與 Prod
代表測試環境與正式環境:
接著,讓我們點擊 PROJECT
設置 Configurations
:
這邊我們多設置了四種:
- Debug-Staging
- Debug-Prod
- Release-Staging
- Release-Prod
分別代表 Flutter 模式 + Flavor。Release-Prod
就代表使用 Release
模式,並且使用 Flavor
Prod
。
設置完成後,讓我們再次回到 Flutter
專案中,並且使用指令 cd ios && fastlane test
來驗證我們的測試結果。
如果看到這個畫面就代表設置成功!好的,讓我們換到 Android
平台!
首先,使用 Android Studio
打開 Flutter
專案中的 Android
專案。
並且切換到 app/build.gradle
加入以下程式碼:
flavorDimensions "flavor-type"
productFlavors {
Staging {
dimension "flavor-type"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
resValue "string", "app_name", "內部測試版"
}
Prod {
dimension "flavor-type"
resValue "string", "app_name", "正式版"
}
}
與 iOS
相同,我們建立兩種 Flavor
Staging
與 Prod
:
設置完成之後,同樣地我們回到 Flutter
專案中,並且輸入指令 cd.. && cd android && fastlane test
:
成功的話一樣會看到這個畫面!
試驗
當我們做好這些設定的時候,就可以啟動 App
測試一下了!讓我們在介面上新增幾個設定檔案。
將入口跟 Flavor
設置一下,並且修改一下程式碼:
然後看看成果!
我們完成這些環境配置了!
部署設置
部署設置我們一樣為大家做好腳本了,但是有些前置動作需要大家先行完成。
Android
Android 推上 Google Play 時,需要設置一些 API 權限。拿到權限後到 android/fastlane/Appfile
設置:
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
package_name("") # e.g. com.krausefx.app
for_platform :android do
for_lane :beta do
package_name ""
end
end
json_key_file
代表 Google Play API Services
的授權,通常是 json
檔案。讓我們在 package_name
輸入於商店設定好的名稱,並在下面 beta
模式時添加 .staging
,讓 fastlane
運行時自動判斷與替換。
先在 app/build.gradle
加入以下設定。
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
同時,在 app
目錄下把要簽名的 key store
放入資料夾中。在 Android
目錄底下新增檔案 key.properties
,這是關於 android key store
的密碼,內容如下:
storePassword={{store 密碼}}
keyPassword={{store key 密碼}}
keyAlias={{keyAlias}}
storeFile={{store 檔名}}
記得要移除 {{}}。
接下來,使用已經寫好的 fastlane betabuild
或 fastlane beta
,就可以將 APK or AAB
上傳部署至 Google Play Console
。
iOS
iOS 部分比較單純,先設置好自動簽名跟 App Connent
的 identifier
,然後到 ios/fastlane/Appfile
填入相關資訊。
app_identifier("") # The bundle identifier of your app
apple_id("") # Your Apple email address
itc_team_id("") # App Store Connect Team ID
team_id("") # Developer Portal Team ID
for_platform :ios do
for_lane :beta do
app_identifier "{{your prod app_identifier}}-staging"
end
for_lane :betagitlab do
app_identifier "{{your prod app_identifier}}"
end
end
# For more information about the Appfile, see:
# https://docs.fastlane.tools/advanced/#appfile
接下來,到 ios/fastlane/Fastlane
設置好 App 密碼ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = ""
如果不了解如何產生 App 專用密碼,可以參考這篇文件。
然後,同樣使用 fastlane betabuild
或 fastlane beta
,就可以將 iOS App
部署到商店中!
Fastlane 腳本分析
首先,先讓我們分析下 android/fastlane/Fastfile
的腳本設置。腳本主要以 beta
部署為主,其他腳本其實都是一些參數的微小變化。
desc "Submit a new build to apk"
lane :beta do
# Return the number of commits in current git branch
build_number = number_of_commits(all: true)
ci_pipeline_id = ENV['CI_PIPELINE_ID']
if !ci_pipeline_id.nil? && !ci_pipeline_id.empty?
build_number = ci_pipeline_id
end
Dir.chdir "../.." do
sh("flutter", "packages", "get")
sh("flutter", "clean")
sh("flutter", "build", "appbundle", "-t", "lib/main_staging.dart", "--flavor=Staging", "--build-number=#{build_number}")
end
supply(track: "internal", aab:"../build/app/outputs/bundle/StagingRelease/app-Staging-release.aab", rollout: "1.0",
skip_upload_images: true,
skip_upload_screenshots: true,
skip_upload_metadata: true,)
end
build_number = number_of_commits(all: true)
這邊,適用於取得 commit
次數來作為版本建置號。當在 CI
環境中,這個數字會取代成 CI
工作的代號。
Dir.chdir "../.." do
sh("flutter", "packages", "get")
sh("flutter", "clean")
sh("flutter", "build", "appbundle", "-t", "lib/main_staging.dart", "--flavor=Staging", "--build-number=#{build_number}")
end
這邊是建置 flutter
的核心,因為我們執行的目錄是在 ios or android
之下,想要建置 flutter
就需要回到上層目錄。
接下來,執行建置指令 flutter build appbundle -t lib/main_staging.dart --flavor=Staging --build-number=#{build_number}
。
如此一來,就一口氣把入口、flavor
、build-number
都做了指定。
supply(track: "internal", aab:"../build/app/outputs/bundle/StagingRelease/app-Staging-release.aab", rollout: "1.0",
skip_upload_images: true,
skip_upload_screenshots: true,
skip_upload_metadata: true,)
最後,就是把 aab
檔案上傳到 Google Play
的內部測試版中。
接下來,讓我們看看 ios/fastlane/Fastfile
的 beta
。
desc "Push a new beta build to TestFlight"
lane :beta do
# Return the number of commits in current git branch
build_number = number_of_commits(all: true)
copy_config_file("Staging")
Dir.chdir "../.." do
sh("flutter", "packages", "get")
sh("flutter", "clean")
sh("flutter", "build", "ios", "-t", "lib/main_staging.dart", "--flavor=Staging", "--no-codesign", "--build-number=#{ENV['CI_PIPELINE_ID']}")
end
sigh(force: true)
build_app(workspace: "Runner.xcworkspace", scheme: "Staging", configuration: "Release-Staging")
# upload_to_testflight
pilot(skip_waiting_for_build_processing: true)
# post_slack_message("Staging version #{ENV['CI_PIPELINE_ID']}")
end
建置的邏輯跟方式都跟 android
差不多,build_app
時選擇正確的 scheme
與 configuration
檔案。
總結
自動化的邏輯與方式的核心觀念,不會因為專案或語言的不同而改變,我們都是需要「想辦法」,把環境分開,然後透過工具來自動化。如此一來,就可以大大減少工程師開發時的心智負擔,甚至還發展出快速指令建置專案模板的方式,讓開發工程師可以專注在設計 App 與介面,而不需要花心力思考如何讓 App 能夠與大眾見面。有了這些基礎設施底層,絕對可以讓大家事半功倍。
DevOps 或 CI/CD 自動化的領域需要創意與思考,才能用最精簡的方式,組合出最符合需求的應用場景。工具沒有分好與壞,只是哪一項工具比較適合當下的應用場景。希望今天的分享能夠給大家一些啟發。如果你有任何問題,或者有其他關於自動化、DevOps、微服務、容器化、或者其他軟體工程的問題,都可以聯絡我,很高興為您解答!讓我們下次見。
如果你需要一個範例對照,可以查看這個專案。