鍍金池/ 教程/ iOS/ 為 iOS 建立 Travis CI
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無(wú)人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無(wú)止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

為 iOS 建立 Travis CI

你是否曾經(jīng)試著為 iOS 項(xiàng)目搭建一臺(tái)支持持續(xù)集成的服務(wù)器,從我的個(gè)人經(jīng)驗(yàn)而言,這可不是一個(gè)輕松的活。首先需要準(zhǔn)備一臺(tái) Mac 電腦,并安裝好全部所需的軟件和插件。你要負(fù)責(zé)管理所有的用戶賬戶,并提供安全保護(hù)。你需要授予訪問倉(cāng)庫(kù)的權(quán)限,并配置所有的編譯步驟和證書。在項(xiàng)目運(yùn)行時(shí)期,你需要保持服務(wù)器的穩(wěn)健和最新。

最后,原本你想節(jié)省的時(shí)間,會(huì)發(fā)現(xiàn)你花費(fèi)了大量的時(shí)間去維護(hù)這臺(tái)服務(wù)器。不過如果你的項(xiàng)目托管在 GitHub) 上,現(xiàn)在有了新的希望:Travis CI。該服務(wù)可以為你的項(xiàng)目提供持續(xù)集成的支持,也就意味著它會(huì)負(fù)責(zé)好托管一個(gè)項(xiàng)目的所有細(xì)節(jié)。在 Ruby 的世界中,Travis CI 已久負(fù)盛名。從 2013 年 4 月起,Travis 也開始支持 iOS 和 Mac 平臺(tái)。

在這篇文章中,我將向你展示如何一步步的在項(xiàng)目中集成 Travis。不僅包括項(xiàng)目的編譯和單元測(cè)試的運(yùn)行,還包括將應(yīng)用部署到你所有的測(cè)試設(shè)備上。為了演示,我在 GitHub 上放了一個(gè)示例項(xiàng)目。在這篇文章的最后,我會(huì)教你一些提示:如何用 Travis 去定位程序中的錯(cuò)誤。

GitHub 集成

我最喜歡 Travis 的一點(diǎn)就是它與 GitHub 的 Web UI 集成的非常好。例如 pull 請(qǐng)求。Travis 會(huì)為每次請(qǐng)求都執(zhí)行編譯操作。如果一切正常,pull 請(qǐng)求在 GitHub 上看起來(lái)就像這樣:

http://wiki.jikexueyuan.com/project/objc/images/6-5.jpg" alt="" />

萬(wàn)一編譯不成功,GitHub 頁(yè)面會(huì)修改相應(yīng)的顏色,給予提醒:

http://wiki.jikexueyuan.com/project/objc/images/6-6.jpg" alt="" />

鏈接 Travis 和 GitHub

讓我們看一下如何將 GitHub 項(xiàng)目與 Travis 鏈接上。使用 GitHub 賬號(hào)登錄 Travis 站點(diǎn)。對(duì)于私有倉(cāng)庫(kù),需要注冊(cè)一個(gè) Travis 專業(yè)版賬號(hào)。

登錄成功后,需要為項(xiàng)目開啟 Travis 支持。導(dǎo)航到屬性頁(yè)面,該頁(yè)面列出了所有 GitHub 項(xiàng)目。不過要注意,如果你此后創(chuàng)建了一個(gè)新的倉(cāng)庫(kù),要使用 Sync now 按鈕進(jìn)行同步。Travis 只會(huì)偶爾更新你的項(xiàng)目列表。

http://wiki.jikexueyuan.com/project/objc/images/6-7.jpg" alt="" />

現(xiàn)在只需要打開這個(gè)開關(guān)就可以為你的項(xiàng)目添加 Travis 服務(wù)。之后你會(huì)看到 Travis 會(huì)和 GitHub 項(xiàng)目設(shè)置相關(guān)聯(lián)。下一步就是告訴 Travis, 當(dāng)它收到項(xiàng)目改動(dòng)通知之后該做什么。

最簡(jiǎn)單的項(xiàng)目配置

Travis CI 需要項(xiàng)目的一些基本信息。在項(xiàng)目的根目錄創(chuàng)建一個(gè)名叫 .travis.yml 的文件,文件中的內(nèi)容如下:

language: objective-c

