小團隊的自動化發布-Fastlane帶來的全自動化部署
「文章標題雖然說是小團隊的自動發布工具,但是這篇文章適合只要你們公司還沒有自動化發布流程。」
From: Hailongs 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報告
- 證書,配置文件
- cert => 自動創建管理iOS代碼簽名證書
- sigh => 一聲嘆息啊,這麼多年和Provisioning Profile戰鬥過無數次。總是有這樣那樣的問題導致配置文件過期或者失效。sigh是用來創建、更新、下載、修復Provisioning Profile的工具。
- pem => 自動生成、更新推送配置文件
- match => 一個新的證書和配置文件管理工具。我會另寫一篇文章專門介紹這個工具。他會所有需要用到的證書傳到git私有庫上,任何需要配置的機器直接用match同步回來就不用管證書問題了,小團隊福音啊!
- 截圖
- snapshot => 用Xcode7推出的UI test功能實現自動化截圖
- frameit => 可以把截的圖片自動套上一層外邊框
- 編譯
- shenzhen => 當年大名鼎鼎的自動編譯工具,現在已經被棄用了
- gym => Fastlane家族的自動化編譯工具,和其他工具配合的非常默契
- 發布
- produce => 如果你的產品還沒在iTunes Connect(iTC)或者Apple Developer Center(ADC)建立,produce可以自動幫你完成這些工作
- deliver => 自動上傳截圖,APP的元數據,二進位(ipa)文件到iTunes Connect
- TestFlight管理
- pilot => 管理TestFlight的測試用戶,上傳二進位文件
- boarding => 建立一個添加測試用戶界面,發給測試者,可自行添加郵件地址,並同步到iTC
- 輔助工具
- 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 versioningfastlane 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常用的配置項:
NameTypeDescriptionDefaultadhocbool獲取adhoc證書fasledevelopmentbool更新開發證書,不更新production證書falseforcebool強制更新證書,不管證書是否在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常用配置項:
NameTypeDescriptionDefaultschemestring指定需要編譯的schemecleanbool是否在編譯前cleanfalseoutput_directorystring導出目錄./output_namestring導出ipa名字[app_name].ipaexport_optionshash/string這裡指定Xcode API的外部配置文件地址,或者配置hash,見下文export_methodstring打包方式,可選項app-store ad-hoc package enterprise development developer-id如果在fastlane中使用了sigh,這個值會從上下文獲取include_bitcodebool是否開啟bitcodeXcode API 默認值為trueinclude_symbolsbool是否生成符號表Xcode API 默認值為trueXcode7之後,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常用配置項:
NameTypeDescriptionDefaultfirim_api_tokenstring指定http://fir.im的tokenipastringipa地址如果使用gym,可以通過上下文獲取iconstringicon的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常用配置項:
NameTypeDescriptionDefaultipastringipa地址如果使用gym,可以通過上下文獲取metadata_pathstring指定metadata目錄地址如果在fastlane中./fastlane/metadata,如果作為獨立的命令行應用./metadatascreenshots_pathstring指定screenshots目錄地址如果在fastlane中./fastlane/screenshots,如果作為獨立的命令行應用./screenshotsskip_binary_uploadbool跳過二進位文件上傳,適用於只想改metadatafalseskip_screenshotsbool跳過截圖上傳,如果截圖沒有變化,開啟這項節約時間falseskip_metadatabool跳過元數據上傳falseforcebooldeliver會在上傳時匯總信息生成HTML也,等待你審核。跳過這項審核此項設為truefalsesubmit_for_reviewbool上傳完成是否自動提交審核falseautomatic_releasebool審核通過是否自動釋放falseprice_tierintApp價格級別。注意:這項提交當時就會生效,所以更改價格還是在後天手動操作submission_informationhash這是在iTC上點擊提交之後的問答表格,可選項app_review_informationhash提供審核時的信息,詳情app_iconstring指定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
後記
終於寫了第二篇分享,第一篇還是3年前在知乎上的問答。這篇文章前後用業餘時間改了一個星期,主要時間都放在驗證各種情況,核實很多細節,盡量能把提到的說的詳細。以前讀一些入門文章,經常我的環境稍微不對,可能就跑不通了(當然,這篇也不可能覆蓋所有情況)。所以最後也導致寫的太長,而且有很多廢話,希望大家諒解。
寫文章真沒有想像中容易。以前看別人寫的文章,讀可能只要10分鐘,作者可能要寫幾個小時。感謝所有人的分享!
寫文章是個好習慣。程序員的工作雖然辛苦,但是太單調安逸。有時感到很絕望,感覺生命就這樣一直走到底,再也不會改變。我想逼自己一次,做點自己不喜歡或者不擅長的事。
這篇文章還沒寫完,因為太長,只能分成兩部分。另一部分會講下fastlane一些進階用法,還有自動上傳到TestFlight => pilot,還有證書管理工具match
最後,謝謝大家的觀看!
推薦閱讀:
※iOS 如何進行逆向工程?
※發布 App 時,如何合理設置關鍵詞?
※敢吃的 iOS 客戶端如何做到上線第一天衝到美食分類第一的?
※iOS 有哪些常用的第三方類庫?