iOS持續(xù)化集成之Jenkins (三)

前言
在之前的兩篇iOS持續(xù)化集成之Jenkins (一)iOS持續(xù)化集成之Jenkins (二)中介紹了 Jenkins 環(huán)境搭建以及配合插件實現(xiàn)了自動化打包分發(fā) APP,但是我覺得用起來還是不夠爽,所以就有了這么一篇利用 Jenkins+shell+python實現(xiàn)更加自由的持續(xù)化集成自動化

1、Jenkins 可選參數(shù)配置

這一塊不是重點(diǎn),我這里只是舉個例子,大家根據(jù)自己的需求來定制,比如tage、Debug、Release等等,就算在不同項目簡單修改 shell 腳本達(dá)到目的也是 ok 的
選項參數(shù),選項框里填寫選項,每個選項換行填寫,作用是在下面構(gòu)建的過程變得比較靈活,例如下圖,特別說明下取值為
${名稱},例如${Archive}

參數(shù)化構(gòu)建過程.png

2、shell & Python

前期準(zhǔn)備

這里呢,為了便于復(fù)用到其他項目,也為了后面Python 讀取SVN日志,建議大家的做法是在項目的根目錄建立一個目錄用于存放 shell ,Python腳本以及項目需要用到的等等,比如


package目錄.png

ad-hoc是用來到處ipa的配置文件,這個文件你可以用xcode手動打包導(dǎo)出一次,在導(dǎo)出的目錄中ExportOptions.plist這個配置文件就是我們要的,AutoPackage.sh 打包腳本,SVNLog.py讀取Jenkins日志用的,代碼在下文

重要注意點(diǎn)

xcode里面的證書管理必須為手動管理,然后分別選擇debug,release證書,證書到蘋果開發(fā)者網(wǎng)站生成并安裝到電腦中

2.1 shell

步驟:構(gòu)建->增加構(gòu)建步驟->shell


shell.png

那么 shell 腳本怎么寫呢,不廢話直接上代碼,在代碼中注釋,比較清晰,我這里用到${Archive}可選參數(shù)取值,大家可以按照自己的需求稍加修改

export LANG="en_US.UTF-8"


####################參數(shù)、環(huán)境變量定義#########################
#工程項目路徑
projectPath="$(pwd)"
#工程項目名稱
projectName="xxxx"
#工程項目打包模式
buildConfiguration="Release"
#IPA配置文件
exportOptionsPlist="${projectPath}/Package/${Archive}.plist"

#證書
ADHOCCODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
DEVELOPMENT_TEAM="跟在iPhone Distribution:xxxx后面括號里面的值"
#描述文件
Main_Provisioning_Profile="xxxx-xxxx-xxxx-xxxx-xxxxx"
Extension_Provisioning_Profile="xxxx-xxxx-xxxx-xxxx-xxxxx"

#build文件路徑
buildPath="${projectPath}/build"
#發(fā)布文件路徑
releasePath="${projectPath}/build/Release-iphoneos"
#archive保存路徑
archivePath="${projectPath}/archive"
archiveName="${projectName}.xcarchive"
archiveFilePath="${archivePath}/${archiveName}"

#ipa保存路徑
ipaPath="${projectPath}/ipa"
#log日志路徑
logfilePath="${projectPath}/ChangeLog"



#先刪除存在的文件目錄
rm -rdf "$buildPath"
rm -rdf "$archivePath"
rm -rdf "$ipaPath"
rm -rdf "${logfilePath}"
#再創(chuàng)建新的文件目錄
mkdir "$buildPath"
mkdir "$releasePath"
mkdir "$archivePath"
mkdir "$ipaPath"
touch "${logfilePath}"

echo "***********************參數(shù)、環(huán)境變量***********************"
echo "當(dāng)前目錄路徑-------->${projectPath}"
echo '打包模式:'$buildConfiguration
echo '工程目錄:'$projectPath
echo '工程名稱:'$projectName
echo '安裝包路徑 '$archiveFilePath 
echo '\n'

echo "***********************開始build archive app文件***********************"


#打包的命令
xcodebuild -workspace "${projectPath}/${projectName}.xcworkspace" -scheme "$projectName" -configuration ${buildConfiguration} -archivePath "${archiveFilePath}" CONFIGURATION_BUILD_DIR="${releasePath}" DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" CODE_SIGN_IDENTITY="${ADHOCCODE_SIGN_IDENTITY}" APP_PROFILE="${Main_Provisioning_Profile}" EXTENSION_PROFILE="${Extension_Provisioning_Profile}" clean archive


EXCODE=$?
if [ "$EXCODE" == "0" ]; then
echo "O.K"
else
echo "***********************編譯失敗********************************"
exit 1
fi

#導(dǎo)出ipa文件
xcodebuild -exportArchive -archivePath ${archiveFilePath} -exportPath ${ipaPath} -exportOptionsPlist $exportOptionsPlist

echo "***********************結(jié)束build archive app文件***********************"