Travis 編譯器運(yùn)行在虛擬機(jī)環(huán)境下。該編譯器已經(jīng)利用 RubyHomebrew,CocoaPods一些默認(rèn)的編譯腳本進(jìn)行過預(yù)配置。上述的配置項(xiàng)已經(jīng)足夠編譯你的項(xiàng)目了。

預(yù)裝的編譯腳本會(huì)分析你的 Xcode 項(xiàng)目,并對(duì)每個(gè) target 進(jìn)行編譯。如果所有文件都沒有編譯錯(cuò)誤,并且測(cè)試也沒有被打斷,那么項(xiàng)目就編譯成功了?,F(xiàn)在可以將相關(guān)改動(dòng) Push 到 GitHub 中看看能否成功編譯。

雖然上述配置過程真的很簡(jiǎn)單,不過對(duì)你的項(xiàng)目不一定適用。這里幾乎沒有什么文檔來(lái)指導(dǎo)用戶如何配置默認(rèn)的編譯行為。例如,有一次我沒有用 iphonesimulator SDK 導(dǎo)致代碼簽名錯(cuò)誤。如果剛剛那個(gè)最簡(jiǎn)單的配置對(duì)你的項(xiàng)目不適用的話,讓我們來(lái)看一下如何對(duì) Travis 使用自定義的編譯命令。

自定義編譯命令

Travis 使用命令行對(duì)項(xiàng)目進(jìn)行編譯。因此,第一步就是使項(xiàng)目能夠在本地編譯。作為 Xcode 命令行工具的一部分,Apple 提供了 xcodebuild 命令。

打開終端并輸入:

xcodebuild --help

上述命令會(huì)列出 xcodebuild 所有可用的參數(shù)。如果命令執(zhí)行失敗了,確保命令行工具已經(jīng)成功安裝。一個(gè)常見的編譯命令看起來(lái)是這樣的:

xcodebuild -project {project}.xcodeproj -target {target} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

使用 iphonesimulator SDK 是為了避免簽名錯(cuò)誤。直到我們稍后引入證書之前,這一步是必須的。通過設(shè)置 ONLY_ACTIVE_ARCH=NO 我們可以確保利用模擬器架構(gòu)編譯工程。你也可以設(shè)置額外的屬性,例如 configuration,輸入 man xcodebuild 查看相關(guān)文檔。

對(duì)于使用 CocoaPods 的項(xiàng)目,需要用下面的命令來(lái)指定 workspacescheme

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

schemes 是由 Xcode 自動(dòng)生成的,但這在服務(wù)器上不會(huì)發(fā)生。確保所有的 scheme 都被設(shè)為 shared 并加入到倉(cāng)庫(kù)中。否則它只會(huì)在本地工作而不會(huì)被 Travis CI 識(shí)別。

http://wiki.jikexueyuan.com/project/objc/images/6-8.jpg" alt="" />

我們示例項(xiàng)目下的 .travis.yml 文件現(xiàn)在看起來(lái)應(yīng)該像這樣:

language: objective-c
script: xcodebuild -workspace TravisExample.xcworkspace -scheme TravisExample -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

運(yùn)行測(cè)試

對(duì)于測(cè)試來(lái)說(shuō),通常使用如下這個(gè)命令 (注意 test 屬性):

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

不幸的是,xcodebuild 對(duì)于 iOS 來(lái)說(shuō),并不能正確支持 target 和應(yīng)用程序的測(cè)試。這里有一些解決方案,不過我建議使用 Xctool。

Xctool

Xctool 是來(lái)自 Facebook 的命令行工具,它可以簡(jiǎn)化程序的編譯和測(cè)試。它的彩色輸出信息比 xcodebuild 更加簡(jiǎn)潔直觀。同時(shí)還添加了對(duì)邏輯測(cè)試,應(yīng)用測(cè)試的支持。

Travis 中已經(jīng)預(yù)裝了 xctool。要在本地測(cè)試的話,需要用 Homebrew 安裝 xctool:

brew update
brew install xctool

xctool 用法非常簡(jiǎn)單,它使用的參數(shù)跟 xcodebuild 相同:

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

一旦相關(guān)命令在本地能正常工作,那么就是時(shí)候把它們添加到 .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

到此為止,介紹的內(nèi)容對(duì)于使用 Travis 的 library 工程來(lái)說(shuō),已經(jīng)足夠了。我們可以確保項(xiàng)目正常編譯并測(cè)試通過。但對(duì)于 iOS 應(yīng)用來(lái)說(shuō),我們希望能在真實(shí)的物理設(shè)備上進(jìn)行測(cè)試。也就是說(shuō)我們需要將應(yīng)用部署到我們的所有測(cè)試設(shè)備上。當(dāng)然,我們希望 Travis 能自動(dòng)完成這項(xiàng)任務(wù)。首先,我們需要給程序簽名。

應(yīng)用程序的簽名

為了在 Travis 中能給程序簽名,我們需要準(zhǔn)備好所有必須的證書和配置文件。就像每個(gè) iOS 開發(fā)人員知道的那樣,這可能是最困難的一步。后面,我將寫一些腳本在服務(wù)器上給應(yīng)用程序簽名。

證書和配置文件

1. 蘋果全球開發(fā)者關(guān)系認(rèn)證

蘋果官網(wǎng)下載證書,或者從鑰匙串中導(dǎo)出。并將其保存到項(xiàng)目的目錄 scripts/certs/apple.cer 中。

2. iPhone 發(fā)布證書 + 私鑰

如果還沒有發(fā)布證書的話,先創(chuàng)建一個(gè)。登錄蘋果開發(fā)者賬號(hào),按照步驟,創(chuàng)建一個(gè)新的生產(chǎn)環(huán)境證書 (Certificates > Production > Add > App Store and Ad Hoc)。然后下載并安裝證書。之后,可以在鑰匙串中找到它。打開 Mac 中的 鑰匙串 應(yīng)用程序:

http://wiki.jikexueyuan.com/project/objc/images/6-9.jpg" alt="" />

右鍵單擊證書,選擇 Export... 將證書導(dǎo)出至 scripts/certs/dist.cer。然后導(dǎo)出私鑰并保存至 scripts/certs/dist.p12。記得輸入私鑰的密碼。

由于 Travis 需要知道私鑰密碼,因此我們要把這個(gè)密碼存儲(chǔ)在某個(gè)地方。當(dāng)然,我們不希望以明文的形式存儲(chǔ)。我們可以用 Travis 的安全環(huán)境變量。打開終端,并定位到包含 .travis.yml 文件所在目錄。首先用 gem install travis 命令安裝 Travis gem。之后,用下面的命令添加密鑰密碼:

travis encrypt "KEY_PASSWORD={password}" --add

上面的命令會(huì)安裝一個(gè)叫做 KEY_PASSWORD 的加密環(huán)境變量到 .travis.yml 配置文件中。這樣就可以在被 Travis CI 執(zhí)行的腳本中使用這個(gè)變量。

3. iOS 配置文件 (發(fā)布)

如果還沒有用于發(fā)布的配置文件,那么也創(chuàng)建一個(gè)新的。根據(jù)開發(fā)者賬號(hào)類型,可以選擇創(chuàng)建 Ad HocIn House 配置文件 (Provisioning Profiles > Distribution > Add > Ad Hoc or In House)。然后將其下載保存至 scripts/profile/ 目錄。

由于 Travis 需要訪問這個(gè)配置文件,所以我們需要將這個(gè)文件的名字存儲(chǔ)為一個(gè)全局環(huán)境變量。并將其添加至 .travis.yml 文件的全局環(huán)境變量 section 中。例如,如果配置文件的名字是 TravisExample_Ad_Hoc.mobileprovision,那么按照如下進(jìn)行添加:

env:
  global:
  - APP_NAME="TravisExample"
  - 'DEVELOPER_NAME="iPhone Distribution: {your_name} ({code})"'
  - PROFILE_NAME="TravisExample_Ad_Hoc"

上面還聲明了兩個(gè)環(huán)境變量。第三行中的 APP_NAME 通常為項(xiàng)目默認(rèn) target 的名字。第四行的 DEVELOPER_NAME 是 Xcode 中,默認(rèn) target 里面 Build SettingsCode Signing Identity > Release 對(duì)應(yīng)的名字。然后搜索程序的 Ad HocIn House 配置文件,將其中黑體文字取出。根據(jù)設(shè)置的不同,括弧中可能不會(huì)有任何信息。

加密證書和配置文件

如果你的 GitHub 倉(cāng)庫(kù)是公開的,你可能希望對(duì)證書和配置文件 (里面包含了敏感數(shù)據(jù)) 進(jìn)行加密。如果你用的是私有倉(cāng)庫(kù),可以跳至下一節(jié)。

