在《用 Jenkins 搭建 iOS/Android 持續整合打包平臺》一文中,我對如何使用Jenkins搭建iOS/Android持續整合打包平臺的基礎概念和實施流程進行了介紹。本文作為配套,對搭建持續整合打包平臺中涉及到的執行命令、構建指令碼(build.py),以及Jenkins的配置進行詳細的補充說明。
當然,如果你不關心技術實現細節,也可以完全不用理會,直接參照【開箱即用】部分按照步驟進行操作即可。
關於iOS的構建
對iOS原始碼進行構建,目標是要生成.ipa
檔案,即iOS應用安裝包。
當前,構建方式主要包括兩種:
原始碼
->.archive
檔案 ->.ipa
檔案原始碼
->.app
檔案 ->.ipa
檔案
這兩種方式的主要差異是生成的中間產物不同,對應的,兩種構建方式採用的命令也不同。
原始碼
-> .archive
-> .ipa
1 2 3 4 5 6 7 |
# build archive file from source code xcodebuild \ # xctool -workspace ${WORKSPACE_PATH} \ -scheme ${SCHEME} \ -configuration ${CONFIGURATION} \ -sdk ${SDK} archive -archivePath ${archive_path} |
archive
:打包命令,會生成一個.xcarchive
的檔案;archive命令需要接一個引數-archivePath
,即存放Archive檔案的目錄
1 2 3 4 5 6 7 8 |
# export ipa file from .archive xcodebuild -exportArchive \ -exportFormat format \ -archivePath xcarchivepath \ -exportPath destinationpath \ -exportProvisioningProfile profilename \ [-exportSigningIdentity identityname] [-exportInstallerIdentity identityname] |
原始碼
-> .app
-> .ipa
1 2 3 4 5 6 7 |
# build .app file from source code xcodebuild \ # xctool -workspace ${WORKSPACE_PATH} \ -scheme ${SCHEME} \ -configuration ${CONFIGURATION} \ -sdk ${SDK} -derivedDataPath build |
1 2 3 4 5 6 |
# convert .app file to ipa file xcrun \ -sdk iphoneos \ PackageApplication \ -v build/Release-iphoneos/xxx.app \ -o build/Release-iphoneos/xxx.ipa |
引數說明
xcodebuild/xctool引數:
-workspace
:需要打包的workspace,後面接的檔案一定要是.xcworkspace
結尾的;-scheme
:需要打包的Scheme,一般與$project_name
相同;-sdk
:區分iphone device和Simulator;-configuration
:需要打包的配置檔案,我們一般在專案中新增多個配置,適合不同的環境,Release/Debug;-derivedDataPath
:指定編譯結果檔案的儲存路徑;例如,指定-derivedDataPath build
時,將在專案根目錄下建立一個build
資料夾,生成的.app
檔案將位於build/Build/Products/Release-iphoneos
中。
除了採用官方的xcodebuild
命令,還可以使用由Facebook開發維護的xctool
。xctool
命令的使用方法基本與xcodebuild
一致,但是輸出的日誌會清晰很多,而且還有許多其它優化,詳情請參考xctool
的官方文件。
xcrun引數:
-v
:指定.app
檔案的路徑-o
:指定生成.ipa
檔案的路徑
補充說明
1、獲取Targets、Schemes、Configurations引數
在填寫target
/workspace
/scheme
/configuration
等引數時,如果不知道該怎麼填寫,可以在專案根目錄下執行xcodebuild -list
命令,它會列出當前專案的所有可選引數。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
➜ Store_iOS git:(NPED) ✗ xcodebuild -list Information about project "Store": Targets: Store StoreCI Build Configurations: Debug Release If no build configuration is specified and -scheme is not passed then "Release" is used. Schemes: Store StoreCI |
2、清除快取檔案
在每次build之後,工程目錄下會遺留一些快取檔案,以便下次build時減少編譯時間。然而,若因為工程配置錯誤等問題造成編譯失敗後,下次再編譯時就可能會受到快取的影響。
因此,在持續整合構建指令碼中,比較好的做法是在每次build之前都清理一下上一次編譯遺留的快取檔案。
1 2 3 4 5 6 |
# clean before build xctool \ -workspace ${WORKSPACE_PATH} \ -scheme ${SCHEME} \ -configuration ${CONFIGURATION} \ clean |
clean
:清除編譯產生的問題,下次編譯就是全新的編譯了
3、處理Cocoapod依賴庫
另外一個需要注意的是,若專案是採用Cocoapod管理專案依賴,每次拉取最新程式碼後直接編譯可能會報錯。這往往是因為其他同事更新了依賴庫(新增了第三方庫或升級了某些庫),而本地還採用之前的第三方庫進行編譯,從而會出現依賴庫缺失或版本不匹配等問題。
應對的做法是,在每次build之前都更新一下Cocoapod。
1 2 3 4 |
# Update pod repository pod repo update # Install pod dependencies pod install |
4、修改編譯包的版本號
通過持續整合打包,我們會得到大量的安裝包。為了便於區分,比較好的做法是在App中顯示版本號,並將版本號與Jenkins的BUILD_NUMBER
關聯起來。
例如,當前專案的主版本號為2.6.0
,本次構建的BUILD_NUMBER
為130,那麼我們就可以將本次構建的App版本號設定為2.6.0.130
。通過這種方式,我們可以通過App中顯示的版本號快速定位到具體到構建歷史,從而對應到具體的程式碼提交記錄。
要實現對App版本號的設定,只需要在打包前對Info.plist
檔案中的CFBundleVersion
和CFBundleShortVersionString
進行修改即可。在Python中,利用plistlib
庫可以很方便地實現對Info.plist
檔案的讀寫。
5、模擬器執行
如果持續整合測試是要執行在iOS模擬器上,那麼就需要構建生成.app
檔案。
在前面講解的兩種構建方式中,中間產物都包含了.app
檔案。對於以.xcarchive
為中間產物的方式,生成的.app
檔案位於output_dir/StoreCI_Release.xcarchive/Products/Applications/
目錄中。
不過,這個.app
檔案在模擬器中還無法直接執行,還需要在Xcode中修改Supported Platforms
,例如,將iphoneos
更改為iOS
。詳細原因請參考《從0到1搭建移動App功能自動化測試平臺(1):模擬器中執行iOS應用》
關於Android的構建
待續
關於構建指令碼
對於構建指令碼(build.py
)本身,原始碼應該是最好的說明文件。
在build.py
指令碼中,主要實現的功能就四點:
- 執行構建命令,編譯生成
.ipa
檔案,這部分包含了關於iOS的構建
部分的全部內容; - 構建時動態修改
Info.plist
,將編譯包的版本號與Jenkins的BuildNumber關聯起來; - 上傳
.ipa
檔案至pyger
/fir.im
平臺,並且做了失敗重試機制; - 解析
pyger
/fir.im
平臺頁面中的二維碼,將二維碼圖片儲存到本地。
需要說明的是,對於構建任務中常用的可配置引數,例如BRANCH
/SCHEME
/CONFIGURATION
/OUTPUT_FOLDER
等,需要在構建指令碼中通過OptionParser
的方式實現可傳引數機制。這樣我們不僅可以命令列中通過傳參的方式靈活地呼叫構建指令碼,也可以在Jenkins中實現引數傳遞。
之所以強調常用的
可配置引數,這是為了儘可能減少引數數目,降低指令碼呼叫的複雜度。像PROVISIONING_PROFILE
和pgyer/fir.im
賬號這種比較固定的配置引數,就可以寫死在指令碼中。因此,在使用構建指令碼(build.py)之前,需要先在指令碼中配置下PROVISIONING_PROFILE
和pgyer/fir.im
賬號。
另外還想多說一句,pyger
/fir.im
這類第三方平臺在為我們提供便利的同時,穩定性不可控也是一個不得不考慮的問題。在我使用pgyer
平臺期間,就遇到了平臺服務變動、介面時而不穩定出現502等問題。因此,最好的方式還是自行搭建一套類似的服務,反正我是打算這麼做了。
Jenkins的詳細配置
對於Jenkins的詳細配置,需要補充說明的有四點。
1、引數的傳遞
在構建指令碼中,我們已經對常用的可配置引數實現了可傳參機制。例如,在Terminal中可以通過如下形式呼叫構建指令碼。
1 |
$ python build.py --scheme SCHEME --workspace Store.xcworkspace --configuration CONFIGURATION --output OUTPUT_FOLDER |
那麼我們在Jenkins中要怎樣才能指定引數呢?
實際上,Jenkins針對專案具有引數化的功能。在專案的配置選項中,勾選This project is parameterized
後,就可以為當前project新增多種型別的引數,包括:
- Boolean Parameter
- Choice Parameter
- Credentials Parameter
- File Parameter
- Multi-line String Parameter
- Password Parameter
- Run Parameter
- String Parameter
通常,我們可以選擇使用String Parameter
來定義自定義引數,並可對每個引數設定預設值。
當我們配置了BRANCH
、SCHEME
、CONFIGURATION
、OUTPUT_FOLDER
、BUILD_VERSION
這幾個引數後,我們就可以在Build
配置區域的Execute shell
通過如下形式來進行引數傳遞。
1 2 3 4 5 6 |
$ python ${WORKSPACE}/Build_scripts/build.py \ --scheme ${SCHEME} \ --workspace ${WORKSPACE}/Store.xcworkspace \ --configuration ${CONFIGURATION} \ --output ${WORKSPACE}/${OUTPUT_FOLDER} \ --build_version ${BUILD_VERSION}.${BUILD_NUMBER} |
可以看出,引數的傳遞方式很簡單,只需要預先定義好了自定義引數,然後就可以通過${Param}
的形式來進行呼叫了。
不過你也許會問,WORKSPACE
和BUILD_NUMBER
這兩個引數我們並未進行定義,為什麼也能進行呼叫呢?這是因為Jenkins自帶部分與專案相關的環境變數,例如BRANCH_NAME
、JOB_NAME
等,這部分引數可以在shell指令碼中直接進行呼叫。完整的環境變數可在Jenkins_Url/env-vars.html/
中檢視。
配置完成後,就可以在Build with Parameters
中通過如下形式手動觸發構建。
2、修改build名稱
在Build History
列表中,構建任務的名稱預設顯示為按照build次數遞增的BUILD_NUMBER
。有時候我們可能想在build名稱中包含更多的資訊,例如包含當次構建的SCHEME
和CONFIGURATION
,這時我們就可以通過修改BuildName
實現。
Jenkins預設不支援BuildName
設定,但可通過安裝build-name-setter
外掛進行實現。安裝build-name-setter
外掛後,在配置頁面的Build Environment
欄目下會出現Set Build Name
配置項,然後在Build Name
中就可以通過環境變數引數來設定build名稱。
例如,要將build名稱設定為上面截圖中的StoreCI_Release_#130
樣式,就可以在Build Name
中配置為${SCHEME}_${CONFIGURATION}_#${BUILD_NUMBER}
。
除了在Build Name
中傳遞環境變數引數,build-name-setter
還可以實現許多更加強大的自定義功能,大家可自行探索。
3、展示二維碼圖片
然後再說下如何在Build History
列表中展示每次構建對應的二維碼圖片。
需要說明的是,在上圖中,綠色框對應的內容是BuildName
,我們可以通過build-name-setter
外掛來實現自定義配置;但是紅色框已經不在BuildName
的範圍之內,而是對應的BuildDescription
。
同樣地,Jenkins預設不支援在構建過程中自動修改BuildDescription
,需要通過安裝description setter plugin
外掛來輔助實現。安裝description setter plugin
外掛後,在配置頁面的Build
欄目下,Add build step
中會出現Set build description
配置項,新增該配置項後就會出現如下配置框。
該功能的強大之處在於,它可以在構建日誌中通過正規表示式來匹配內容,並將匹配到的內容新增到BuildDescription
中去。
例如,我們想要展示的二維碼圖片是在每次構建過程中生成的,因此我們首先要獲取到二維碼圖片檔案。
我的做法是,在build.py
中將蒲公英平臺返回的應用下載頁面地址和二維碼圖片地址列印到log中。
1 2 3 4 |
appDownloadPage: https://www.pgyer.com/035aaf10acf5dd7c279c4fe423a57674 appQRCodeURL: https://o1wjx1evz.qnssl.com/app/qrcodeHistory/fe7a8c9051f0c7fc0affc78f40c20a4b5e4bdb4c77b91a29501f55fd9039c659 Save QRCode image to file: /Users/Leo/.jenkins/workspace/DJI_Plus_Store_iOS/build_outputs/QRCode.png |
然後,在Set build description
配置項的Regular expression
就可以按照如下正規表示式進行匹配:
1 2 |
appDownloadPage: (.*)$ |
接下來,就可以在Description
中對匹配到的結果進行引用。
1 2 |
<img src='${BUILD_URL}artifact/build_outputs/QRCode.png'>\n<a href='\1'>Install Online</a> |
在這裡,我們用到了HTML的標籤,而Jenkins的Markup Formatter
預設是採用Plain text
模式,因此還需要對Jenkins對系統配置進行修改,在《用 Jenkins 搭建 iOS/Android 持續整合打包平臺》中已進行了詳細說明,在此就不再重複。
通過以上方式,就可以實現前面圖片中的效果。
4、收集編譯成果物
在上面講解的展示二維碼圖片一節中,用到了${BUILD_URL}artifact/build_outputs/QRCode.png
一項,這裡的URL就是用到了編譯成果物收集後儲存的路徑。
Archives build artifacts
是Jenkins預設自帶的功能,無需安裝外掛。該功能在配置頁面的Post-build Actions
欄目下,在Add post-build action
的列表中選擇新增Archives build artifacts
。
新增後的配置頁面如下圖所示:
通常,我們只需要配置Files to archive
即可。定位檔案時,可以通過正規表示式進行匹配,也可以呼叫專案的環境變數;多個檔案通過逗號進行分隔。
例如,假如我們想收集QRCode.png
、StoreCI_Release.ipa
、Info.plist
這三個檔案,那麼我們就可以通過如下表示式來進行指定。
1 2 |
${OUTPUT_FOLDER}/*.ipa,${OUTPUT_FOLDER}/QRCode.png,${OUTPUT_FOLDER}/*.xcarchive/Info.plist |
當然,目標檔案的具體位置是我們在構建指令碼(build.py
)中預先進行處理的。
通過這種方式,我們就可以實現在每次完成構建後將需要的檔案收集起來進行存檔,以便後續在Jenkins的任務頁面中進行下載。
也可以直接通過歸檔檔案的URL進行訪問。例如,上圖中QRCode.png
的URL為Jenkins_Url/job/JenkinsJobName/131/artifact/build_outputs/QRCode.png
,而Jenkins_Url/job/JenkinsJobName/131/
即是${BUILD_URL}
,因此可以直接通過${BUILD_URL}artifact/build_outputs/QRCode.png
引用。
總結
至此,《用 Jenkins 搭建 iOS/Android 持續整合打包平臺》一文中涉及到的Jenkins配置和構建指令碼實現細節均已補充完畢了。相信大家結合這兩篇文章,應該會對如何使用Jenkins搭建iOS/Android持續整合打包平臺的基礎概念和實現細節都有一個比較清晰的認識。
對於還未完善的部分,我後續將在部落格中進行更新。
操作手冊請參考文章末尾的【開箱即用】部分,祝大家玩得愉快!
開箱即用
GitHub地址:https://github.com/debugtalk/JenkinsTemplateForApp
1、新增構建指令碼
- 在構建指令碼中配置
PROVISIONING_PROFILE
和pgyer/fir.im
賬號; - 在目標構建程式碼庫的根目錄中,建立
Build_scripts
資料夾,並將build.py
拷貝到Build_scripts
中; - 將
Build_scripts/build.py
提交到專案中。
除了與Jenkins實現持續整合,構建指令碼還可單獨使用,使用方式如下:
1 2 3 4 5 |
$ python ${WORKSPACE}/Build_scripts/build.py \ --scheme ${SCHEME} \ --workspace ${WORKSPACE}/Store.xcworkspace \ --configuration ${CONFIGURATION} \ --output ${WORKSPACE}/${OUTPUT_FOLDER} |
2、執行jenkins,安裝必備外掛
1 |
$ nohup java -jar jenkins_located_path/jenkins.war & |
3、建立Jenkins Job
- 在Jenkins中建立一個
Freestyle project
型別的Job,先不進行任何配置; - 然後將
config.xml
檔案拷貝到~/.jenkins/jobs/YourProject/
中覆蓋原有配置檔案,重啟Jenkins; - 完成配置檔案替換和重啟後,剛建立好的Job就已完成了大部分配置;
- 在
Job Configure
中根據專案實際情況調整配置,其中Git Repositories
是必須修改的,其它配置項可選擇性地進行調整。