
前言
開發(fā)一段時(shí)間,每次在測試上線階段,你會發(fā)現(xiàn)有一種酸楚感,因?yàn)槟慵磳㈤_始一項(xiàng)沒有任何技術(shù)含量的build,archive,export的過程,這時(shí)候是最累的時(shí)候,如果你們的測試很敬業(yè),給你一點(diǎn)點(diǎn)的提bug,那么你將面臨的一次接一次的build,archive,export的過程,還有上傳第三方分發(fā)平臺,這個(gè)酸爽感??。。。。有人說不用啊,直接插我電腦上給裝一下就行了,而且現(xiàn)在Xcode還有無線調(diào)試了,都不用到我跟前來了,直接給安裝了。當(dāng)然小團(tuán)隊(duì)開發(fā)這種模式肯定是沒任何毛病的,但當(dāng)你公司有好幾個(gè)測試,一個(gè)個(gè)讓你給安裝最新版本,是不是還是挺煩的!而且你可能還有這樣一個(gè)困窘,當(dāng)測試給你提多個(gè)bug 的時(shí)候,你改著改著等你上線的時(shí)候,你還擔(dān)心這個(gè)提交的包到底是不是最新的版本打的包,心里多少有點(diǎn)害怕出錯(cuò),這時(shí)候這篇文章可以解決你所有的擔(dān)心以及減輕你所有的工作量,如果你說這個(gè)情況也不存在,那你可以關(guān)閉這個(gè)網(wǎng)頁尋找你感興趣的文章了。
正文
要徹底明白這個(gè)打包的過程我們還需要了解以下幾個(gè)概念
官方文檔中對這個(gè)有明確的解釋
Xcode Workspace:一個(gè)工作空間包括多個(gè)projects
A workspace is an Xcode document that groups projects and other documents so you can work on them together.
A workspace can contain any number of Xcode projects, plus any other files you want to include.
In addition to organizing all the files in each Xcode project, a workspace provides implicit and explicit relationships among the included projects and their targets.
Xcode Target:多個(gè)Target可以組成一個(gè)project,說白了就是一個(gè)依賴
A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace.
A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product.
Projects can contain one or more targets, each of which produces one product.
Xcode Project:一個(gè)project就是一個(gè)項(xiàng)目,可以包含多個(gè)targets,而通過 build settings來定義如何build.
An Xcode project is a repository for all the files, resources, and information required to build one or more software products.
A project contains all the elements used to build your products and maintains the relationships between those elements.
It contains one or more targets, which specify how to build products.
A project defines default build settings for all the targets in the project (each target can also specify its own build settings, which override the project build settings).
Xcode Scheme:scheme是來定義一系列targets來build
一個(gè)scheme只能應(yīng)用于一個(gè)workspace。
An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.
You can have as many schemes as you want, but only one can be active at a time.
You can specify whether a scheme should be stored in a project—in which case it’s available in every workspace that includes that project, or in the workspace—in which case it’s available only in that workspace.
When you select an active scheme, you also select a run destination (that is, the architecture of the hardware for which the products are built).
Workspace:簡單來說,Workspace就是一個(gè)容器,在該容器中可以存放多個(gè)你創(chuàng)建的Xcode Project, 以及其他的項(xiàng)目中需要使用到的文件。使用Workspace的好處有,1),擴(kuò)展項(xiàng)目的可視域,即可以在多個(gè)項(xiàng)目之間跳轉(zhuǎn),重構(gòu),一個(gè)項(xiàng)目可以使用另一個(gè)項(xiàng)目的輸出。Workspace會負(fù)責(zé)各個(gè)Project之間提供各種相互依賴的關(guān)系;2),多個(gè)項(xiàng)目之間共享Build目錄。
Project:指一個(gè)項(xiàng)目,該項(xiàng)目會負(fù)責(zé)管理生成一個(gè)或者多個(gè)軟件產(chǎn)品的全部文件和配置,一個(gè)Project可以包含多個(gè)Target。
Target:一個(gè)Target是指在一個(gè)Project中構(gòu)建的一個(gè)產(chǎn)品,它包含了構(gòu)建該產(chǎn)品的所有文件,以及如何構(gòu)建該產(chǎn)品的配置。
Scheme:一個(gè)定義好構(gòu)建過程的Target成為一個(gè)Scheme??稍赟cheme中定義的Target的構(gòu)建過程有 Build/Run/Test/Profile/Analyze/Archive
BuildSetting:配置產(chǎn)品的Build設(shè)置,比方說,使用哪個(gè)Architectures?使用哪個(gè)版本的SDK?。在Xcode Project中,有Project級別的Build Setting,也有Target級別的Build Setting。Build一個(gè)產(chǎn)品時(shí)一定是針對某個(gè)Target的,因此,XCode中總是優(yōu)先選擇Target的Build Setting,如果Target沒有配置,則會使用Project的Build Setting。
2.build -> archive -> export 到底干了什么
平時(shí)開發(fā)中我們很輕易的點(diǎn)擊了 command + B 執(zhí)行了一次build的過程 ,其實(shí)對Xcode 來發(fā)你給他下發(fā)了一條指令,我們可以將編譯過后的右紅變黑的 項(xiàng)目名.app 點(diǎn)擊
Show in Finder 會發(fā)現(xiàn)build 文件夾下產(chǎn)生的一系列的文件,你會發(fā)現(xiàn)多個(gè)tagets 被加載進(jìn)來,并生成了一個(gè) 項(xiàng)目名.app的包,這轉(zhuǎn)換為為指令如下:
xcodebuild
我們 可以在終端執(zhí)行該條命令對項(xiàng)目進(jìn)行build,該指令還可以生成項(xiàng)目名.xcarchive文件,對應(yīng)為我們執(zhí)行選擇真機(jī)點(diǎn)擊archive過程,對應(yīng)指令如下
WORKSPACENAME :工作區(qū)名字
SCHEMENAME : scheme名字
CONFIGURATION : release 或者 Debug
ARCHIVEPATH :文件輸入路徑, 一般為cd 路徑下拼接地址
\ : 拼接符號
- archive
xcodebuild archive \
-workspace ${WORKSPACENAME}.xcworkspace \
-scheme ${SCHEMENAME} \
-configuration ${CONFIGURATION} \
-archivePath ${ARCHIVEPATH}
看到以上命令我們應(yīng)該就豁然了,例如給應(yīng)用商店上傳我們指定Release,在Xcode 8以前還需要指定描述文件以及開發(fā)者,也就是發(fā)布的描述文件,8以后都自動(dòng)管理了,所以會自己去讀取,不需要我們?nèi)ブ付?
最后一步便是生成 export