首先,我們需要一個(gè)密碼來(lái)對(duì)所有的文件進(jìn)行加密。在我們的示例中,密碼為 “foo”,記住在你的工程中設(shè)置的密碼應(yīng)該更加復(fù)雜。在命令行中,我們使用 openssl 加密所有的敏感文件:

openssl aes-256-cbc -k "foo" -in scripts/profile/TravisExample_Ad_Hoc.mobileprovision -out scripts/profile/TravisExample_Ad_Hoc.mobileprovision.enc -a
openssl aes-256-cbc -k "foo" -in scripts/certs/dist.cer -out scripts/certs/dist.cer.enc -a
openssl aes-256-cbc -k "foo" -in scripts/certs/dist.p12 -out scripts/certs/dist.cer.p12 -a

通過上面的命令,可以創(chuàng)建出以 .enc 結(jié)尾的加密文件。之后可以把原始文件忽略或者移除掉。至少不要把原始文件提交到 GitHub 中,否則原始文件會(huì)顯示在 GitHub 中。如果你不小心把原始文件提交上去了,那么請(qǐng)看這里如何解決

現(xiàn)在,我們的文件已經(jīng)被加密了,接下來(lái)需要告訴 Travis 對(duì)文件進(jìn)行解密。解密過程,需要用到密碼。具體使用方法跟之前創(chuàng)建的 KEY_PASSWORD 變量一樣:

travis encrypt "ENCRYPTION_SECRET=foo" --add

最后,我們需要告訴 Travis 哪些文件需要進(jìn)行解密。將下面的命令添加到 .travis.yml 文件中的 before-script 部分:

before_script:
- openssl aes-256-cbc -k "$ENCRYPTION_SECRET" -in scripts/profile/TravisExample_Ad_Hoc.mobileprovision.enc -d -a -out scripts/profile/TravisExample_Ad_Hoc.mobileprovision
- openssl aes-256-cbc -k "$ENCRYPTION_SECRET" -in scripts/certs/dist.p12.enc -d -a -out scripts/certs/dist.p12
- openssl aes-256-cbc -k "$ENCRYPTION_SECRET" -in scripts/certs/dist.p12.enc -d -a -out scripts/certs/dist.p12

就這樣,在 GitHub 上面的文件就安全了,并且 Travis 依舊能讀取并使用這些加密后的文件。但是有一個(gè)安全問題你需要知道:在 Travis 的編譯日志中可能會(huì)顯示出解密環(huán)境變量。不過對(duì) pull 請(qǐng)求來(lái)說(shuō)不會(huì)出現(xiàn)。

添加腳本

現(xiàn)在我們需要確保證書都導(dǎo)入至 Travis CI 的鑰匙串中。為此,我們需要在 scripts 文件夾中添加一個(gè)名為 add-key.sh 的文件:

#!/bin/sh
security create-keychain -p travis ios-build.keychain
security import ./scripts/certs/apple.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/certs/dist.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
security import ./scripts/certs/dist.p12 -k ~/Library/Keychains/ios-build.keychain -P $KEY_PASSWORD -T /usr/bin/codesign
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp ./scripts/profile/$PROFILE_NAME.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/

通過上面的命令創(chuàng)建了一個(gè)名為 ios-build 的臨時(shí)鑰匙串,里面包含了所有證書。注意,這里我們使用了 $KEY_PASSWORD 來(lái)導(dǎo)入私鑰。最后一步是將配置文件拷貝至 Library 文件夾。

創(chuàng)建好文件之后,確保給其授予了可執(zhí)行的權(quán)限:在命令行輸入:chmod a+x scripts/add-key.sh 即可。為了正常使用腳本,必須要這樣處理一下。

至此,已經(jīng)導(dǎo)入了所有的證書和配置文件,我們可以開始給應(yīng)用程序簽名了。注意,在給程序簽名之前必須對(duì)程序進(jìn)行編譯。由于我們需要知道編譯結(jié)果存儲(chǔ)在磁盤的具體位置,我建議在編譯命令中使用 OBJROOTSYMROOT 來(lái)指定輸出目錄。另外,為了創(chuàng)建 release 版本,還需要把 SDK 設(shè)置為 iphoneos,以及將 configuration 修改為 Release

xctool -workspace TravisExample.xcworkspace -scheme TravisExample -sdk iphoneos -configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build ONLY_ACTIVE_ARCH=NO 'CODE_SIGN_RESOURCE_RULES_PATH=$(SDKROOT)/ResourceRules.plist'

