objc系列譯文(6.5):為iOS專案搭建Travis CI伺服器

袁欣發表於2013-11-28

你是否曾經試著為你的iOS專案搭建一臺支援持續整合的伺服器,從我的個人經驗而言,這可不是一個輕鬆的活。你需要準備一部Mac,安裝好全部所需的軟體和外掛。你要負責管理所有的使用者賬戶並提供安全保護。

原本你想節省的時間,最終你會發現你花費了大量的時間去維護這臺伺服器。不過如果你的專案託管在GitHub上,現在有了新的希望:Travis CI。該服務可以為你的專案提供持續整合的支援,也就意味著它會負責好託管一個專案的所有細節。在Ruby的世界中,Travis CI已久負盛名。從2013年4月開始,Travis也開始支援iOS和Mac平臺。

在這篇文章中,我會向你展示如何一步步為你的專案整合Travis。不僅包括編譯專案和進行單元測試,還能夠將你的應用投送到你所有的測試裝置上。為了演示,我在GitHub上放了一個示例專案。在這篇文章的最後,我會教你如何用Travis去定位程式中的錯誤。

GitHub整合

我最喜歡Travis的一點就是他與GitHub的Web UI整合的非常好。譬如pull請求。Travis會為每次請求都執行編譯操作。如果一切正常,pull請求在GitHub上看起來就像這樣:

萬一編譯不成功,GitHub頁面會相應的改變顏色給予提醒:

連結Travis和GitHub

讓我們看一下如何連結你的GitHub專案到Travis。使用你的GitHub賬號登陸Travis。對於私有工作目錄,你需要註冊一個Travis專業版賬號。

登陸成功後,你就可以為你的專案開啟Travis支援。找到屬性頁面,在此列出了你的所有GitHub專案。不過要注意,如果你此後建立了一個新的工作目錄,要使用Sync now按鈕進行同步。Travis只會偶爾更新你的專案列表。

現在只需要開啟這個開關就可以為你的專案新增Travis服務。以後你會看到Travis會和你的GitHub專案設定相關聯。下一步應該告訴Travis當它收到專案改動之後該做什麼。

輕量級的專案配置

Travis CI需要你的專案的一些基本資訊。在你專案的根目錄建立一個名叫.travis.yml的檔案,內容如下:

Travis編譯器執行在虛擬機器環境下.已經使用Ruby,Homebrew,CocoaPods和一些編譯指令碼進行過配置。上述的配置項已經足夠編譯你的專案了。

預裝的編譯指令碼會分析你的Xcode專案,編譯專案下的所有Target。如果所有檔案都沒有編譯錯誤測試也沒有跳出專案就編譯成功了。現在可以將你的改動Push到GitHub看看能成功編譯。

雖然這看起來好像很簡單,不過對你的專案不一定適用。幾乎沒有什麼文件來指導使用者如何配置預設的編譯行為。舉個栗子, 有一次我沒有用模擬器的SDK導致檢查應用簽名時發生了錯誤。如果剛剛那個最輕量級的配置對你的專案不適用的話,讓我們來看一下如何用定製的編譯命令來適用Travis。

定義編譯命令

Travis使用命令列來編譯你的專案。因此,第一步就是使專案能夠在本地編譯。作為Xcode命令列工具的一部分,Apple提供了xcodebuild命令。

開啟你的終端輸入:

這會列出xcodebuild可用的所有引數。如果命令執行不成功,確保你的命令列工具已經成功安裝。通常一個常見的編譯命令看起來像這樣:

使用iphonesimulator SDK是為了避免應用簽名錯誤。這是必須的一步直到我們稍後引入證書為止。通過設定ONLY_ACTIVE_ARCH=NO我們可以確保在模擬器的架構下編譯。你也可以設定額外的屬性。用man xcodebuild來閱讀文件。

對於使用CocoaPods的專案,你需要指定workspace和scheme。

Schemes是由Xcode自動生成的,但這在伺服器上不會發生。確保所有的scheme都被設為shared並加入到工作目錄中。否則它只會在本地工作而不會被Travis CI識別。

我們的示例專案下的.travis.yml檔案現在應該看起來像這樣:

執行測試

通常對於測試來說你會使用如下的命令(注意test屬性)