看到這個(gè)界面大家更應(yīng)該明白了export,對應(yīng)指令如下:
xcodebuild -exportArchive \
-archivePath ${ARCHIVEPATH}.xcarchive \
-exportPath ${IPAPATH} \
-exportOptionsPlist ExportOptions.plist \
需要明確以下幾點(diǎn):
1.Xcode 8 以后使用以上新命令,xcrun指令被廢棄了!
2.這里ARCHIVEPATH 對應(yīng)的.xcarchive文件的輸出路徑,IPAPATH是ipa 輸出地址
3.ExportOptions.plist對應(yīng)的已怎樣的方式輸出,相當(dāng)于一個(gè)配置文件,我們需要以什么樣的方式輸出.ipa。

對應(yīng)的是以下我們手動(dòng)打包的方式:



以上生成.ipa的流程我們應(yīng)該明了于心了,接下來我們用shell腳本將整個(gè)流程自動(dòng)化。
shell 腳本
#!/bin/bash
#workspaceName
WORKSPACENAME='xxx'
#schemeName 可以在工程根目錄下使用 xcodebuild -list 命令查看schemes
SCHEMENAME= 'xxxx'
#(Release or Debug)
CONFIGURATION='xxxxxx '
#獲取時(shí)間,這個(gè)格式可以自己定義
DATE=`date +%Y%m%d_%H%M`
#源工程的地址,默認(rèn)獲取cd 路徑下的地址
SOURCEPATH=$( cd "$( dirname $0 )" && pwd)
#archieve 輸出地址,這個(gè)路徑可以你自己定義
ARCHIVEPATH=${SOURCEPATH}/AutoBuildIPA/${BRANCHNAME}/${DATE}
#ipa 包輸出地址,這個(gè)路徑也可以你自己定義
IPAPATH=${SOURCEPATH}/AutoBuildIPA/${BRANCHNAME}/${SCHEMENAME}_${DATE}
#ipa 包名
IPANAME=${SCHEMENAME}.ipa
echo "~~~~~~~~~~~~~~~~開始清理~~~~~~~~~~~~~~~~~~~"
# 清理 避免出現(xiàn)一些莫名的錯(cuò)誤
xcodebuild clean \
-workspace ${WORKSPACENAME}.xcworkspace \
-configuration ${CONFIGURATION} \
-scheme ${SCHEMENAME}
echo "~~~~~~~~~~~~~~~~開始構(gòu)建archive~~~~~~~~~~~~~~~~~~~"
# build xxx
xcodebuild archive \
-workspace ${WORKSPACENAME}.xcworkspace \
-scheme ${SCHEMENAME} \
-configuration ${CONFIGURATION} \
-archivePath ${ARCHIVEPATH}
#判斷 'xx.xcarchive' 文件存在,即 build 成功
if [ -e ${ARCHIVEPATH}.xcarchive ]; then
echo "xcodebuild Successful"
else
echo "erro:Build failed!!"
exit 1 #退出命令執(zhí)行
fi
echo "~~~~~~~~~~~~~~~~開始輸出ipa~~~~~~~~~~~~~~~~~~~"
# xcrun xxx
xcodebuild -exportArchive \
-archivePath ${ARCHIVEPATH}.xcarchive \
-exportPath ${IPAPATH} \
-exportOptionsPlist ExportOptions.plist \
# 如果 xx.ipa 存在則輸出成功
if [ -e ${IPAPATH}/${IPANAME} ]; then
echo "\n-------------------------\n\n\n"
echo "Configurations! Build Successful!"
echo "\n\n\n-------------------------\n\n"
else
echo "\n---------------------------------------------------------------\n"
echo "erro:Create IPA failed!!"
echo "\nPlease check the case of failure and contact developers,thanks!"
echo "Export Error Path : ${IPAPATH}/${IPANAME}"
echo "\n---------------------------------------------------------------\n"
exit 1
fi
以上是便是腳本文件,直接將腳本放在項(xiàng)目工程同級目錄下,在終端執(zhí)行 sh xxxx.sh(注:xxxx為腳本文件名),和python 腳本執(zhí)行是一樣的,接下來等待即可輸出ipa包。(注:大多出錯(cuò)就是路徑定義有問題,檢查路徑正確性即可排除問題)
接下來我們需要上傳蒲公英,我們可以查看官網(wǎng)文檔,發(fā)現(xiàn)很簡單的執(zhí)行步驟