如果運(yùn)行了上面的命令,那么編譯完成之后,可以在 build/Release-iphoneos 目錄找到應(yīng)用程序的二進(jìn)制文件。接下來(lái),就可以對(duì)其簽名,并創(chuàng)建 IPA 文件了。為此,我們創(chuàng)建一個(gè)新的腳本:

#!/bin/sh
if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then
  echo "This is a pull request. No deployment will be done."
  exit 0
fi
if [[ "$TRAVIS_BRANCH" != "master" ]]; then
  echo "Testing on a branch other than master. No deployment will be done."
  exit 0
fi

PROVISIONING_PROFILE="$HOME/Library/MobileDevice/Provisioning Profiles/$PROFILE_NAME.mobileprovision"
OUTPUTDIR="$PWD/build/Release-iphoneos"

xcrun -log -sdk iphoneos PackageApplication "$OUTPUTDIR/$APPNAME.app" -o "$OUTPUTDIR/$APPNAME.ipa" -sign "$DEVELOPER_NAME" -embed "$PROVISIONING_PROFILE"

第二行至第九行非常重要。我們并不希望在某個(gè)特性分支上創(chuàng)建新的 release。對(duì) pull 請(qǐng)求也一樣的。由于安全環(huán)境變量被禁用,所以 pull 請(qǐng)求也不會(huì)編譯。

第十四行,才是真正的簽名操作。這個(gè)命令會(huì)在 build/Release-iphoneos 目錄生成 2 個(gè)文件:TravisExample.ipaTravisExample.app.dsym。第一個(gè)文件包含了分發(fā)至手機(jī)上的應(yīng)用程序。dsym 文件包含了二進(jìn)制文件的調(diào)試信息。這個(gè)文件對(duì)于記錄設(shè)備上的 crash 信息非常重要。之后當(dāng)我們部署應(yīng)用程序的時(shí)候,會(huì)用到這兩個(gè)文件。

最后一個(gè)腳本是移除之前創(chuàng)建的臨時(shí)鑰匙串,并刪除配置文件。雖然這不是必須的,不過這有助于進(jìn)行本地測(cè)試。

#!/bin/sh
security delete-keychain ios-build.keychain
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/$PROFILE_NAME.mobileprovision

最后一步,我們必須告訴 Travis 什么時(shí)候執(zhí)行這三個(gè)腳本。在應(yīng)用程序編譯、簽名和清除等之前,需要先添加私鑰。在 .travis.yml 文件中添加如下內(nèi)容:

before_script:
- ./scripts/add-key.sh
- ./scripts/update-bundle.sh
script:
- xctool -workspace TravisExample.xcworkspace -scheme TravisExample -sdk iphoneos -configuration Release OBJROOT=$PWD/build SYMROOT=$PWD/build ONLY_ACTIVE_ARCH=NO
after_success:
- ./scripts/sign-and-upload.sh
after_script:
- ./scripts/remove-key.sh

完成上面的所有操作之后,我們就可以將所有內(nèi)容 push 到 GitHub 上,等待 Travis 對(duì)應(yīng)用程序進(jìn)行簽名。我們可以在工程頁(yè)面下的 Travis 控制臺(tái)驗(yàn)證是否一切正常。如果一切正常的話,下面來(lái)看看如何將簽好名的應(yīng)用程序部署給測(cè)試人員。

部署應(yīng)用程序

這里有兩個(gè)知名的服務(wù)可以幫助你發(fā)布應(yīng)用程序:TestFlightHockeyApp。不管選擇哪個(gè)都能夠滿足需求。就我個(gè)人來(lái)說(shuō),推薦使用 HockeyApp,不過這里我會(huì)對(duì)這兩個(gè)服務(wù)都做介紹。

首先我們對(duì) sign-and-build.sh 腳本做一個(gè)擴(kuò)充 -- 在里面添加一些 release 記錄:

RELEASE_DATE=`date '+%Y-%m-%d %H:%M:%S'`
RELEASE_NOTES="Build: $TRAVIS_BUILD_NUMBER\nUploaded: $RELEASE_DATE"

注意這里使用了一個(gè) Travis 的全局變量 TRAVIS_BUILD_NUMBER

TestFlight

創(chuàng)建一個(gè) TestFlight 賬號(hào),并配置好應(yīng)用程序。為了使用 TestFlight 的 API,首先需要獲得 api_tokenteam_token。再?gòu)?qiáng)調(diào)一下,我們需要確保它們是加密的。在命令行中執(zhí)行如下命令:

travis encrypt "TESTFLIGHT_API_TOKEN={api_token}" --add
travis encrypt "TESTFLIGHT_TEAM_TOKEN={team_token}" --add

現(xiàn)在我們可以調(diào)用相應(yīng)的 API 了。并將下面的內(nèi)容添加到 sign-and-build.sh:

curl http://testflightapp.com/api/builds.json \
  -F file="@$OUTPUTDIR/$APPNAME.ipa" \
  -F dsym="@$OUTPUTDIR/$APPNAME.app.dSYM.zip" \
  -F api_token="$TESTFLIGHT_API_TOKEN" \
  -F team_token="$TESTFLIGHT_TEAM_TOKEN" \
  -F distribution_lists='Internal' \
  -F notes="$RELEASE_NOTES"

千萬(wàn)不要使用 verbose 標(biāo)記 (-v) -- 這會(huì)暴露加密 tokens。

HockeyApp

注冊(cè)一個(gè) HockeyApp 賬號(hào),并創(chuàng)建一個(gè)新的應(yīng)用程序。然后在概述頁(yè)面獲取一個(gè) App ID。接下來(lái),我們必須創(chuàng)建一個(gè) API token。打開這個(gè)頁(yè)面,并創(chuàng)建一個(gè)。如果你希望自動(dòng)的將新版本部署給所有的測(cè)試人員,那么請(qǐng)選擇 Full Access 版本。

對(duì) App ID 和 token 進(jìn)行加密:

travis encrypt "HOCKEY_APP_ID={app_id}" --add
travis encrypt "HOCKEY_APP_TOKEN={api_token}" --add

然后在 sign-and-build.sh 文件中調(diào)用相關(guān)的 API:

curl https://rink.hockeyapp.net/api/2/apps/$HOCKEY_APP_ID/app_versions \
  -F status="2" \
  -F notify="0" \
  -F notes="$RELEASE_NOTES" \
  -F notes_type="0" \
  -F ipa="@$OUTPUTDIR/$APPNAME.ipa" \
  -F dsym="@$OUTPUTDIR/$APPNAME.app.dSYM.zip" \
  -H "X-HockeyAppToken: $HOCKEY_APP_TOKEN"

注意我們還上傳了 dsym 文件。如果集成了 TestFlightHockeyApp SDK,可以立即收集到易讀的 crash 報(bào)告。

Travis 故障排除

使用 Travis 一個(gè)月以來(lái),并不總是那么順暢。知道如何不通過直接訪問編譯環(huán)境就能找出問題是非常重要的。

在寫本文的時(shí)候,還沒有可以下載的虛擬機(jī)映像 (VM images) 。如果 Travis 不能正常編譯,首先試著在本地重現(xiàn)問題。在本地執(zhí)行跟 Travis 相同的編譯命令:

xctool ...

為了調(diào)試 shell 腳本,首先需要定義環(huán)境變量。我的做法是創(chuàng)建一個(gè)新的 shell 腳本來(lái)設(shè)置所有的環(huán)境變量。記得將這個(gè)腳本添加到 .gitignore 文件中 -- 因?yàn)槲覀儾⒉幌M麑⒃撐募_暴露出去。針對(duì)示例工程來(lái)說(shuō), config.sh 腳本文件看起來(lái)是這樣的:

#!/bin/bash

# Standard app config
export APP_NAME=TravisExample
export DEVELOPER_NAME=iPhone Distribution: Mattes Groeger
export PROFILE_NAME=TravisExample_Ad_Hoc
export INFO_PLIST=TravisExample/TravisExample-Info.plist
export BUNDLE_DISPLAY_NAME=Travis Example CI

# Edit this for local testing only, DON'T COMMIT it:
export ENCRYPTION_SECRET=...
export KEY_PASSWORD=...
export TESTFLIGHT_API_TOKEN=...
export TESTFLIGHT_TEAM_TOKEN=...
export HOCKEY_APP_ID=...
export HOCKEY_APP_TOKEN=...

# This just emulates Travis vars locally
export TRAVIS_PULL_REQUEST=false
export TRAVIS_BRANCH=master
export TRAVIS_BUILD_NUMBER=0

為了暴露出所有的環(huán)境變量,執(zhí)行如下命令(確保 config.sh 是可執(zhí)行的):

. ./config.sh