xcodebuild test -workspace {workspace}.xcworkspace -scheme {test_scheme} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

不幸的是xcodebuild並不能支援多target以及iOS的應用測試。蘋果已經在著手解決這個問題,不過我建議使用Xctool來代替。

Xctool

Xctool是來自Facebook的命令列工具,他可以幫助你更加輕鬆快捷的編譯測試你的應用。他的彩色輸出資訊比xcodebuild更加簡潔直觀,結構清晰。同時還新增了對邏輯測試,應用測試的支援。

Travis中已經預裝了xctool。要在本地測試的話,需要用Homebrew先安裝xctool:

用法非常簡單,xctool 使用跟 xcodebuild相同的引數:

xctool test -workspace TravisExample.xcworkspace -scheme TravisExampleTests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

這要這些命令在本地能正常工作,我們就可以把他們新增到.travis.yml檔案裡:

language: objective-c

script:

  – xctool -workspace TravisExample.xcworkspace -scheme TravisExample -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

  – xctool test -workspace TravisExample.xcworkspace -scheme TravisExampleTests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

目前我們所新增的配置已經足夠編譯一個框架類的應用。我們能夠保證專案可以正常編譯並通過測試。但對於真正的iOS應用來說,我們希望在真實的物理裝置上進行測試。很顯然,我們要藉助Travis來幫我們自動部署。整個過程的第一步,我們需要給我們的應用簽名。

應用簽名

為了在Travis中給我們的應用簽名,我們需要準備好所有必要的證書和配置檔案。就像每個iOS開發人員知道的那樣,這可能是最困難的一步。後面我們將寫一些指令碼來幫助我們在伺服器上給應用簽名。

證書和配置檔案

  1. 蘋果全球開發者關係認證

從蘋果的配置頁面中下載或者從你的Keychain中匯出,將它儲存到你的專案目錄下scripts/certs/apple.cer這個位置。

2.  iPhone釋出證書 + 私鑰

如果你還沒有一個iPhone釋出證書你需要建立一個。登陸你的蘋果開發者賬號,你可以跟隨下面的步驟建立一個生產環境的新證書(Certificates > Production > Add > App Store and Ad Hoc)。確保你已經下載並安裝了這個證書。以後你可以在你的Keychain中找到它,還有一個捆綁的私鑰。現在開啟你Mac上的Keychain應用:

右擊證書選擇匯出將其放在scripts/certs/dist.cer路徑,在匯出捆綁的私鑰,儲存到scripts/certs/dist.p12。可以根據你的需要設定一個密碼。

Travis需要知道你的私鑰密碼,我們需要將其儲存在某個地方。顯然我們不想用簡單的文字來儲存這個密碼。我們可以利用Travis的安全環境變數。開啟終端進入包含.travis.yml檔案的目錄。首先用gem install travis命令安裝Travis gem。安裝完成後你就可以用以下命令新增密碼:

travis encrypt “KEY_PASSWORD={password}” –add

這樣就可以安裝一個叫做KEY_PASSWORD的加密環境變數到你的.travis.yml配置檔案。在任何可以被Travis CI執行的指令碼中都可以使用這個變數。

3. iOS 移動裝置備案檔案(釋出用)

如果你還沒有一個釋出用的移動裝置備案檔案。根據你的開發者賬號型別,你可以選擇Ad Hoc或者In House兩種不同的備案檔案(Provisioning Profiles > Distribution > Add > Ad Hoc or In House).下載將其儲存到scripts/profile/目錄下。

我們需要在Travis中訪問此備案檔案,所以我們需要將此檔案的名字儲存為一個全域性變數。譬如我們可以將其命名為TravisExample_Ad_Hoc.mobileprovision,像這樣新增:

這裡還有兩個宣告的全域性變數。APP_NAME通常指的就是你的專案主target的名字。DEVELOPER_NAME裡是你在專案主target下Xcode Build Settings 中Code Signing Identity > Release裡面看到的名字。最後搜尋一下你應用的Ad Hoc或者In House配置檔案,將其中的黑體文字全部去掉。根據你設定的不同,在一些屬性的方括號裡面可能不會有任何資訊。

加密證書和備案檔案