我們可以在上述shell腳本中將這條命令添加上去
# 是否上傳蒲公英
UPLOADPGYER=false
#上傳ipa到蒲公英
if [ $UPLOADPGYER = true ]
then
echo "~~~~~~~~~~~~~~~~上傳ipa到蒲公英~~~~~~~~~~~~~~~~~~~"
#installType 是安裝類型
#password 是你設(shè)置的安裝密碼
# 上面兩個(gè)新增字段可以具體查看官方文檔
curl -F "file=@${IPAPATH}/${IPANAME}" \
-F "uKey=xxxx" \
-F "_api_key=xxxx" \
-F "installType=2" \
-F "password=1" \
https://www.pgyer.com/apiv1/app/upload --verbose
if [ $? = 0 ]
then
echo "~~~~~~~~~~~~~~~~上傳蒲公英成功~~~~~~~~~~~~~~~~~~~"
else
echo "~~~~~~~~~~~~~~~~上傳蒲公英失敗~~~~~~~~~~~~~~~~~~~"
echo "${IPAPATH}/${IPANAME}"
fi
fi
fi
那么上傳 Appstore 也是相應(yīng)的套路,蘋果官方提供了我們類似的方法,我們需要利用終端安裝
//安裝 xctool
brew install xctool
//查看版本
xctool -version
同樣我們可以將上傳Appstore 添加到對應(yīng)的shell 腳本中
# 是否上傳到APPStore
UPLOADAPPStore=false
#對應(yīng)appid
AppleID='xxx'
#pwd
AppleIDPWD='xxx'
# 上傳AppStore
#首先是驗(yàn)證過程,看你appstore 對應(yīng)的信息是否正確
if [ $UPLOADAPPStore = true ]
then
altoolPath="/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
${altoolPath} --validate-app \
-f ${IPAPATH}/${IPANAME} \
-u ${AppleID} \
-p ${AppleIDPWD} \
-t ios --output-format xml
#驗(yàn)證成功后直接上傳
if [ $? = 0 ]
then
echo "~~~~~~~~~~~~~~~~驗(yàn)證ipa成功~~~~~~~~~~~~~~~~~~~"
${altoolPath} --upload-app \
-f ${IPAPATH}/${IPANAME} \
-u ${AppleID} \
-p ${AppleIDPWD} \
-t ios --output-format xml
if [ $? = 0 ]
then
echo "~~~~~~~~~~~~~~~~提交AppStore成功~~~~~~~~~~~~~~~~~~~"
else
echo "~~~~~~~~~~~~~~~~提交AppStore失敗~~~~~~~~~~~~~~~~~~~"
fi
else
echo "~~~~~~~~~~~~~~~~驗(yàn)證ipa失敗~~~~~~~~~~~~~~~~~~~"
fi
以上整個(gè)自動(dòng)化過程已經(jīng)完成了,接下來我們看本地jenkins 搭建,首先安裝
brew install jenkins
安裝完成等待 install,可以在終端使用
jenkins
來啟動(dòng)jenkins,然后在瀏覽器輸入框輸入
http://localhost:8080
打開Jenkins,我們?nèi)缓笮陆?xiàng)目(注:以上詳細(xì)安裝過程,以及plugin缺失問題,都可以通過百度,google方式解決,我們說具體操作)
第一步:新建項(xiàng)目