echo "***********************設(shè)置包名稱信息***********************"
#app文件存放位置和命名
appPath="${archiveFilePath}/Products/Applications"
appFile="${appPath}/${projectName}.app"
#app文件中Info.plist文件路徑
appInfoPlistPath=$appFile/Info.plist
#取版本號
version=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${appInfoPlistPath})
#取Build號
buildNo=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${appInfoPlistPath})
#取bundle id
bundleId=$(/usr/libexec/PlistBuddy -c "print CFBundleIdentifier" ${appInfoPlistPath})
#取應(yīng)用名稱
appName=$(/usr/libexec/PlistBuddy -c "print CFBundleDisplayName" ${appInfoPlistPath})
#包編譯類型(ad-hoc,enterprise...)
buildMethod=$(/usr/libexec/PlistBuddy -c "print method" ${exportOptionsPlist})
#打包的時間
date=$(date +%Y%m%d%H%M)
#判斷放ipa包的目錄是否存在
destinationPath="{這里填上你最后想保存的路徑目錄}/${buildMethod}/${projectName}/${version}"
if [ ! -d "$destinationPath" ]; then
    mkdir -p "$destinationPath"
fi
ipaFile="${projectName}_${buildMethod}_${version}(${date}).ipa"
dSYMFile="${projectName}_${buildMethod}_${version}(${date}).app.dSYM"
ipaFilePath="${destinationPath}/${ipaFile}"
dSYMFilePath="${destinationPath}/${dSYMFile}"

#將ipa跟dSYM移動到指定目錄下
mv -f "${releasePath}/${projectName}.ipa" $ipaFilePath
mv -f "${releasePath}/${projectName}.app.dSYM" $dSYMFilePath
echo "** 安裝包最終存放路徑--->${ipaFilePath} **"

echo "*************************開始上傳到fir**************************"

fir login "fir 登錄的 token"

fir me


if [ ! -f "$logfileDir" ]; then
fir publish ${ipaFilePath} -c "無更新記錄"
else
fir publish ${ipaFilePath} -c ${logfileDir}
fi
echo "*************************結(jié)束上傳到fir**************************"

echo "*************************開始上傳到蒲公英**************************"

curl -F "file=${ipaFilePath}" \
 -F "updateDescription=${logfileDir}" \
-F "uKey=蒲公英賬戶中心 userkey" \
-F "_api_key=蒲公英賬戶中心 apikey" \
https://www.pgyer.com/apiv1/app/upload
echo "*************************結(jié)束上傳到蒲公英**************************"
#移除日志文件
rm -rdf "${logfileDir}"
exit

提示,fir 和 蒲公英 需要先要安裝環(huán)境,具體查閱 fir 官網(wǎng)官方文檔蒲公英官網(wǎng)文檔

python

其實呢,在Jenkins 構(gòu)建后都會在Jenkins所安裝的目錄的jobs生成對應(yīng)項目的編譯相關(guān)文件,其中就包括了 svn 日志


編譯文件.png

那我們就去讀取這個文件并保存到我們指定的目錄去,然后在上傳到蒲公英等第三方托管平臺,這個在上面 shell 腳本的末端寫了,下面,就直接上 Python 代碼

from xml.dom.minidom import parse
import xml.dom.minidom,sys,os

# 相關(guān)目錄

numbulindline = open('/Users/Shared/Jenkins/jobs/項目名稱/nextBuildNumber','r').readline()
needNumbulindline = int(numbulindline)-1
xmlPath = '/Users/Shared/Jenkins/jobs/項目名稱)/builds/%d/changelog.xml'%needNumbulindline
#保存的文件名稱
txtPath = 'ChangeLog'

#文件寫入編碼
reload(sys)
sys.setdefaultencoding('utf8')

#寫入log到txt
def text_write(text):
    #保存的路徑
    logPath = "./%s"%txtPath
    file = open(logPath,'a')
    file.write(text)
    file.close()

#獲取xml節(jié)點(diǎn)值方法
def get_xmlnode(node, name):
    return node.getElementsByTagName(name) if node else []
try:
    DOMTree = xml.dom.minidom.parse("%s"%xmlPath)
    collection = DOMTree.documentElement
    logentry = collection.getElementsByTagName("logentry")
    text_write("=============================\n")
    for index in range(len(logentry)):
        print "==========log日志寫入中========="
        logentrysub = logentry[index]
        author = get_xmlnode(logentrysub,'author')[0].firstChild.nodeValue
        date = get_xmlnode(logentrysub,'date')[0].firstChild.nodeValue [0:10]
        msgdom = get_xmlnode(logentrysub,'msg')[0].firstChild
        if msgdom != None:
            msg = msgdom.data
        else:
            msg = "空"
        text_write(author+"  "+date+"\n"+msg+"\n\n")
    text_write("=============================")
    print "==========log日志寫入完成========="
    print "==========log日志內(nèi)容========="
    f = open("./%s"%txtPath,'r')
    lines = f.readlines()
    for line in lines:
        print line
    f.close()
except Exception,e:
    print "==========xml文件不合法==========%s"%e

寫在最后,如果你不想用這種方式,你也可以使用
第三方工具比如fastlane,也是擁有比較成熟的打包方案,相關(guān)具體用法這里就不做闡述了,自行百度。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容