你是否曾經(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ò)誤。
我最喜歡 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="" />
讓我們看一下如何將 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)通知之后該做什么。
Travis CI 需要項(xiàng)目的一些基本信息。在項(xiàng)目的根目錄創(chuàng)建一個(gè)名叫 .travis.yml
的文件,文件中的內(nèi)容如下:
language: objective-c
Travis 編譯器運(yùn)行在虛擬機(jī)環(huán)境下。該編譯器已經(jīng)利用 Ruby,Homebrew,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)指定 workspace
和 scheme
:
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
對(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 是來(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ù)。首先,我們需要給程序簽名。
為了在 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 Hoc 或 In 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 Settings
的 Code Signing Identity
> Release
對(duì)應(yīng)的名字。然后搜索程序的 Ad Hoc
或 In 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ǔ)在磁盤的具體位置,我建議在編譯命令中使用 OBJROOT
和 SYMROOT
來(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.ipa
和 TravisExample.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è)試人員。
這里有兩個(gè)知名的服務(wù)可以幫助你發(fā)布應(yīng)用程序:TestFlight 和 HockeyApp。不管選擇哪個(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
。
創(chuàng)建一個(gè) TestFlight 賬號(hào),并配置好應(yīng)用程序。為了使用 TestFlight 的 API,首先需要獲得 api_token 和 team_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。
注冊(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
文件。如果集成了 TestFlight 或 HockeyApp SDK,可以立即收集到易讀的 crash 報(bào)告。
使用 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)非常迅速。
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ā)編譯。
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,趕緊去試試吧,它棒極了!