第二步:設(shè)置相關(guān)的配置

2.1 填寫項(xiàng)目相關(guān)信息

2.2倉庫地址需要填寫到項(xiàng)目的根路徑那一級為止


svn 和 git 是一樣的設(shè)置。
2.3 在增加構(gòu)建步驟中選擇Xcode

2.4簽名設(shè)置,填寫鑰匙串的路徑,通過該路徑Jenkins可以找到打包證書

2.5高級設(shè)置,Scheme同shell腳本填寫是一致的

2.6上傳蒲公英或者App Store

App Store 參照腳本命令執(zhí)行。
附:當(dāng)提交新代碼時(shí)候設(shè)置自動(dòng)打包

完成以上配置,點(diǎn)擊保存后,點(diǎn)擊立即構(gòu)建則會輸出同shell 腳本同樣的結(jié)果。
參照J(rèn)enkins 文章:
[0]http://blog.csdn.net/yaoliangjun306/article/details/72471429
相應(yīng)配置文件的github地址
https://github.com/markdashi/shellIPA
將 package.sh 和 ExportOptions.plist 導(dǎo)入同級目錄下,設(shè)置好相應(yīng)參數(shù),執(zhí)行腳本即可。
2020-8-27 更新
Xcode更新11之后,不再包含Application Loader。為了更好的支持ipadOS、macOS、iOS統(tǒng)一管理。
所以上傳AppStore的命令發(fā)生了變化

如果賬號沒有開啟雙步驗(yàn)證,可以通過
-u -p賬號密碼上傳驗(yàn)證,如果開啟了,需要采取密鑰上傳
# 上傳AppStore
#首先是驗(yàn)證過程,看你appstore 對應(yīng)的信息是否正確
if [ $UPLOADAPPStore = true ]
then
xcrun altool --validate-app \
-f ${IPAPATH}/${IPANAME} \
-apiKey ${apiKey} \
-apiIssuer ${apiIssuer} \
-t ios --output-format xml --verbose
fi
#驗(yàn)證成功后直接上傳
if [ $? = 0 ]
then
echo "~~~~~~~~~~~~~~~~驗(yàn)證ipa成功~~~~~~~~~~~~~~~~~~~"
xcrun altool --upload-app \
-f ${IPAPATH}/${IPANAME} \
-apiKey ${apiKey} \
-apiIssuer ${apiIssuer} \
-t ios --output-format xml --verbose
if [ $? = 0 ]
then
echo "~~~~~~~~~~~~~~~~提交AppStore成功~~~~~~~~~~~~~~~~~~~"
else
echo "~~~~~~~~~~~~~~~~提交AppStore失敗~~~~~~~~~~~~~~~~~~~"
fi
else
echo "~~~~~~~~~~~~~~~~驗(yàn)證ipa失敗~~~~~~~~~~~~~~~~~~~"
fi
apiKey apiIssuer 的獲取方式可以參照