不過你的GitHub許可權是公開的。你可能會想要給你的證書和備案檔案加密,因為他們包含了你應用的重要資訊。如果你使用的是一個私有目錄,你可以跳過這一小節。

首先我們需要想出一個密碼來加密我們所有的檔案。在下面的例子中,我們使用foo這個單詞,你完全可以將其替換為使用於你專案的更安全的密碼。在命令列中需要使用openssl來加密這些敏感檔案:

這樣可以建立.enc字尾的加密版本證書和配置檔案。現在你可以刪除或者忽略那些原始檔案。但是有一點千萬不要提交未加密的證書與配置檔案,否則它們會出現在GitHub上。如果你不小心這樣做了,立刻去尋求幫助。

現在我們的檔案都已經加密,我們需要告訴Travis如何解密。所以我們需要提供解密的密碼,使用之前建立KEY_PASSWORD變數一樣的方式:

travis encrypt “ENCRYPTION_SECRET=foo” –add

Lastly, we have to tell Travis which files to decrypt. Add the following commands to the before-script phase in the .travis.yml:

最後我們需要告訴Travis那些檔案需要解密。在.travis.yml檔案的before-script階段新增如下命令:

現在你在GitHub上的都是安全的了,Travis可以正常的讀取使用它們。但是還有一個安全問題你需要知道:在Travis的編譯日誌裡可能會顯示出解密的密碼,不過對pull請求來說不會出現。

新增指令碼

現在我們需要確保所有的證書都匯入到Travis CI的鑰匙串中。這一步我們需要在scripts資料夾下面新增一個新的檔案add-key.sh:

這裡我們建立了一個新的臨時keychain叫做ios-build,其中包含了所有的證書。注意這裡我們使用$KEY_PASSWORD來匯入私鑰。在最後一步,備案檔案會拷貝到Library資料夾。

在建立這個檔案後,確保給予它可執行的許可權。在命令列裡輸入chmod a+x scripts/add-key.sh。下面所有的指令碼都需要執行這一步。

現在所有的證書和配置檔案都已就緒我們可以開始給我們的應用簽名。注意一定要先編譯應用再給其簽名。由於我們需要知道編譯好的二進位制檔案的位置,我推薦在編譯命令中使用OBJROOT和SYMROOT這兩個變數。另外,我們應該把SDK設定為iphoneos,configuration項改為Release。

xctool -workspace TravisExample.xcworkspace -scheme TravisExample -sdk iphoneos -configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build ONLY_ACTIVE_ARCH=NO

執行這個命令,你會發現應用編譯後的二進位制檔案出現在build/Release-iphoneos資料夾下面。現在我們可以使用如下的指令碼給應用簽名和打包。

第二行到第九行尤其重要。你不會想要給一個試驗用的分支建立一次新的release。對pull請求也是一樣。環境變數已經被禁用,所以pull請求也不會編譯。

第十四行才是真正的簽名操作。這會使build/Release-iphoneo檔案下下面出現兩個新檔案TravisExample.ipa和TravisExample.app.dsym。第一個檔案包含了將被安裝到手機的應用。dsym檔案包含了二進位制檔案的除錯資訊。這個檔案對記錄以應用在裝置上的crash非常重要。後面我們將會使用它們來部署我們的應用。

最後要新增一些指令碼語句移除臨時的Keychain,將備案檔案刪除。你可以在本地測試一下這些語句,雖然不是很必要。

最後一步我們需要告訴Travis什麼時候執行這三個指令碼。私鑰需要在應用編譯前新增,然後再進行簽名和清除等操作。在.travis.yml檔案裡新增如下語句:

完成這一步,我們可以將所有的檔案放到GitHub上等待Travis給我們的應用簽名。我們可以在專案頁面下的Travis控制檯驗證簽名是否成功。如果一切正常,我們來看一下如何將簽名好的應用提交給測試人員。

釋出應用

有兩個知名的服務可以幫助你釋出你的應用:TestFlight and HockeyApp。不管哪一個都足夠滿足你的需要。個人比較推薦HockeyApp,不過兩種服務我都會教你怎麼整合到專案裡。

我們在sign-and-build.sh中新增指令碼語句,首先新增一些釋出日誌:

注意這裡我們使用了一個Travis的全域性環境變數(TRAVIS_BUILD_NUMBER)。