然后試著運(yùn)行 echo $APP_NAME,以此檢查腳本是否正確。如果正確的話,那么現(xiàn)在我們不用做任何修改,就能在本地運(yùn)行所有的 shell 腳本了。

如果在本地得到的是不同的編譯信息,那么可能是使用了不同的庫(kù)和 gems。盡量試著將配置信息設(shè)置為與 Travis VM 相同的信息。Travis 在這里列出了其所有安裝的軟件版本。你也可以在 Travis 的配置文件中添加調(diào)試信息得到所有庫(kù)文件的版本:

gem cocoapod --version
brew --version
xctool -version
xcodebuild -version -sdk

在本地安裝好與服務(wù)器完全相同的軟件之后,再重新編譯項(xiàng)目。

如果獲取到的編譯信息仍然不一樣,試著將項(xiàng)目 check out 到一個(gè)新的目錄。并確保所有的緩存都已清空。每次編譯程序時(shí),Travis 都會(huì)創(chuàng)建一個(gè)全新的虛擬機(jī),所以不存在緩存的問題,但在你的本地機(jī)器上可能會(huì)出現(xiàn)。

一旦在本地重現(xiàn)出和服務(wù)器上相同的錯(cuò)誤,就可以開始調(diào)查具體問題了。當(dāng)然導(dǎo)致問題的原因取決于具體問題。一般來(lái)說(shuō),通過 Google 都能找到引起問題的根源。

如果一個(gè)問題影響到了 Travis 上其它的項(xiàng)目,那么可能是 Travis 環(huán)境配置的原因。我曾經(jīng)遇到過幾次這樣的問題 (特別是剛開始時(shí))。如果發(fā)生這樣的情況試著聯(lián)系 Travis,取得支持,以我的經(jīng)驗(yàn)來(lái)說(shuō),他們的響應(yīng)非常迅速。

點(diǎn)評(píng)

Travis CI 跟市面上同類產(chǎn)品相比還是有一些限制。因?yàn)?Travis 運(yùn)行在一個(gè)預(yù)先配置好的虛擬機(jī)上,因此必須為每次編譯都安裝一遍所有的依賴。這會(huì)花費(fèi)一些額外的時(shí)間。不過 Travis 團(tuán)隊(duì)已經(jīng)在著手提供一種緩存機(jī)制解決這個(gè)問題了。

在一定程度上,你會(huì)依賴于 Travis 所提供的配置。比如你只能使用 Travis 內(nèi)置的 Xcode 版本進(jìn)行編譯。如果你本地使用的 Xcode 版本較新,你的項(xiàng)目在服務(wù)器上可能無(wú)法編譯通過。如果 Travis 能夠?yàn)椴煌?Xcode 版本都分別設(shè)置一個(gè)對(duì)應(yīng)虛擬機(jī)會(huì)就好了。

對(duì)于復(fù)雜的項(xiàng)目來(lái)說(shuō),你可能希望把整個(gè)編譯任務(wù)分為編譯應(yīng)用,運(yùn)行集成測(cè)試等等。這樣你可以快速獲得編譯信息而不用等所有的測(cè)試都完成。目前 Travis 還沒有直接支持有依賴的編譯。

當(dāng)項(xiàng)目被 push 到 GitHub 上時(shí),Travis 會(huì)自動(dòng)觸發(fā)。不過編譯動(dòng)作不會(huì)立即觸發(fā),你的項(xiàng)目會(huì)被放到一個(gè)根據(jù)項(xiàng)目所用語(yǔ)言不同而不同的一個(gè)全局編譯隊(duì)列,不過專業(yè)版允許并發(fā)編譯。

總結(jié)

Travis CI 提供了一個(gè)功能完整的持續(xù)集成環(huán)境,以進(jìn)行應(yīng)用程序的編譯、測(cè)試和部署。對(duì)于開源項(xiàng)目來(lái)說(shuō),這項(xiàng)服務(wù)是完全免費(fèi)的。很多社區(qū)項(xiàng)目都得益于 GitHub 強(qiáng)大的持續(xù)集成能力。你可能已經(jīng)看過如下這樣的按鈕

對(duì)于商業(yè)項(xiàng)目,Travis 專業(yè)版也能為私有倉(cāng)庫(kù)提供快捷、簡(jiǎn)便的持續(xù)集成支持。

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

更多鏈接

上一篇:Collection View 動(dòng)畫下一篇:插件