In Jenkins (1) and Jenkins (2) of iOS Sustainable Integration, Jenkins environment building and automatic packaging and distribution of APP with plug-ins were introduced. However, I think it is still not cool enough to use. So there is an article that uses Jenkins+shell+ Python to achieve more free and persistent integration automation
1. Jenkins Optional parameters
This is not the focus, I just take an example here, you can customize according to your own needs, such as TAGE, Debug, Release, etc., even if you simply modify the shell script in different projects to achieve the purpose, the ok option parameter is filled in the option box, and each option is filled in line. The effect is to make the following build procedures more flexible, as shown in the figure below, especially with the value ${name}, such as ${Archive}
Shell & Python
For reuse in other projects, and for Python to read SVN logs later, it is recommended that you create a directory in the root directory of the project to store shells, Python scripts, and other items needed by the project, for example
Ad-hoc is a configuration file that you can export manually using Xcode. In the exported directory, exportOptions. plist is the configuration file we want. Autopackage. sh package script, used by SVNlog. py to read Jenkins logs, code below
Important points
The certificate management in Xcode must be manually managed, then select Debug, Release certificate, certificate to The Apple developer website to generate and install to the computer
2.1 the shell
Steps: Build -> Add build steps -> Shell
${Archive} ${Archive} ${Archive} ${Archive} ${Archive
export LANG="en_US.UTF-8"
# # # # # # # # # # # # # # # # # # # # parameters and environment variables defined # # # # # # # # # # # # # # # # # # # # # # # # #
# Engineering project path
projectPath="$(pwd)"
# Project name
projectName="xxxx"
# Engineering project packaging mode
buildConfiguration="Release"
IPA configuration file
exportOptionsPlist="${projectPath}/Package/${Archive}.plist"
# certificate
ADHOCCODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
DEVELOPMENT_TEAM="The value in parentheses following iPhone Distribution: XXXX"
# description file
Main_Provisioning_Profile="xxxx-xxxx-xxxx-xxxx-xxxxx"
Extension_Provisioning_Profile="xxxx-xxxx-xxxx-xxxx-xxxxx"
#build file path
buildPath="${projectPath}/build"
Publish file path
releasePath="${projectPath}/build/Release-iphoneos"
#archive Save path
archivePath="${projectPath}/archive"
archiveName="${projectName}.xcarchive"
archiveFilePath="${archivePath}/${archiveName}"
# IPA save path
ipaPath="${projectPath}/ipa"
#log Log path
logfilePath="${projectPath}/ChangeLog"
Delete the existing file directory first
rm -rdf "$buildPath"
rm -rdf "$archivePath"
rm -rdf "$ipaPath"
rm -rdf "${logfilePath}"
Create a new file directory
mkdir "$buildPath"
mkdir "$releasePath"
mkdir "$archivePath"
mkdir "$ipaPath"
touch "${logfilePath}"
echo "* * * * * * * * * * * * * * * * * * * * * * * parameters, environment variables, * * * * * * * * * * * * * * * * * * * * * * *"
echo "Current directory path -------->${projectPath}"
echo 'Packaging Mode:'$buildConfiguration
echo 'Project Catalogue:'$projectPath
echo Project Name:$projectName
echo 'Installation package path'$archiveFilePath
echo '\n'
echo "* * * * * * * * * * * * * * * * * * * * * * * began to build archive app file * * * * * * * * * * * * * * * * * * * * * * *"
The package command
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 "* * * * * * * * * * * * * * * * * * * * * * * compile * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
exit 1
fi
Export the IPA file
xcodebuild -exportArchive -archivePath ${archiveFilePath} -exportPath ${ipaPath} -exportOptionsPlist $exportOptionsPlist
echo "* * * * * * * * * * * * * * * * * * * * * * * end of build archive app file * * * * * * * * * * * * * * * * * * * * * * *"
echo "* * * * * * * * * * * * * * * * * * * * * * * set package name information * * * * * * * * * * * * * * * * * * * * * * *"
#app file location and name
appPath="${archiveFilePath}/Products/Applications"
appFile="${appPath}/${projectName}.app"
# info.plist file path in app file
appInfoPlistPath=$appFile/Info.plist
Get the version number
version=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${appInfoPlistPath})
# # for the Build
buildNo=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${appInfoPlistPath})
Take the bundle id #
bundleId=$(/usr/libexec/PlistBuddy -c "print CFBundleIdentifier" ${appInfoPlistPath})
Select the name of the application
appName=$(/usr/libexec/PlistBuddy -c "print CFBundleDisplayName" ${appInfoPlistPath})
# Package compile type (ad-hoc, Enterprise...)
buildMethod=$(/usr/libexec/PlistBuddy -c "print method" ${exportOptionsPlist})
# Time to pack
date=$(date +%Y%m%d%H%M)
Check whether the directory for storing ipA packets exists
destinationPath="{fill in the last directory you want to save}/${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}"
Move ipA and dSYM to the specified directory
mv -f "${releasePath}/${projectName}.ipa" $ipaFilePath
mv -f "${releasePath}/${projectName}.app.dSYM" $dSYMFilePath
echo "** Final directory of installation package -->${ipaFilePath}* *"
echo "* * * * * * * * * * * * * * * * * * * * * * * * * start uploaded to fir * * * * * * * * * * * * * * * * * * * * * * * * * *"
fir login "Fir login token"
fir me
if[!-f "$logfileDir" ]; then
fir publish ${ipaFilePath} -c "No update record"
else
fir publish ${ipaFilePath} -c ${logfileDir}
fi
echo "* * * * * * * * * * * * * * * * * * * * * * * * * end to fir * * * * * * * * * * * * * * * * * * * * * * * * * *"
echo "* * * * * * * * * * * * * * * * * * * * * * * * * began to upload the dandelion * * * * * * * * * * * * * * * * * * * * * * * * * *"
curl -F "file=${ipaFilePath}" \
-F "updateDescription=${logfileDir}" \
-F "UKey = Dandelion account center userkey" \
-F "_API_key = Dandelion Account center APIkey" \
https://www.pgyer.com/apiv1/app/upload
echo "* * * * * * * * * * * * * * * * * * * * * * * * * end of upload the dandelion * * * * * * * * * * * * * * * * * * * * * * * * * *"
# Remove log files
rm -rdf "${logfileDir}"
exit
Copy the code
Tip, FIR and dandelion need to install the environment first, specifically refer to the FIR official website official documents, dandelion official website documents
python
As a matter of fact, after Jenkins’ construction, the compile-related files of the corresponding project will be generated in jobs of the directory where Jenkins was installed, including SVN logs
We’ll read the file and save it to the directory we specify, and then upload it to a third-party hosting platform like Dandelion. This is written at the end of the shell script above
from xml.dom.minidom import parse
import xml.dom.minidom,sys,os
# Related Contents
numbulindline = open('/ Users/Shared/Jenkins/jobs/project name/nextBuildNumber'.'r').readline()
needNumbulindline = int(numbulindline)-1
xmlPath = '/ Users/Shared/Jenkins/jobs/project name)/builds / % d/changelog. The XML'%needNumbulindline
The name of the saved file
txtPath = 'ChangeLog'
# File write encoding
reload(sys)
sys.setdefaultencoding('utf8')
Write log to TXT
def text_write(text):
# Save path
logPath = "./%s"%txtPath
file = open(logPath,'a')
file.write(text)
file.close()
Get the XML node value method
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 Log writing ========="
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
ifmsgdom ! = None: msg = msgdom.dataelse:
msg = "Empty"
text_write(author+""+date+"\n"+msg+"\n\n")
text_write("= = = = = = = = = = = = = = = = = = = = = = = = = = = = =")
print "==========log writing completed ========="
print "==========log Log content ========="
f = open("./%s"%txtPath,'r')
lines = f.readlines()
for line in lines:
print line
f.close()
except Exception,e:
print "========== XML file is invalid ==========%s"%e
Copy the code
At the end, if you don’t want to use this method, you can also use a third-party tool such as Fastlane, which has a more mature packaging solution.