TestFlight

建立一個TestFlight賬號並配置好你的應用。為了使用TestFlight的API,你需要先獲得api_tokenteam_token,在強調一次,我們需要確保它們是加密的,在命令列執行以下命令:

現在我們可以呼叫相應的API了。新增以下命令到sign-and-build.sh:

千萬不要使用-v標記,這樣會暴露你的加密token。

HockeyApp

註冊一個HockeyApp賬號,建立一個新應用。從網站上找到App ID資訊。下面我們要生成API token。如果你希望自動部署最新的版本給測試人員,需要選擇Full Access版本。

加密所有的token:

在從sign-and-build.sh中呼叫API:

注意我們上傳了dsym檔案。如果你整合了TestFlight和HockeyApp SDK,你可以立刻收到易讀的Crash報告。

Travis故障檢查

超過一個月的Travis使用經歷並不是完美的。知道如何不通過訪問編譯環境就能找出問題是非常重要。

寫這篇文章時,還沒有可用的Travis模擬器截圖可以下載。如果Travis不能正常編譯了,第一步先試著在本地重現問題。在本地執行跟Travis使用的相同的編譯命令:

xctool …

如果要除錯指令碼語句,你要先定義環境變數。我的方法是建立一個全新的指令碼來設定所有的環境變數。將這個指令碼新增到.gitignore檔案中,你不會想把它暴露給其他人。我的config.sh檔案如下:

執行config.sh匯出這些環境變數(確保config.sh有足夠的許可權),

接著輸入echo $APP_NAME命令看看它能否正常工作,你不用做任何修改就可以在本地執行這些指令碼語句。

如果你在本地得到的是不同的編譯資訊,可能好似因為你安裝了不同版本的三方庫。試著模擬和Travis模擬器相同的配置。Travis在他們的網站上裡列出了所有安裝的軟體版本。你也可以在Travis配置檔案中新增除錯資訊找出所有庫檔案的版本:

在本地安裝好於伺服器完全相同的軟體再重新編譯專案。

如果編譯資訊還是不一樣,試著將重新Check out專案到一個新的目錄。確保將所有的快取資訊都清空。Travis會為每次編譯建立一個全新的虛擬機器,所以不存在快取的問題,但在你的本地機器上可能會出現。

一旦你在本地重現出和伺服器上相同的錯誤,你就能定位到問題的根源,當然那取決於你的具體問題。Google一下通常是幫你找出問題的好方法。

如果一個問題影響到了Travis上其他的專案,那麼可能是Travis環境配置的原因。我發現過幾次這樣的問題。如果發生這樣的情況試著聯絡Travis的Support,以我的經歷它們的響應非常迅速。

批評指正

Travis CI跟市面上同類產品相比還是有一些限制。因為Travis執行於一個預先配置好的模擬器,你必須為每次編譯都安裝一遍所有的依賴軟體。這會花費一些額外的時間。不過Travis團隊已經在著手提供一種快取機制來解決這個問題了。

在一定程度上你依賴於Travis所提供的配置。比如你只能使用Travis內建的Xcode版本進行編譯。如果你本地使用的Xcode版本較新,你的專案可能會在伺服器上無法編譯。如果Travis能夠為不同的Xcode版本都設定一個對應虛擬機器會更有幫助。

對於複雜的專案來說,你會希望把整個編譯任務分為編譯應用,執行整合測試等等。這樣你可以快速獲得編譯資訊而不用等所有的測試都完成。目前Travis還不能支援有依賴的編譯。

當你將你的專案push到GitHub上時,Travis會自動觸發。不過編譯動作不會立即觸發,你的專案會被放到一個根據專案所用語言不同而不同的一個全域性編譯佇列,不過專業版可以允許併發編譯。

總結

Travis CI提供了一個功能完整的持續整合環境幫助你編譯測試和部署你的iOS應用。對於開源專案來說,這項服務是完全免費的。很多社群專案都得益於GitHub的持續整合能力。你應該已經看過像這樣的按鈕了:

對於商業專案,Travis專業版也能為私有目錄提供快捷簡便的持續整合支援。

如果你還沒有用過Travis,趕緊去試試吧,它棒極了!

相關文章