Jenkins iOS自動打包流程
背景
在開發(fā)中我們經(jīng)常需要打測試包叙赚,然后上傳至蒲公英等三方平臺,這其中需要經(jīng)歷的操作為:
- Git倉庫拉取代碼
- 設(shè)置項目的打包環(huán)境
- 利用 xcode 進行打包
- 上傳至蒲公英等三方平臺
每一次打包,上面的過程必不可少岭参,而且都是手工的,本篇文章我們將采用CD(Continuous Delivery)持續(xù)交付和CI(Continuous Integration)持續(xù)集成來進行自動化打包一鍵操作,解放雙手顷歌,拒絕手動的重復(fù)低效率勞動。
本篇文章講解如何使用 Jenkins + shell腳本 以及 Jenkins + fastlane 自動打包發(fā)布至蒲公英等三方平臺幔睬。
CI/CD(持續(xù)集成眯漩、持續(xù)交付/持續(xù)部署)
CI/CD 是一種通過在應(yīng)用開發(fā)階段引入自動化來頻繁向客戶交付應(yīng)用的方法。
CI/CD 的核心概念是持續(xù)集成麻顶、持續(xù)交付和持續(xù)部署赦抖。作為一個面向開發(fā)和運營團隊的解決方案,CI/CD 主要針對在集成新代碼時所引發(fā)的問題辅肾。
具體而言摹芙,CI/CD 可讓持續(xù)自動化和持續(xù)監(jiān)控貫穿于應(yīng)用的整個生命周期(從集成和測試階段,到交付和部署)宛瞄。這些關(guān)聯(lián)的事務(wù)通常被統(tǒng)稱為“CI/CD 管道”浮禾,由開發(fā)和運維團隊以敏捷方式協(xié)同支持交胚。
CI(Continuous Integration)是指持續(xù)集成,它屬于開發(fā)人員的自動化流程盈电。成功的 CI 意味著應(yīng)用代碼的新更改會定期構(gòu)建蝴簇、測試并合并到共享存儲庫中。該解決方案可以解決在一次開發(fā)中有太多應(yīng)用分支匆帚,從而導(dǎo)致相互沖突的問題熬词。
-
CD(Continuous Delivery/Deployment)是指持續(xù)交付或持續(xù)部署
持續(xù)交付通常是指開發(fā)人員對應(yīng)用的更改會自動進行錯誤測試并上傳到存儲庫(如 GitHub 或容器注冊表),然后由運維團隊將其部署到實時生產(chǎn)環(huán)境中吸重。這旨在解決開發(fā)和運維團隊之間可見性及溝通較差的問題互拾。因此,持續(xù)交付的目的就是確保盡可能減少部署新代碼時所需的工作量嚎幸。
持續(xù)部署(另一種“CD”)指的是自動將開發(fā)人員的更改從存儲庫發(fā)布到生產(chǎn)環(huán)境颜矿,以供客戶使用。它主要為了解決因手動流程降低應(yīng)用交付速度嫉晶,從而使運維團隊超負荷的問題骑疆。持續(xù)部署以持續(xù)交付的優(yōu)勢為根基,實現(xiàn)了管道后續(xù)階段的自動化替废。
詳細流程
自動化打包流程
graph LR
1[本地代碼] --提交--> 2[遠程倉庫] --> 3[Jenkins自動打包] --> 4[部署發(fā)布到蒲公英/Testflight] --> 5[結(jié)果郵件/釘釘/QQ通知]
Jenkins概述
Jenkins 是一款流行的開源持續(xù)集成(Continuous Integration)工具箍铭,廣泛用于項目開發(fā),具有自動化構(gòu)建椎镣、測試和部署等功能诈火。
安裝Jenkins環(huán)境
使用 Homebrew 軟件包管理器安裝Jenkins
沒有Homebrew的請先安裝Homebrew,網(wǎng)上資料很多状答,這里不再贅述柄瑰。
安裝命令
- 安裝最新的LTS版本:brew install jenkins-lts
- 安裝特定的LTS版本:brew install jenkins-lts@YOUR_VERSION
- 啟動 Jenkins 服務(wù):brew services start jenkins-lts
- 重新啟動 Jenkins 服務(wù):brew services restart jenkins-lts
- 更新 Jenkins 版本:brew upgrade jenkins-lts
安裝步驟
- 啟動 Jenkins 服務(wù)后,瀏覽 http://localhost:8080 剪况,該頁面需要確認是管理員安裝教沾,讓我們輸入密碼,根據(jù)提示獲取管理員密碼译断,點擊繼續(xù)授翻。
- 選擇安裝推薦的插件,等待插件安裝完畢孙咪。
- 插件安裝完成后會自動跳轉(zhuǎn)到配置管理員賬戶頁堪唐,配置完成點擊保存并完成注冊。
- 瀏覽器輸入 http://localhost:8080 打開Jenkins翎蹈,登錄即可淮菠。
Jenkins 配置-iOS
安裝相關(guān)插件
構(gòu)建xcode項目需要安裝的插件
- Git Parameter:分支管理
在Jenkins首頁->系統(tǒng)管理->插件管理搜索相關(guān)插件進行安裝即可。
環(huán)境變量配置
選擇系統(tǒng)管理 -> 系統(tǒng)配置 -> 全局屬性 -> 勾選環(huán)境變量選項
鍵:PATH
值:在終端中輸入echo $PATH
將輸出內(nèi)容復(fù)制填寫荤堪。
新建任務(wù)
創(chuàng)建任務(wù)
在Jenkins首頁->新建任務(wù)中輸入任務(wù)名稱合陵,選擇構(gòu)建一個自由風(fēng)格的軟件項目枢赔,點擊確定。
任務(wù)相關(guān)配置
選擇General -> 參數(shù)化構(gòu)建過程 -> Git參數(shù)
填寫名稱拥知,選擇類型為分支類型踏拜,默認origin/master
選擇源碼管理 -> Git,輸入倉庫地址低剔,添加賬號密碼速梗。
選擇構(gòu)建 -> 增加構(gòu)建步驟 -> 執(zhí)行shell
添加打包腳本,保存襟齿。
Fastlane打包
1姻锁、簡介
Fastlane是用Ruby語言編寫的一套自動化工具集和框架,每一個工具實際都對應(yīng)一個Ruby腳本猜欺,用來執(zhí)行某一個特定的任務(wù)位隶,而Fastlane核心框架則允許使用者通過類似配置文件的形式,將不同的工具有機而靈活的結(jié)合在一起替梨,從而形成一個個完整的自動化流程。
2装黑、原理
- Fastlane命令執(zhí)行的底層并不是自己實現(xiàn)的副瀑,而是調(diào)用其他的插件或者工具執(zhí)行的。
- 核心一:打包命令恋谭,F(xiàn)astlane中的
gym
工具只是xcodebuild工具的一個封裝糠睡。因此安裝Fastlane的步驟里有安裝IDE的指令集。 - 核心二:iTC(即iTunesConnect 蘋果的賬號證書應(yīng)用管理平臺)命令疚颊。蘋果除了提供圖形化可操作的網(wǎng)頁之外狈孔,還提供相對應(yīng)的一整套底層 API 給開發(fā)者使用,F(xiàn)astlane的底層操作是封裝的這套API材义。
我們iOS開發(fā)者均抽,感覺最繁瑣的事就是打包上架了,打包過程不僅繁瑣還特別耗費時間其掂。那么有沒有工具能將我們解放出來呢油挥?有,答案就是Fastlane
款熬。Fastlane
是移動端App開發(fā)的腳本工具深寥。
使用Fastlane
自動打包上傳的基本步驟如下:
1、安裝Fastlane
-> 2贤牛、Fastlane
初始化 -> 3惋鹅、配置證書和描述文件 -> 4、一鍵上傳
Fastlane
的安裝有多種途徑殉簸,其中之一就是使用sudo gem install fastlane
命令安裝闰集,其他途徑這里不再贅述沽讹,也是一行命令的事。可以用命令
fastlane --version
來查看是否安裝成功在項目中初始化
fastlane
使用命令fastlane init
返十,根據(jù)提示一步步進行即可妥泉。初始化成功之后,在項目根目錄下會多幾個文件洞坑,
Appfile
蘋果賬號信息盲链、Fastfile
填寫打包腳本的地方(重點、重點迟杂、重點)刽沾。若使用蒲公英上傳,需安裝蒲公英插件排拷,在終端輸入
fastlane add_plugin pgyer
命令侧漓,即可安裝蒲公英的 fastlane 插件。
Fastlane
打包腳本示例(使用Ruby
語言)
default_platform(:iOS)
#蒲公英api_key和user_key
api_key = "your api_key"
user_key = "your user_key"
configuration = "Release"#Debug_Production_Server
platform :iOS do
desc "??????????打包并上傳蒲公英????????"
lane :pgybeta do
#指定項目的scheme名稱
scheme = "TuWanApp"
#更新描述
update_desc = "更新描述"
gym(
#輸出的ipa名稱
output_name:"#{scheme}_#{Time.new.strftime("%Y%m%d%H%M")}",
# 是否清空以前的編譯信息 true:是
clean:true,
scheme:scheme,
# 指定打包方式监氢,Release 或者 Debug
configuration:"#{configuration}",
# 指定打包所使用的輸出方式布蔗,目前支持app-store, package, ad-hoc, enterprise, development
export_method:"ad-hoc",
# 指定輸出文件夾
output_directory:"./build/pgy",
)
pgyer(api_key: "#{api_key}", user_key: "#{user_key}", update_description: "#{update_desc}")
end
end
Jenkins + Fastlane配置
Jenkins shell腳本
#!/bin/bash
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
set -e
#輸出錯誤
function echo_error() {
echo "?????????? $1 ??????????"
}
#輸出信息
function echo_info() {
echo "==================== $1 ===================="
}
#輸出成功
function echo_success() {
echo "?????????? $1 ??????????"
}
#json解析
function parseJson() {
local json=$1
local key=$2
if [ "$key" == "code" ]; then
echo "$json" | sed -E 's/,/\n/g' | grep -E "${key}" | sed -E 's/:/\n/g' | sed -E '1d' | sed -E 's/"http://g'
else
echo "$json" | tr "\n" " " | sed -E 's/.*"'"${key}"'" *: *"([^"]*)".*/\1/g' | sed -E 's/\\\//\//g'
fi
}
#檢測IPA包是否上傳成功
function checkIPA() {
echo_info "檢測應(yīng)用是否發(fā)布完成,并獲取發(fā)布應(yīng)用的信息"
result=$(curl -D - --form-string "_api_key=$1" --form-string "buildKey=$2" "https://www.pgyer.com/apiv2/app/buildInfo")
#解析數(shù)據(jù)浪腐,生成下載地址
resultCode=$(parseJson "$result" "code")
resultMessage=$(parseJson "$result" "message")
resultBuildKey=$(parseJson "$result" "buildKey")
buildShortcutUrl=$(parseJson "$result" "buildShortcutUrl")
buildQRCodeURL=$(parseJson "$result" "buildQRCodeURL")
if [ "$resultCode" == 1246 ] || [ "$resultCode" == 1247 ]; then
echo_info "code: $resultCode"
echo_error "message: $resultMessage"
sleep 5
checkIPA $1 $2
elif [ -n "$resultBuildKey" ]; then
echo_success "上傳成功"
echo_info "二維碼地址=> ${buildQRCodeURL} <=二維碼地址"
echo_info "app下載地址=> https://www.pgyer.com/${buildShortcutUrl} <=app下載地址"
else
echo_info "code: $resultCode"
echo_error "message: $resultMessage"
exit $resultCode
fi
}
#打包
function packaging() {
echo_info "開始打包"
#參數(shù)校驗 $# 添加到Shell的參數(shù)個數(shù)
if [ $# != "1" ] || ([ $1 != "debug" ] && [ $1 != "release" ] && [ $1 != "appleStore" ] && [ $1 != "appleStore_beta" ]); then
echo_error '請指定打包類型:debug, release, appleStore, appleStore_beta'
exit 1
fi
#定義一些符變量
export v_env=$1
export v_project_name="TuWanApp"
export pgy_ipa_path="./build/pgy"
export pgy_api_key="蒲公英API Key"
export pgy_install_pwd="123456"
echo_info "安裝pod依賴"
#安裝pod依賴
#rm -rf './Pods/Local Podspecs'
#pod update
pod install --repo-update
#構(gòu)建
echo_info "開始構(gòu)建"
#fastlane打包
if ([ -n "${version}" ] && [ -n "${build}" ]); then
fastlane ${v_env} v:${version} b:${build}
elif [ -n "${version}" ]; then
fastlane ${v_env} v:${version}
else
fastlane ${v_env}
fi
#上傳二進制包到蒲公英
if ([ $1 != "appleStore" ] && [ $1 != "appleStore_beta" ]); then
echo_info "上傳二進制包到蒲公英"
tokenResp=$(curl -D - --form-string "_api_key=${pgy_api_key}" --form-string 'buildType=ios' --form-string 'buildInstallType=2' --form-string "buildPassword=${pgy_install_pwd}" --form-string "buildUpdateDescription=${updateContent}" "https://www.pgyer.com/apiv2/app/getCOSToken")
echo_info "獲取預(yù)上傳 url 和相關(guān)的簽名參數(shù)=${tokenResp}"
echo_info "解析預(yù)上傳數(shù)據(jù)"
code=$(parseJson "$tokenResp" "code")
message=$(parseJson "$tokenResp" "message")
key=$(parseJson "$tokenResp" "key")
signature=$(parseJson "$tokenResp" "signature")
xcossecuritytoken=$(parseJson "$tokenResp" "x-cos-security-token")
endpoint=$(parseJson "$tokenResp" "endpoint")
echo_info "預(yù)上傳數(shù)據(jù)解析結(jié)果"
echo_info "code:$code"
echo_info "message:$message"
echo_info "key:$key"
echo_info "signature:$signature"
echo_info "xcossecuritytoken:$xcossecuritytoken"
echo_info "endpoint:$endpoint"
if [ "$code" != 0 ]; then
echo_error "$message"
exit $code
else
echo_info "開始上傳IPA文件"
cd ${pgy_ipa_path}
pgy_ipa_name=`ls -lt | grep .ipa | head -n 1 | awk '{print $9}'`
echo_info "IPA文件名:${pgy_ipa_name}"
resp=$(curl -D - --form-string "key=${key}" --form-string "signature=${signature}" --form-string "x-cos-security-token=${xcossecuritytoken}" -F "file=@${pgy_ipa_name}" ${endpoint})
echo_info "pgy_resp=$resp"
sleep 5
checkIPA ${pgy_api_key} ${key}
fi
fi
}
export FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT=120
#執(zhí)行打包
packaging ${BuildType}
上傳符號表到bugly
if ([ "${BuildType}" == "appleStore" ] || [ "${BuildType}" == "appleStore_beta" ]); then
cd /Users/tuwanmini/.jenkins/workspace/TuwanApp/build/appStore
rm -rf dsym_tmp
mkdir -p dsym_tmp
filename=`ls -lt | grep .zip | head -n 1 | awk '{print $9}'`
version=$(echo ${filename} | awk -F "_" '{print $2}')
unzip ${filename} -d dsym_tmp
cd /Users/tuwanmini/Desktop/buglyqq-upload-symbol/
java -jar buglyqq-upload-symbol.jar -appid xxxxxxxxx -appkey xxxxxxxxx -bundleid xxxxxxxxx -version ${version} -platform IOS -inputSymbol /Users/tuwanmini/.jenkins/workspace/TuwanApp/build/appStore/dsym_tmp
else
cd /Users/tuwanmini/.jenkins/workspace/TuwanApp/build/pgy
rm -rf dsym_tmp
mkdir -p dsym_tmp
filename=`ls -lt | grep .zip | head -n 1 | awk '{print $9}'`
version=$(echo ${filename} | awk -F "_" '{print $2}')
unzip ${filename} -d dsym_tmp
cd /Users/tuwanmini/Desktop/buglyqq-upload-symbol/
java -jar buglyqq-upload-symbol.jar -appid xxxxxxxxx -appkey xxxxxxxxx -bundleid xxxxxxxxx -version ${version} -platform IOS -inputSymbol /Users/tuwanmini/.jenkins/workspace/TuwanApp/build/pgy/dsym_tmp
fi
發(fā)送釘釘消息
注:使用非text消息類型纵揍,需在消息內(nèi)容中添加"@xxx"的文本內(nèi)容,@功能才能生效
#!/bin/bash
# 定義釘釘機器人 webhook 地址和 access_token
webhook_url="你的釘釘機器人webhook"
appName="你的應(yīng)用名稱"
downloadShortUrl="你的蒲公英下載地址短連接"
installPSW="你的蒲公英安裝密碼"
atUserMobiles="@你要艾特的釘釘用戶手機號"
# 定義消息內(nèi)容
pgyContent="### ${appName} \
\n版本號: ${version} (${build})\n \
\n更新內(nèi)容:\n \
\n${updateContent}\n \
\n下載地址: https://www.pgyer.com/${downloadShortUrl}\n \
\n安裝密碼: ${installPSW}\n \
\n\n \
\n${atUserMobiles}"
appStoreContent="### ${appName}提審包上傳成功 \
\n版本號:${version}(${build})\n \
\n${atUserMobiles}"
releaseBateContent="### ${appName}隊長試用包上傳成功 \
\n版本號:${version}(${build})\n \
\n${atUserMobiles}"
if [ "${BuildType}" == "appleStore" ]; then
# 發(fā)送 HTTP 請求议街,向釘釘機器人發(fā)送報警消息
curl ${webhook_url} \
-H 'Content-Type: application/json' \
-d "{\"at\": {\"atMobiles\": [${atUserMobile}],\"isAtAll\": false},\"msgtype\": \"markdown\",\"markdown\": {\"title\": \"${appName}\", \"text\": \"${appStoreContent}\"}}"
elif [ "${BuildType}" == "appleStore_beta" ]; then
# 發(fā)送 HTTP 請求泽谨,向釘釘機器人發(fā)送報警消息
curl ${webhook_url} \
-H 'Content-Type: application/json' \
-d "{\"at\": {\"atMobiles\": [${atUserMobile}],\"isAtAll\": false},\"msgtype\": \"markdown\",\"markdown\": {\"title\": \"${appName}\", \"text\": \"${releaseBateContent}\"}}"
else
# 發(fā)送 HTTP 請求,向釘釘機器人發(fā)送報警消息
curl ${webhook_url} \
-H 'Content-Type: application/json' \
-d "{\"at\": {\"atMobiles\": [${atUserMobile}],\"isAtAll\": false},\"msgtype\": \"markdown\",\"markdown\": {\"title\": \"${appName}\", \"text\": \"${pgyContent}\"}}"
fi
構(gòu)建打包任務(wù)
選擇已創(chuàng)建的任務(wù)
選擇Build with Parameters -> 分支 -> 開始構(gòu)建特漩。
查看控制臺輸出吧雹。
補充說明
找不到/ExportOptions/XXXExportOptions.plist文件
- AdHocExportOptions.plist,AppStoreExportOptios.plist涂身,EnterpriseExportOptions.plist雄卷,DevelopmentExportOptions.plist文件可通過Xcode -> Product -> Archive打包生成的ExportOptions.plist文件添加相應(yīng)前綴重命名即可獲得。
- 在Jenkins -> workspace -> 項目根目錄下新建名為ExportOptions的文件夾蛤售,將相應(yīng)plist文件拷貝到改文件夾中即可龙亲。
證書及描述文件
在Xcode中配置完成即可。
大文件拉取git-lfs報錯
運行
brew install git-lfs
即可安裝若不再需要大文件支持悍抑,可前往項目根目錄 -> .git -> hooks -> 移除post-checkout文件即可鳄炉。
拉取代碼出錯
cd到Jenkins -> workspace目錄下,手動通過git clone將工程clone到本地搜骡,重啟Jenkins拂盯,再次執(zhí)行打包任務(wù)。
局域網(wǎng)使用IP訪問jenkins
使用brew安裝jenkins會避免很多其他安裝方式產(chǎn)生的用戶權(quán)限問題记靡,但是會將httpListenAddress默認設(shè)置為127.0.0.1谈竿,這樣我們雖然可以在本地用localhost:8080訪問团驱,但是本機和局域網(wǎng)均無法用ip訪問。
解決辦法為修改兩個路徑下的plist配置:
- ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
- /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist
第二個路徑找不到的話空凸,cmd + shift + G 輸入/opt/homebrew/opt/jenkins嚎花,找到homebrew.mxcl.jenkins.plist
將上面兩個plist中的httpListenAddress后的ip地址,修改為本機IP或者0.0.0.0呀洲,修改完成后紊选,重啟Jenkins,接下來就可以使用IP訪問了道逗。
fastlane版本升級注意事項
- 需要修改項目中Gemfile文件中的fastlane版本為當前要升級的版本兵罢;
- cd到項目目錄下,執(zhí)行bundle install滓窍,重新安裝您的 gem 依賴卖词;
- 再次運行 bundle update fastlane ;
- 重啟電腦吏夯,環(huán)境變量生效此蜈;
- 若Jenkins中的fastlane版本仍然沒有升級到最新,可以使用Jenkins新建任務(wù)的方式噪生,在新建任務(wù)中添加構(gòu)建shell腳本裆赵,執(zhí)行如下代碼:
echo 你的電腦密碼 | sudo -S bundle update fastlane
fastlane --version
- 若版本仍然沒有更新,請前往ruby安裝目錄下杠园,卸載fastlane其他舊版本顾瞪,只留新版本即可舔庶。