我想談談使用 fastlane 的行動應用程式的持續整合和交付。 我們如何在所有行動應用程式上實施 CI/CD、我們如何實現這一目標以及最終發生了什麼。
網路上已經有足夠的關於該工具的資料,而我們一開始就缺乏這些資料,所以我故意不詳細描述該工具,而僅參考我們當時擁有的內容:
文章由兩部分組成:
- 公司行動CI/CD出現的背景
- N應用CI/CD落地技術方案
第一部分更多的是對舊時光的懷念,第二部分是你可以運用在自己身上的經驗。
歷史上是這樣發生的
2015年
我們剛開始開發行動應用程序,然後我們對持續整合、DevOps 和其他時髦的東西一無所知。 每個應用程式更新都是由開發人員親自在他的機器上推出的。 對於 Android 來說,這非常簡單 - 組裝、簽名 .apk
並將其上傳到 Google 開發者控制台,然後對於 iOS,透過 Xcode 的分發工具給我們留下了美好的夜晚 - 嘗試下載存檔經常以錯誤結束,我們不得不重試。 事實證明,最高級的開發人員並不是每月編寫幾次程式碼,而是發布應用程式。
2016年
我們長大了,我們已經考慮過如何將開發人員從一整天的發布中解放出來,第二個應用程式也出現了,這只會讓我們更加走向自動化。 同年,我們第一次安裝了 Jenkins,並編寫了一堆可怕的腳本,與 fastlane 在其文件中顯示的腳本非常相似。
$ xcodebuild clean archive -archivePath build/MyApp
-scheme MyApp
$ xcodebuild -exportArchive
-exportFormat ipa
-archivePath "build/MyApp.xcarchive"
-exportPath "build/MyApp.ipa"
-exportProvisioningProfile "ProvisioningProfileName"
$ cd /Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/
$ ./altool —upload-app
-f {abs path to your project}/build/{release scheme}.ipa
-u "[email protected]"
-p "PASS_APPLE_ID"
不幸的是,到目前為止,只有我們的開發人員知道這些腳本是如何運作的,以及為什麼需要無窮無盡的金鑰堆疊,當再次出現問題時,他們得到了分析日誌的「美好夜晚」。
2017年
今年我們了解到有「快車道」這樣的東西。 那時的訊息並不像現在那麼多——如何啟動、如何使用它。 而且當時這個工具本身還很粗糙:不斷的錯誤只會讓我們失望,很難相信他們所承諾的神奇自動化。
然而,fastlane 核心中包含的主要實用程式是 gym
и pilot
,我們成功啟動了它。
我們的腳本有了一些改進。
$ fastlane gym —-workspace "Example.xcworkspace"
--scheme "AppName"
—-buildlog_path "/tmp"
-—clean
它們已經得到了改進,只是因為並非所有必要的參數 xcodebuild
,您需要指出 - gym
將獨立了解位置和內容。 為了進行更多微調,您可以指定與中相同的鍵 xcodebuild
,只是按鍵的命名更加清晰。
這次,多虧了gym和內建的xcpretty格式化程序,建立日誌變得更加清晰。 這開始節省修復損壞的程序集的時間,有時發布團隊可以自行解決這個問題。
不幸的是,裝配速度測量 xcodebuild
и gym
我們沒有這樣做,但我們會相信文件 - 速度提升高達 30%。
適用於所有應用程式的單一流程
2018年至今
到 2018 年,建置和推出應用程式的過程完全轉移到 Jenkins,開發人員停止從他們的機器上發布,只有發布團隊有權發布。
我們已經希望改進測試和靜態分析的啟動,並且我們的腳本不斷增長。 與我們的應用程式一起成長和變化。 當時大約有 10 個應用程序,考慮到我們有兩個平台,大約有 20 個「活」腳本。
每次我們想要為腳本新增步驟時,我們都必須將這些片段複製並貼上到所有 shell 腳本中。 也許我們可以更仔細地工作,但此類更改通常會以拼寫錯誤而告終,這讓發布團隊花費了很多時間來修復腳本並找出哪個聰明人添加了此命令以及它實際上做了什麼。 一般來說,不能說一個平台的彙編腳本至少有些相似。 儘管他們確實做了同樣的事情。
為了啟動新應用程式的流程,有必要花一天的時間來選擇這些腳本的“新”版本,對其進行調試並說“是的,它有效”。
2018年夏天,我們再次將目光投向仍在發展的快車道。
任務#1:總結所有腳本步驟並在 Fastfile 中重寫它們
當我們開始時,我們的腳本看起來就像一塊腳布,由 Jenkins 的一個 shell 腳本中的所有步驟和拐杖組成。 我們還沒有轉向管道和階段劃分。
我們查看了現有的內容並確定了符合 CI/CD 描述的 4 個步驟:
- 建置 - 安裝依賴項,組裝存檔,
- 測試 - 執行開發人員單元測試,計算覆蓋率,
- sonar - 啟動所有 linter 並將報告傳送至 SonarQube,
- 部署 — 將工件傳送到 alpha (TestFlight)。
如果你不深入細節,省略操作中使用的按鍵,你將得到這個 Fastfile:
default_platform(:ios)
platform :ios do
before_all do
unlock
end
desc "Build stage"
lane :build do
match
prepare_build
gym
end
desc "Prepare build stage: carthage and cocoapods"
lane :prepare_build do
pathCartfile = ""
Dir.chdir("..") do
pathCartfile = File.join(Dir.pwd, "/Cartfile")
end
if File.exist?(pathCartfile)
carthage
end
pathPodfile = ""
Dir.chdir("..") do
pathPodfile = File.join(Dir.pwd, "/Podfile")
end
if File.exist?(pathPodfile)
cocoapods
end
end
desc "Test stage"
lane :test do
scan
xcov
end
desc "Sonar stage (after run test!)"
lane :run_sonar do
slather
lizard
swiftlint
sonar
end
desc "Deploy to testflight stage"
lane :deploy do
pilot
end
desc "Unlock keychain"
private_lane :unlock do
pass = ENV['KEYCHAIN_PASSWORD']
unlock_keychain(
password: pass
)
end
end
事實上,考慮到我們仍然需要的一些拐杖以及我們替換的參數數量,我們的第一個 Fastfile 被證明是可怕的:
lane :build do
carthage(
command: "update",
use_binaries: false,
platform: "ios",
cache_builds: true)
cocoapods(
clean: true,
podfile: "./Podfile",
use_bundle_exec: false)
gym(
workspace: "MyApp.xcworkspace",
configuration: "Release",
scheme: "MyApp",
clean: true,
output_directory: "/build",
output_name: "my-app.ipa")
end
lane :deploy do
pilot(
username: "[email protected]",
app_identifier: "com.example.app",
dev_portal_team_id: "TEAM_ID_NUMBER_DEV",
team_id: "ITS_TEAM_ID")
end
在上面的範例中,我們只需要指定部分參數:這些是建置參數 - 架構、配置、Provision Profile 名稱,以及分發參數 - 開發者帳戶的 Apple ID、密碼、應用程式 ID 等在。 作為第一個近似,我們將所有這些密鑰放入特殊檔案中 - Gymfile
, Matchfile
и Appfile
.
現在,在 Jenkins 中,您可以呼叫簡短的命令,這些命令不會模糊視圖並且易於肉眼讀取:
# fastlane ios <lane_name>
$ fastlane ios build
$ fastlane ios test
$ fastlane ios run_sonar
$ fastlane ios deploy
萬歲,我們很棒
你得到了什麼? 每一步都有清晰的命令。 清理腳本,整齊地排列在 fastlane 檔案中。 我們很高興地跑向開發人員,要求他們將所需的一切添加到他們的儲存庫中。
但我們及時意識到我們會遇到同樣的困難 - 我們仍然會有 20 個彙編腳本,它們會以某種方式開始過自己的生活,編輯它們會更加困難,因為腳本會移動到存儲庫,我們無法訪問那裡。 一般來說,這種方式不可能解決我們的痛苦。
任務#2:為 N 個應用程式取得單一 Fastfile
現在看來,解決問題並沒有那麼困難——設定好變量,然後開始吧。 是的,事實上,問題就是這樣解決的。 但在我們把事情搞砸的那一刻,我們既沒有fastlane 本身的專業知識,也沒有編寫fastlane 的Ruby 的專業知識,也沒有網絡上有用的示例——當時每個寫過fastlane 的人都僅限於一個應用程式的範例。一名開發商。
Fastlane 可以處理環境變量,我們已經透過設定 Keychain 密碼進行了嘗試:
ENV['KEYCHAIN_PASSWORD']
查看我們的腳本後,我們確定了公共部分:
#for build, test and deploy
APPLICATION_SCHEME_NAME=appScheme
APPLICATION_PROJECT_NAME=app.xcodeproj
APPLICATION_WORKSPACE_NAME=app.xcworkspace
APPLICATION_NAME=appName
OUTPUT_IPA_NAME=appName.ipa
#app info
APP_BUNDLE_IDENTIFIER=com.example.appName
[email protected]
TEAM_ID=ABCD1234
FASTLANE_ITC_TEAM_ID=123456789
現在,為了開始在 fastlane 檔案中使用這些金鑰,我們必須弄清楚如何將它們傳遞到那裡。 Fastlane 對此有一個解決方案: .env
, .env.default
, .env.development
.
然後我們決定以稍微不同的方式使用這個函式庫。 讓我們在開發人員的儲存庫中放置的不是 fastlane 腳本及其元訊息,而是該應用程式在檔案中的唯一鍵 .env.appName
.
他們自己 Fastfile
, Appfile
, Matchfile
и Gymfile
,我們將其隱藏在一個單獨的儲存庫中。 那裡隱藏著一個帶有來自其他服務的密碼密鑰的附加文件 - .env
.
你可以看一個例子
在 CI 上,呼叫沒有太大變化;新增了針對特定應用程式的配置金鑰:
# fastlane ios <lane_name> --env appName
$ fastlane ios build --env appName
$ fastlane ios test --env appName
$ fastlane ios run_sonar --env appName
$ fastlane ios deploy --env appName
在運行命令之前,我們使用腳本載入儲存庫。 看起來不太好看:
git clone [email protected]/FastlaneCICD.git fastlane_temp
cp ./fastlane_temp/fastlane/* ./fastlane/
cp ./fastlane_temp/fastlane/.env fastlane/.env
儘管 Fastlane 有一個透過下載 Fastfile 的解決方案,但暫時保留此解決方案 import_from_git
,但僅適用於 Fastfile,不適用於其他文件。 如果你想要“真漂亮”,你可以自己寫 action
.
Android 應用程式和 ReactNative 也做了類似的設置,檔案位於同一個儲存庫中,但位於不同的分支中 iOS
, android
и react_native
.
當發布團隊想要新增一些新步驟時,腳本中的變更會透過 git 中的 MR 進行記錄,不再需要尋找損壞腳本的罪魁禍首,而且一般來說,現在您必須嘗試破壞它。
現在可以肯定的是
以前,我們花時間維護所有腳本、更新它們並修復更新的所有後果。 當發布中的錯誤和停機的原因是簡單的拼字錯誤而很難在混亂的 shell 腳本中追蹤時,這是非常令人失望的。 現在此類錯誤已減少到最低限度。 更改會立即推廣到所有應用程式。 將新應用程式放入流程中需要 15 分鐘 - 在 CI 上設定範本管道並將金鑰新增至開發人員的儲存庫。
似乎 Android 版 Fastfile 和應用程式簽名的要點仍然無法解釋;如果這篇文章有趣,我會繼續寫一篇。 我很高興在評論或 Telegram 上看到您的問題或建議“您將如何解決這個問題”
來源: www.habr.com