From: Hailong's Blog (格式比專欄的好!)
作為公司的iOS程序員,少不了在發布應用的時候各種等待。標准的手動發布流程是:編譯->打包上傳->填寫應用更新數據->等待iTunesConnect編譯->選擇版本發布,整個過程大概需要30分鍾左右。關鍵是這個過程就像windows裝系統一樣,雖然手工參與的不多,但是要一直守在電腦前等着。
程序員這么懶,一定會想辦法讓他自動化的。后來發現特別懶的Felix Krause · GitHub)寫的Fastlane,Fastlane可以非常快速簡單的搭建一個自動化發布服務,並且支持Android,iOS,MacOS。他可以實現一條命令從編譯到選版發布全程不用干預。作為程序員的你只要一條命令,看集美劇,發布就完成了。截止剛剛Fastlane官網上宣稱已經為程序員節省了4百萬小時+
這是一篇中速入門,看完這篇文章,自己搭建個「iOS自動發布服務」肯定是沒問題了。這篇文章很長,有很多細節,可以當文檔查。
文章難度:★☆☆☆☆
系統要求
- 會編程
- Xcode7+
- Mac OS 10.11+
Fastlane組件
Fastlane是一套工具集,包括:
- 測試
- scan => 自動運行測試工具,並且可以生成漂亮的HTML報告
- 證書,配置文件
- 截圖
- 編譯
- shenzhen => 當年大名鼎鼎的自動編譯工具,現在已經被棄用了
- gym => Fastlane家族的自動化編譯工具,和其他工具配合的非常默契
- 發布
- TestFlight管理
- 輔助工具
- spaceship => 為pilot,boarding和deliver等工具提供和 iTC 和 ADC 的交互API。spaceship本來是個獨立的項目,后來被Fastlane收編進來
- WatchBuild => 是一個獨立的iTC監控工具,開啟WatchBuild可以監控iTC上的文件狀態,彈出MacOS自帶的Notification
- Android
- supply => 自動上傳到Google Play工具(如果有時間,我想把國內提供API的Android Store都寫個插件自動上傳,這個問題從10年我剛開始工作就覺得是個痛點)
- screengrab => Android的自動截圖工具
Fastlane中的概念
fastlane命令是一個流程控制的命令行工具(CLI),通過內部集成action和第三方的action完成一系列控制流程。運行fastlane命令行工具,會讀取當前目錄或者./fastlane目錄下的Fastfile配置文件。
在Fastfile中:
- action => Fastlane中的每一條命令都是一個擴展(action),上面提到的deliver,sigh之類的工具本身是CLI,但是在Fastlane中內嵌了對他們支持的action
- lane => Fastlane中流程的合集,每一個動作即可以是action,也可以是其他的lane。語法和ruby中的rake非常像
一個簡單的發布流程:
lane :deploy do
# 執行 pod instasll
cocoapods
# 執行 carthage bootstrap
carthage
# 增加build版本號
increment_build_number
# 編譯代碼
gym
# 發布到Apple Store
deliver(force: true)
end
安裝
蘋果的系統升級率非常高,所以現在絕大部分開發者電腦應該是滿足依賴要求的。
確保Xcode Command Line Tools 安裝了最新版
xcode-select --install
如果你單獨安裝過ruby(如果你能看得懂這句),去掉sudo。如果使用系統自帶的ruby,需要sudo權限
[sudo] gem install fastlane
- 如果在安裝中速度過慢,請參照更改Gem鏡像。
初始化
在項目根目錄下,初始化Fastlane:
fastlane init
提問了你的Apple ID,Team的問題之后,fastlane會自動檢測當前目錄下項目的App Name和App Identifier。如果檢測的不對,選擇n自行輸入。
接下來會問你這個app是否需要在iTC和ADC中創建(上一步中如果選擇y會自動檢測是否需要創建),fastlane會調用produce進行初始化,如果現在還不想創建,也可以之后再運行produce init進行這個流程。如果不執行produce的流程,deliver的流程也會被掠過,當然之后也可以deliver init運行完全一樣的流程。
在執行deliver init的過程中,會同步iTC中的所有語言的元數據和截圖,並按照目錄結構組織好。目錄結構應該類似下面:
fastlane
├── Appfile
├── Deliverfile
├── Fastfile
├── metadata
│ ├── copyright.txt
│ ├── en-US
│ │ ├── description.txt
│ │ ├── keywords.txt
│ │ ├── marketing_url.txt
│ │ ├── name.txt
│ │ ├── privacy_url.txt
│ │ ├── release_notes.txt
│ │ └── support_url.txt
│ ├── primary_category.txt
│ ├── primary_first_sub_category.txt
│ ├── primary_second_sub_category.txt
│ ├── secondary_category.txt
│ ├── secondary_first_sub_category.txt
│ ├── secondary_second_sub_category.txt
│ └── zh-Hans
│ ├── description.txt
│ ├── keywords.txt
│ ├── marketing_url.txt
│ ├── name.txt
│ ├── privacy_url.txt
│ ├── release_notes.txt
│ └── support_url.txt
└── screenshots
├── README.txt
├── en-US
│ ├── 一堆png圖片
這里肯定會被創建的是Appfile和Fastfile。如果Deliverfile,screenshots和metadata目錄沒被創建,可以運行deliver init來創建。
- Fastfile => 用來定義所有的lane任務Fastfile幫助
- Appfile => 是用來存儲一些公共信息的,比如app_identifier,apple_id,team_id,itc_team_id等。Appfile幫助
- Deliverfile => deliver的配置文件Deliverfile幫助
PS:
- 這里有個小問題,iTC和ADC中的Team ID是不一樣的,在fastlane init中只會自動在Appfile里寫入ADC的team_id,所以在這個過程中會不停的問你iTC的Team ID,所以在創建完Appfile后,手動在里面添加itc_team_id。
- 這個問答對不同的項目可能有各種各樣的分支。我已經用不同的項目試過很多次了,但是可能還不是全部,所以你還需要見招拆招。
- 在這里可以安心的輸入密碼,所有的密碼都加密保存在系統的Keychain里。
插件
Fastlane的插件是一個或者一組action的打包,單獨發布在fastlane之外。Fastlane Plugin 指南
自從16年5月份推出插件系統以來,現在已經有很多第三方的插件可以使用。查看所有插件:
fastlane search_plugins
這里介紹兩個下文會用到的插件:
-
fastlane-plugin-versioning => 用來修改build版本號和version版本號。
Fastlane內嵌的actionincrement_build_number使用的是蘋果提供的agvtool,agvtool在更改Build的時候會改變所有target的版本號。這時如果你在一個工程里有多個產品的話,每次編譯,所有的Build都要加1,最后就不知道高到哪里去了。
有了fastlane-plugin-versioning不僅可以指定target增加Build,而且可以按照「語義化版本」規范增加Version,當然也可以直接設定Version。
PS:最開始寫iOS時不知道怎么定義Build。現在我一般都直接定義成純數字,比如100起,每次編譯的時候讓他自動加一。
-
fastlane-plugin-firim => 直接把AdHoc或者InHouse打包的ipa上傳到fir.im,供測試下載。
安裝上面的插件
fastlane add_plugin [name] # 安裝方法
fastlane add_plugin versioning
fastlane add_plugin firim
配置發布流程
可以把二進制發布到三個地方:
- 發布測試版到Fir.im
- 發布測試版到TestFlight (這個留到下篇文章寫)
- 發布到Apple Store
發布到http://Fir.im
Lane
有了Fastfile,就可以添加自己的發布流程了。打開Fastfile文件(這里我用Sublime 設定語法為Ruby),如果不出意外的話你生成的Fastfile和我應該差不多。這里我就不貼出來了。
最開始定義了
- fastlane_version => 指定fastlane最小版本
- default_platform => 指定當前平台,可選ios,android,mac
在platform中就是需要修改的重點。先忽略before_all,after_all,error這些方法,這里的lane就是一組任務,上傳到Firim的任務如下
lane :to_firim do
# 如果你用 pod install
cocoapods
# 如果你沒有申請adhoc證書,sigh會自動幫你申請,並且添加到Xcode里
sigh(adhoc: true)
# 以下兩個action來自fastlane-plugin-versioning,
# 第一個遞增 Build,第二個設定Version。
# 如果你有多個target,就必須指定target的值,否則它會直接找找到的第一個plist修改
# 在這里我建議每一個打的包的Build都要不一樣,這樣crash了拿到日志,可以對應到ipa上
increment_build_number_in_plist(target: [target_name])
increment_version_number_in_plist(
target: [target_name],
version_number: '7.1.3'
)
# gym用來編譯ipa
gym(
output_directory: './firim',
export_options: {
method: "ad-hoc", # 這可以不指定
thinning: "<none>"
}
)
# 上傳ipa到fir.im服務器,在fir.im獲取firim_api_token
firim(firim_api_token: [firim_api_token])
end
Sigh
如果你不確定證書目前是否可用,可以用Sigh自動生成獲取證書。Sigh會自動根據Appfile里設置的app_identifier從ADC(蘋果開發者中心)生成證書,並下載到項目根目錄下(不是fastlane目錄),下載后自動安裝。你可以通過指定output_path指定證書下載位置。
PS:建議不要把這個文件夾同步到項目的git中(Fastlane提供了match專門管理所有證書)。可以在.gitignore中可以忽略這個文件夾。
Sigh常用的配置項:
Name Type Description Default adhoc bool 獲取adhoc證書 fasle development bool 更新開發證書,不更新production證書 false force bool 強制更新證書,不管證書是否在ADC中存在 false
iOS里code打包證書有4種,adhoc,inhouse,appstore,development證書。
價格 AppStore證書 In-House證書 AdHoc證書 Development證書 企業帳戶 $299 √ √ √ 公司賬號 $99 √ √ √ 個人賬戶 $99 √ √ √
其中In-House的方式打包的ipa安裝沒有設備的限制。AdHoc打包的ipa必須提前把設備的UDID添加到證書中,並且有100台設備限制。
所以如果你不指定adhoc為true,Sigh會識別帳戶類型,企業帳戶默認生成In-House證書,公司賬號和個人帳戶默認生成AppStore證書。
Gym
Gym常用配置項:
Name Type Description Default scheme string 指定需要編譯的scheme clean bool 是否在編譯前clean false output_directory string 導出目錄 ./ output_name string 導出ipa名字 [app_name].ipa export_options hash/string 這里指定Xcode API的外部配置文件地址,或者配置hash,見下文 export_method string 打包方式,可選項app-store ad-hoc package enterprise development developer-id 如果在fastlane中使用了sigh,這個值會從上下文獲取 include_bitcode bool 是否開啟bitcode Xcode API 默認值為true include_symbols bool 是否生成符號表 Xcode API 默認值為true
Xcode7之后,Xcode API允許我們指定一個plist文件作為額外的配置文件。gym默認會幫你創建這個文件,你可以直接指定配置。更多關於plist可配置項,執行xcodebuild -help查看Available keys for -exportOptionsPlist。
export_method, include_symbols,和include_bitcode 這些參數都是exportOptionsPlist的配置,對應method,uploadSymbols和uploadBitcode。
Gym可以指定配置文件Gymfile。 初始化:
gym init
Firim
http://Fir.im是一個ipa托管網站。你可以用AdHoc或者In-House的方式打包,上傳到http://fir.im發送給測試人員測試。需要先注冊Fir.im帳戶,並生成Token。這種方式適合:
- 小團隊 => 測試的機器數量很少並且非常可控。直接連上開發機,真機調試一次,就可以添加
- 土豪 => 有企業帳戶可以打In-House的包
Firim常用配置項:
Name Type Description Default firim_api_token string 指定http://fir.im的token ipa string ipa地址 如果使用gym,可以通過上下文獲取 icon string icon的path,注意這里有個非常坑的地方,http://fir.im只支持jpg格式的圖片
還有項如app_name等等,是用來配置http://fir.im頁面屬性的。firim --help
Firim的配置文件是Firimfile。初始化:
firim init
Firim是我完全仿照fastlane組件的方式寫的,所以也可以單獨作為CLI使用。
如果有任何Firim的問題請到Firim的issues提問。
發布
配置完以上項,就可以一條命令發布到http://fir.im了:
fastlane to_firim
發布到App Store
Lane
先看下lane:
lane :deploy do
# 如果你用 pod install
cocoapods
# 不帶adhoc參數,sigh會自動生成App Store證書(公司或個人帳戶)
sigh
increment_build_number_in_plist(target: [target_name])
increment_version_number_in_plist(
target: [target_name],
version_number: '7.1.3'
)
# 指定輸出目錄
gym(
output_directory: './build',
)
# 上傳所有信息到App Store
deliver(force: true)
end
Deliver
Deliver可以完全管理與iTC的交互。其中包括:
- 上傳和下載多語言截圖
- 上傳和下載多語言元數據
- 上傳二進制文件
還記得上面初始化的時候初始化的metadata,screenshots目錄么?iTC中的所有的元數據信息都被保存在metadata中,所有的截圖信息都被保存在screenshots中。
metadata:
- 可以很容易的管理對應目錄下的文件和iTC后台的表單項,在執行deliver時會自動被傳到iTC。
- 在metadata目錄下的文件,如copyright.txt,是沒有本地化的,在二層目錄中的文件都是需要對應不同語言的表單項。
- 如果你不想修改某些項的信息,直接把對應的文件刪除即可。
- 所有這些表單項也可以在Deliverfile中指定,Deliverfile中指定的項優先級比文件高
screenshots:
- 如果不想更改截圖,可以把整個截圖目錄刪除
- 如果不使用snapshot(自動化截圖),也可以自己截圖放到對應目錄下,比一張一張上傳iTC快的多。截圖在iTC中的排列順序就是本地文件名的「字母表順序」(在目錄中右擊,按文件名排序)。deliver會識別圖片分辨率,上傳到對應設備中。
如果要通過deliver修改元數據或截圖,你必須提供所有iTC后台中有的語言。比如后台中有「簡體中文」和「英文」,你也必須提供對應的zh-Hans和en-US文件,否則deliver會報缺少語言的錯誤。可以在iTC后台提交的版本中刪除語言。
Deliver常用配置項:
Name Type Description Default ipa string ipa地址 如果使用gym,可以通過上下文獲取 metadata_path string 指定metadata目錄地址 如果在fastlane中./fastlane/metadata,如果作為獨立的命令行應用./metadata screenshots_path string 指定screenshots目錄地址 如果在fastlane中./fastlane/screenshots,如果作為獨立的命令行應用./screenshots skip_binary_upload bool 跳過二進制文件上傳,適用於只想改metadata false skip_screenshots bool 跳過截圖上傳,如果截圖沒有變化,開啟這項節約時間 false skip_metadata bool 跳過元數據上傳 false force bool deliver會在上傳時匯總信息生成HTML也,等待你審核。跳過這項審核此項設為true false submit_for_review bool 上傳完成是否自動提交審核 false automatic_release bool 審核通過是否自動釋放 false price_tier int App價格級別。注意:這項提交當時就會生效,所以更改價格還是在后天手動操作 submission_information hash 這是在iTC上點擊提交之后的問答表格,可選項app_review_information hash 提供審核時的信息,詳情 app_icon string 指定icon圖片地址,必須為png格式
-
submission_information =>
- 前綴export_compliance => 對應「出口合規信息」,沒有特殊情況都選false就可以。
- 前綴content_rights => 問你是否包含,顯示,訪問第三方內容(這項我沒在我提交過程中找到),沒有特殊情況也都選false就可以。
-
前綴add_id_info => 可就關鍵了,對應「廣告標識符」,如果你在App中使用了IDFA。你必須在這給個理由,而不能直接選false。
下圖等價下表,App中投放了廣告。
submission_information({ export_compliance_available_on_french_store: "false", export_compliance_contains_proprietary_cryptography: "false", export_compliance_contains_third_party_cryptography: "false", export_compliance_is_exempt: "false", export_compliance_uses_encryption: "false", export_compliance_app_type: nil, export_compliance_encryption_updated: "false", export_compliance_compliance_required: "false", export_compliance_platform: "ios", content_rights_contains_third_party_content: "false", content_rights_has_rights: "false", add_id_info_limits_tracking: "true", add_id_info_serves_ads: "true", add_id_info_tracks_action: "false", add_id_info_tracks_install: "false", add_id_info_uses_idfa: "true" });
deliver的CLI工具:
- 下載iTC上的截圖deliver download_screenshots
- 下載iTC上的元數據 deliver download_metadata
發布
配置后,還是一條命令:
fastlane deploy