以前寫過一篇基於Jenkins和Fastlane自動打包的文章,文中簡述了不少的的環境搭建以及一些遇到的問題。這篇文章的目的是使用腳本和Jenkins來自動打包,爲什麼不在使用以前的Fastlane呢?首先Fastlane是很不錯的,也是使用xcodebuild命令來封裝實現的,可是在使用過程當中會涉及到一個腳本的遷移能力即運用到其餘項目上,我認爲是不算高的,至少在Jenkins上。反正Fastlane也是基於xcodebuild來進行編譯打包的,那咱們何不直接編寫腳本進行打包呢?html
還有一個問題就是你們都知道使用Git基於Jenkins的自動化打包能夠直接在每次提交時實現自動打包,可是在Jenkins中使用SVN咱們沒有發現相似這個功能的插件。文中的解決辦法是一種曲線救國的方法,在文章的下半部分會講到。我一開始的構思仍是想經過局域網來實現,可是越到後面發現行不通,沒有感受到自動打包帶來的便利。因而有了本文這個方案。ios
①請確保打包電腦的環境已經安裝好了HomeBrew、Jenkins(建議使用brew安裝Jenkins並進行開機自啓動)git
②請確保打包電腦的要是串中有你須要打包的APP所需的證書、描述文件程序員
③請確保此打包腳本用於基於提交SVN和Jenkins環境中自動打包,不然將會致使腳本不可用的狀態😂😂github
具體的結構能夠參照下圖,若是有其餘的類型的打包,能夠在Plist文件夾中添加新的plist文件,這個文件能夠在手動打包時導出的文件夾裏有xxxExportOptions.plist文件。而後在配置文件裏面進行更改類型。shell
修改配置文件就能直接控制打包類型是否是很方便。一開始採用json文件來進行配置,使用json文件的話對我來講有兩個缺點。其一就是json文件中沒法進行註釋,這會致使後面接手的人沒法明白其意,或者須要在添加一個說明文檔;其二是由於在使用json配置路徑的時候還須要對值進行去除雙引號,能夠實現很麻煩。另外就是Shell解析json數據仍是有點麻煩,使用jq解析挺方便就是得配置一下環境。其中global.config代碼以下:json
#是否須要打包
is_need_package=true
#生產環境/開發環境證書設置
code_sign_distribution="iPhone Distribution: xxxx xxx (xxx)"
code_sign_development="iPhone Developer: xxxx xxxx (xxx)"
#項目的target/scheme設置
package_target_name="xxx"
package_scheme_name="xxx"
#打包結果根目錄
package_root_path=/Users/xxx/Desktop/Package/
#打包ipa目錄
package_archive_path=/Users/xxx/Desktop/Package/Archive/
#打包類型 AdHoc:1 AppStore:2 Enterprise:3 Development:4
package_export_type=1
#打包存放的xxxExportOptions.plist文件夾
package_export_options_dir_path=./AutoPacking/Plist/
#是否使用workspace進行打包
package_use_workspace=true
#是否使用Release模式進行打包
package_use_release=true
#記錄打包前綴
record_perfix="CEG"
複製代碼
是否是以爲很簡單,可是閱讀性不是很高吧, 在Xcode中沒有高亮顯示,都是一個色容易犯困。可是使用超級簡單xcode
source global.config
複製代碼
是否是很簡單,😀😀,只須要一個命令,便可得到所有的全局變量,$is_need_package就能得到值,路徑也是,因此我選擇了這種方式來進行作配置文件,對不一樣的人有不一樣的效果,對我不熟悉腳本的人真的太有好了。有個注意點就是一行只能寫一個變量,而且變量和值之間只能有等於號,不能有空格號來進行對齊🤡🤡,是否是以爲連最後的美化機會都沒有啦😏。其實習慣就好。🙂bash
小夥子別盯着看標題哈,思想很危險哈😅,我不是來開車的,說完配置文件就該說說這個打包腳本了。還記得準備工做中說了要在SVN中嗎,由於用到了Jenkins中全局構建參數SVN_REVISION,另外看這個腳本其實有點煩,由於很大部分代碼是在作前戲工做😉😉,真正xcodebuild命令在末尾了。另外我寫腳本喜歡都是小寫,全大寫的看着頭痛啊, 因此我也不知道這符不符合腳本的代碼規範吧,可是自我感受良好😎😎。服務器
#!/bin/sh
# 當前shell所處的目錄
shell_dir_path=$(cd "$(dirname "$0")"; pwd)
# 把配置文件裏的參數在該腳本里全局化
source $shell_dir_path"/global.config"
echo "開始檢查環境..........."
# 若是歸檔文件夾不存在則建立
if [ ! -d $package_root_path ]; then
mkdir -pv $package_root_path
fi
# 若是不存在記錄文件則建立,存在文件則把文件內容變量加入到全局變量
if [ -e $package_root_path"result.txt" ]; then
source $package_root_path"result.txt"
# 判斷svn最後提交信息是否已經歸檔過,其中SVN_REVISION是SVN提交時的編號,改變量
# 是Jenkins提供的
if [ $lastestBuildVersion = $SVN_REVISION ] ; then
echo "該版本已經打過包了,請從新提交一次記錄並確保is_need_package爲true"
exit 1
else
# 爲了節省空間,因此每次打包都會在開始前移除以前文件
rm -rf $package_archive_path*
echo "移除舊項目完畢,準備工做已就緒"
fi
else
# 建立記錄文檔
touch $package_root_path"/result.txt"
chmod -R 777 $package_root_path"/result.txt"
echo lastestBuildVersion=0 >> $package_root_path"/result.txt"
fi
# 檢查是否須要打包
if ! $is_need_package; then
exit 1
fi
# 檢查是否設置target/scheme
if test -z $package_target_name; then
echo "❌ 項目Target設置爲空"
exit 1
fi
if test -z $package_scheme_name; then
echo "❌ 項目Scheme設置爲空"
exit 1
fi
# 讀取配置文件歸檔類型是Release仍是Debug
if $package_use_release; then
build_configuration="Release"
else
build_configuration="Debug"
fi
# AdHoc: 1, AppStore: 2, Enterprise: 3, Development: 4
# 導出ipa包的plist文件夾,該文件在打包時會生成
options_dir_path=$package_export_options_dir_path
if [[ $package_export_type -eq 1 ]]; then
export_options_plist_path=$options_dir_path"AdHocExportOptions.plist"
export_type_name="AdHoc"
elif [[ $package_export_type -eq 2 ]]; then
export_options_plist_path=$options_dir_path"AppStoreExportOptions.plist"
export_type_name="AppStore"
elif [[ $package_export_type -eq 3 ]]; then
export_options_plist_path=$options_dir_path"EnterpriseExportOptions.plist"
export_type_name="Enterprise"
elif [[ $package_export_type -eq 4 ]]; then
export_options_plist_path=$options_dir_path"DevelopmentExportOptions.plist"
export_type_name="Development"
fi
echo "✅✅✅ 校驗參數以及環境成功"
echo "⚡️ ⚡️ ⚡️即將開始打包 ⚡️ ⚡️ ⚡️"
##############################自動打包部分##############################
# 返回到工程目錄
cd ../
project_path=`pwd`
# 獲取項目名稱
project_name=`find . -name *.xcodeproj | awk -F "[/.]" '{print $(NF-1)}'`
# 指定工程的Info.plist
current_info_plist_name="Info.plist"
# 配置Info.plist的路徑
current_info_plist_path="${project_name}/${current_info_plist_name}"
# 獲取項目的版本號
bundle_version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${current_info_plist_path}`
# 獲取項目的編譯版本號
bundle_build_version=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" ${current_info_plist_path}`
# 當前版本存放導出文件路徑,能夠根據需求添加不一樣的路徑
currentVersionArchivePath="${package_archive_path}"
# 判斷歸檔當前版本文件夾是否存在,不存在則建立
if [ ! -d $currentVersionArchivePath ]; then
mkdir -pv $currentVersionArchivePath
chmod -R 777 $currentVersionArchivePath
fi
# 歸檔文件路徑
export_archive_path="${currentVersionArchivePath}${package_scheme_name}.xcarchive"
# ipa導出路徑
export_ipa_path="${currentVersionArchivePath}"
# 獲取時間 如:20190630_1420
# current_date="$(date +%Y%m%d_%H%M)"
# ipa 名字, 能夠根據版本號來進行重命名
ipa_name="${package_scheme_name}_${SVN_REVISION}.ipa"
echo "工程目錄 = ${project_path}"
echo "工程Info.plist路徑 = ${current_info_plist_path}"
echo "打包類型 = ${build_configuration}"
echo "打包使用的plist文件路徑 = ${export_options_plist_path}"
###############################打包部分#########################################
echo "🔆🔆🔆正在爲您開始打包🚀🚀🚀🚀🚀🚀"
# 是否使用xxx.xcworkspace工程文件進行打包
if $package_use_workspace; then
if [[ ${build_configuration} == "Debug" ]]; then
# 1. Clean
xcodebuild clean -workspace ${project_name}.xcworkspace \
-scheme ${package_scheme_name} \
-configuration ${build_configuration}
# 2. Archive
xcodebuild archive -workspace ${project_name}.xcworkspace \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} \
CFBundleVersion=${bundle_build_version} \
-destination generic/platform=ios \
elif [[ ${build_configuration} == "Release" ]]; then
# 1. Clean
xcodebuild clean -workspace ${project_name}.xcworkspace \
-scheme ${package_scheme_name} \
-configuration ${build_configuration}
# 2. Archive
xcodebuild archive -workspace ${project_name}.xcworkspace \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} \
CFBundleVersion=${bundle_build_version} \
-destination generic/platform=ios \
fi
else
if [[ ${build_configuration} == "Debug" ]] ; then
# 1. Clean
xcodebuild clean -project ${project_name}.xcodeproj \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-alltargets
# 2. Archive
xcodebuild archive -project ${project_name}.xcodeproj \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} \
CFBundleVersion=${bundle_build_version} \
-destination generic/platform=ios \
elif [[ ${build_configuration} == "Release" ]]; then
# 1. Clean
xcodebuild clean -project ${project_name}.xcodeproj \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-alltargets
# 2. Archive
xcodebuild archive -project ${project_name}.xcodeproj \
-scheme ${package_scheme_name} \
-configuration ${build_configuration} \
-archivePath ${export_archive_path} \
CFBundleVersion=${bundle_build_version} \
-destination generic/platform=ios \
fi
fi
# 檢查是否構建成功
# 由於xxx.xcarchive 是一個文件夾不是一個文件
if [ -d ${export_archive_path} ]; then
echo "🚀 🚀 🚀 項目構建成功 🚀 🚀 🚀"
else
echo "⚠️ ⚠️ ⚠️ 項目構建失敗 ⚠️ ⚠️ ⚠️"
exit 1
fi
echo "開始導出ipa文件"
# 導出ipa文件
xcodebuild -exportArchive -archivePath ${export_archive_path} \
-exportPath ${export_ipa_path} \
-destination generic/platform=ios \
-exportOptionsPlist ${export_options_plist_path} \
-allowProvisioningUpdates
# 默認導出ipa文件路徑
export_ipa_name=$export_ipa_path$package_scheme_name".ipa"
# 判斷是否有這個導出ipa文件
if [ -e $export_ipa_name ]; then
# 更更名稱爲 scheme_version.ipa scheme名稱爲工程名稱,version爲svn最後提交的版本
mv $export_ipa_name $export_ipa_path$ipa_name
# 將當前版本設爲已打包狀態
echo lastestBuildVersion=$SVN_REVISION > $package_root_path"result.txt"
echo "🎉 🎉 🎉 導出 ${ipa_name}.ipa 包成功 🎉 🎉 🎉"
else
echo "❌ ❌ ❌ 導出 ${ipa_name}.ipa 包失敗 ❌ ❌ ❌"
fi
# 輸出打包總用時
echo "本次打包總耗時: ${SECONDS}s"
複製代碼
是否是有點頭暈哇,我是頭痛啊🤗🤗,玩過使用Jenkins腳本自動打包的人看完這個應該知道個人曲線救國方式了,有的同窗確定還在😳😳or🤔🤔。咱們在重頭戲中講🤗🤗。
看風格我就不是開車的人,由於開車哪來這麼多前戲是吧?🤔🤔,開始剝絲抽繭啦。
前面說到在Jenkins中沒有相似Git這樣的提交代碼自動打包的鉤子插件。而咱們使用Jenkins的目的就是爲了釋放程序員的👐,留着更多時間幹正事,若是不能實現提交自動打包那有無Jenkins都同樣了,還不如本身寫個腳本打包呢,是吧?若是是你你會怎麼作呢?
咱們知道Jenkins有個按期執行任務的選項,咱們只需填上參數:H/2 * * * * 而後就會每兩分鐘執行一次,是否是想說爲啥不設置1分鐘,我也想哇,Jenkins不讓啊😞😞。可是總不能讓這傢伙每兩分鐘一直打吧,覆蓋還好,可是每兩分鐘就有一個新的安裝包上傳至測試服務器也很差吧,感受會被測試人員打屎😱😱。那麼咱們就須要解決這個打斷打包的條件🤔。既不能在工程中設置,否則Jenkins每次執行任務就覆蓋了,也就是隻能在打包電腦上保存一個狀態。那麼很好解決啦,咱們只須要在腳本中建立一個文件來記錄上次打包的SVN_REVISION,就能讓腳本讀取文件記錄判斷是否已經打過包了,沒有打過包才執行程序,不然退出程序。😉😉如今想一想是否是很簡單哇,而後就能夠盡情的噠噠噠。
使用FTP上傳至測試服務器這部分該如何解決呢?由於涉及到可變參數,腳本那麼差的我只能使用土辦法,一招一招來,幹到不服爲止😁😁。這部分腳本是上面那長串代碼的末尾後的代碼。
############################上傳部分#####################################
function createUploadShell(){
touch upload.sh
chmod -R 777 upload.sh
echo "cd ${package_archive_path}" >> upload.sh
echo "ftp -i -n -v << !" >> upload.sh
echo "open 117.xx.xxx.xxx" >> upload.sh
echo "user oymuzi xxxx" >> upload.sh
echo "cd ./${upload_dir_path}" >> upload.sh
current_date="$(date +%Y%m%d%H%M%S)"
ipa_new_name=$package_scheme_name"_"$current_date".ipa"
echo "binary" >> upload.sh
echo "put ${upload_volumes_name}${package_archive_path}${ipa_name} ./${ipa_new_name}" >> upload.sh
echo "close" >> upload.sh
echo "bye" >> upload.sh
echo "!" >> upload.sh
}
cd $package_archive_path
createUploadShell
echo "建立上傳腳本成功"
echo "🚀 🚀 🚀 開始上傳至雲端 🚀 🚀 🚀"
sh upload.sh
echo "上傳至雲端完成"
rm -f upload.sh
複製代碼
對,就是這麼feel 倍爽,大佬能夠本身進行更改腳本。其中一點就是使用FTP命令必須使用ftp -i -n -v << ! 你想要的執行的FTP命令 !
。至於上傳公司服務器仍是蒲公英等平臺,這個能夠根據各自公司的規則吧,本身進行修改哈。
源碼地址:OMPackaging(注:此demo僅爲了展現源碼,按照源碼和文章所說的辦法設置環境便可正常打包)
利用Jenkins的定死執行任務功能,在本地保存一個打包的狀態,而後就能每次提交代碼後進行自動打包,有1-2分鐘的延遲,並且要執行打包必須確保配置文件中的is_need_package
爲true並提交一次代碼。在上傳至測試服務器,測試人員就能直接下載安裝。這個方案雖然有些延遲,可是解決了自動打包的問題。
參考: