前一篇:Jenkins十:Mac通过 Tomcat里安装Jenkins配置Master-Slave打包iOS并上传到svn服务器
后一篇:Jenkins十二:Jenkins忘记账号密码处理
前言
随着项目的发展,一般的项目都遇到一份代码可能会要做出多个APP的情况,
典型的就是电商的普通用户版
和商家版
,一些换肤游戏的多个马甲APP。这两个版本大部分UI及逻辑都是一样的,只会在小部分页面主题颜色及业务有些许差别一、单target,代码里写死
在代码里写死,用多个If else,switch来进行判断,使用不同的主题及图片,以及区分不同版本的业务(这种版本方便简洁,谁都会,但是不方便扩展,以及容易出现bug,处理起来也很low)二、多Target
,这种情况就适合用多Target-Scheme
的方式管理,图片等资源每个target自个一分,相同的代码一起引入,不同部分添加到不同的平台(这个各个不同的平台的图片及资源都独立隔离,不同的平台又可以共用一些相同的代码及资源,做到了既公用又独立,坏处是如果平台或马甲包超过3个甚至更多时及其难管理,没次新创建的文件都有可能这个target引入了那个target又没引入,而且如果一直在持续不断的增加新的马甲包平台时,创建target也是一件痛苦的事)三、单Target,不同的平台包在打包时动态配置
一个target,在业务开发时不同平台一些差异化的地方都抽取出来做成plist进行配置化,在打包时手动或自动的修改bundleId
、发布证书、描述文件及其他配置资源和文件(这种方式集合了方式一、方式二的优点,就是不同的平台进行了有效隔离,适合随时增加新的平台,但也集合了方案一、方案二的缺点,同时如果要每次手动在xcode修改配置时是是否痛苦的,必须要和Jenkins配合自动打包才能达到最佳功效)
- 使用xcodebuild命令进行自动化打包
- project.pbxproj文件解析及脚本修改证书配置思路实现
- 懒人福利:用脚本来修改Xcode工程
- Let's Talk About project.pbxproj
将pbxpro转换成xml命令
plutil -convert xml1 -r -o test.xml project.pbxproj
plutil -convert json -r -o test.json Lottery.xcodeproj/project.pbxproj
通过脚本读取p12配置并修改project配置进行自动化修改打包
1、先从svn或网络或其他地方下载描述文件xxxx.mobileprovision
svn_name="xxx"
svn_pwd="xxx"
certificate_folder="./certificate"
svn export "https://xxx.xx.xx.xx/svn/xxx/xxx.mobileprovision" \
"./xxx.mobileprovision" \
--username ${svn_name} \
--password ${svn_pwd} \
--force
2、解析描述文件
#设置跨shell环境及设置好账号密码
tempVarFile="./tempVarFile.txt"
#2.2、读取描述文件
security cms -D -i "./xxx.mobileprovision" > "./profile.plist"
#2.3 读取信息
#证书持有者机构
config_TeamName=$(/usr/libexec/PlistBuddy -c "print TeamName" "./profile.plist")
echo "config_TeamName='${config_TeamName}'" >> $tempVarFile
#证书持有者组Id
config_teamId=$(/usr/libexec/PlistBuddy -c "print :Entitlements:com.apple.developer.team-identifier" "./profile.plist")
echo "config_teamId='${config_teamId}'" >> $tempVarFile
#bundleId
config_identifier=$(/usr/libexec/PlistBuddy -c "print :Entitlements:application-identifier" "./profile.plist")
config_identifier_cut="${config_teamId}."
config_identifier=${config_identifier#*$config_identifier_cut}
echo "config_identifier='${config_identifier}'" >> $tempVarFile
#发布证书名称
config_disName=$(/usr/libexec/PlistBuddy -c "print Name" "./profile.plist")
echo "config_disName='${config_disName}'" >> $tempVarFile
#描述文件UUID
config_uuid=$(/usr/libexec/PlistBuddy -c "print UUID" "./profile.plist")
echo "config_uuid='${config_uuid}'" >> $tempVarFile
#过期时间
config_expirationDate=$(/usr/libexec/PlistBuddy -c "print ExpirationDate" "./profile.plist")
echo "config_expirationDate='${config_expirationDate}'" >> $tempVarFile
cat $tempVarFile
source $tempVarFile
3、修改project配置文件
APP_NAME="xxxx"
cp "./${APP_NAME}.xcodeproj/project.pbxproj" backup.project.pbxproj
# 转换成plist文件
plutil -convert xml1 backup.project.pbxproj
#重命名成plist
cp backup.project.pbxproj backup.project.plist
pbx_rootObject=$(/usr/libexec/PlistBuddy -c "print :rootObject" "./backup.project.plist")
pbx_target=$(/usr/libexec/PlistBuddy -c "print :objects:$pbx_rootObject:targets:0" "./backup.project.plist")
target_buildConfigurationList=$(/usr/libexec/PlistBuddy -c "print :objects:$pbx_target:buildConfigurationList" "./backup.project.plist")
target_debug=$(/usr/libexec/PlistBuddy -c "print :objects:$target_buildConfigurationList:buildConfigurations:0" "./backup.project.plist")
target_release=$(/usr/libexec/PlistBuddy -c "print :objects:$target_buildConfigurationList:buildConfigurations:1" "./backup.project.plist")
#2.3.1、生成change.json文件
cat > "./config_change.json" << END_TEXT
{
"insert" : {
},
"remove" : {
},
"modify" : {
"objects.$target_debug.buildSettings.PRODUCT_BUNDLE_IDENTIFIER" : "${config_identifier}",
"objects.$target_release.buildSettings.PRODUCT_BUNDLE_IDENTIFIER" : "${config_identifier}",
"objects.$target_debug.buildSettings.PROVISIONING_PROFILE_SPECIFIER":"${config_disName}",
"objects.$target_release.buildSettings.PROVISIONING_PROFILE_SPECIFIER":"${config_disName}",
"objects.$target_release.buildSettings.DEVELOPMENT_TEAM":"${config_teamId}",
"objects.$target_debug.buildSettings.DEVELOPMENT_TEAM":"${config_teamId}",
"objects.$pbx_rootObject.attributes.TargetAttributes.$pbx_target.DevelopmentTeam":"${config_teamId}",
"objects.$pbx_rootObject.attributes.TargetAttributes.$pbx_target.ProvisioningStyle":"Manual",
"objects.$target_debug.buildSettings.CODE_SIGN_STYLE":"Manual",
"objects.$target_release.buildSettings.CODE_SIGN_STYLE":"Manual",
"objects.$target_release.buildSettings.CODE_SIGN_IDENTITY":"iPhone Distribution",
"objects.$target_debug.buildSettings.CODE_SIGN_IDENTITY":"iPhone Distribution",
}
}
END_TEXT
cat "./config_change.json"
#判断是否存在pbxproj
if [ ! -x "/usr/local/bin/pbxproj" ];then
echo "---------不存在pbxproj----------"
#下载命令行工具pbxproj
curl -o pbxproj "https://github.com/yulingtianxia/pbxprojHelper/releases/download/1.2.1/pbxproj"
#移动到bin下
mv "./pbxproj" "/usr/local/bin/pbxproj"
#设置权限
chmod 755 "/usr/local/bin/pbxproj"
else
echo "---------已经存在pbxproj---------"
fi
#2.3.2、修改配置
/usr/local/bin/pbxproj -apply "./config_change.json" "./${APP_NAME}.xcodeproj"
#2.4、修改展示名称
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${config_displayName}" "./Resource/platforms/OneNumberDev-Info.plist"
#2.5、修改版本号等信息
#2.6、生成由xxx.xcarchive导出xxx.ipa的ExportOptions.plist
cat > "./ExportOptions.plist" << END_TEXT
compileBitcode
destination
export
method
enterprise
provisioningProfiles
${config_identifier}
${config_disName}
signingCertificate
iPhone Distribution
signingStyle
manual
stripSwiftSymbols
teamID
${config_teamId}
thinning
<none>
END_TEXT
echo "===========修改好配置文件了===================="
4、检测和安装证书
#电脑账号
Node_User="xxx"
#登录密码
Node_Pwd="xxx"
#查找安装过的证书
security find-identity -v \
codesigning "/Users/$Node_User/Library/Keychains/login.keychain" \
> "./loginKeychain.txt"
#解锁钥匙串
security unlock-keychain -p ${Node_Pwd} "/Users/${Node_User}/Library/Keychains/login.keychain"
# 检测p12是否存在
function existP12()
{
find_txt=$*
replace_txt="$(date +%Y%m%d-%H%M%S)"
findPath="./loginKeychain.txt"
sed -i -e "s|$find_txt|'$replace_txt'|" $findPath
if grep -rn -s -q $replace_txt $findPath; then
return 1
else
return 0
fi
}
#检测是否已安装了发布证书
dis_exist_name="iPhone Distribution: $config_TeamName"
existP12 $dis_exist_name
if $? == 0 ; then
echo "=====没有发布证书======"
dis_file_path="${certificate_folder}/${config_disp12_name}"
security import $dis_file_path \
-k "/Users/${Node_User}/Library/Keychains/login.keychain" \
-P "$config_disp12_pwd" \
-T "/usr/bin/codesign"
else
echo "=====已经存在发布证书====="
fi
#检测是否已安装了推送证书
#aps_exist_name="Apple Development IOS Push Services: $config_identifier"
#existP12 $aps_exist_name
#if $? == 0 ; then
# echo "=====没有发布推送证书====="
# aps_file_path="${certificate_folder}/${config_apsp12_name}"
# security import $aps_file_path \
# -k "/Users/${Node_User}/Library/Keychains/login.keychain" \
# -P "$config_apsp12_pwd" \
# -T "/usr/bin/codesign"
#else
# echo "=====已经存在发布推送证书====="
#fi
#判断有没有描述证书
mobileprovision_path="/Users/$Node_User/Library/MobileDevice/Provisioning Profiles/$config_uuid.mobileprovision"
cp ${certificate_folder}/${config_profileFile} "./$config_uuid.mobileprovision"
mv -f "./$config_uuid.mobileprovision" "${mobileprovision_path}"
正式打包
#!/bin/bash
#临时变量
source "./tempVarFile.txt"
# 平台及SVN文件夹名称
APP_NAME="xxx"
SCHEMECA="xxx"
#打包日期(年月日)
DATEOFDAY="$(date +%Y%m%d)"
#打包的时间(年月日-时分秒)
DATE="$(date +%Y%m%d-%H%M%S)"
source "./tempVarFile.txt"
#设置好svn环境,防止中文乱七八糟的问题
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
#*************************************
#下面2行是集成有Cocopods的用法
security unlock-keychain -p ${Node_Pwd} /Users/${Node_User}/Library/Keychains/login.keychain
echo "=================clean================="
echo xcodebuild -workspace "./${APP_NAME}.xcworkspace" -scheme ${SCHEMECA} -configuration 'Release' clean
xcodebuild -workspace "./${APP_NAME}.xcworkspace" -scheme ${SCHEMECA} -configuration 'Release' clean
echo "+++++++++++++++++archive+++++++++++++++++"
#生成的archive的位置
ArchivePath="./${SCHEMECA}_${DATE}.xcarchive"
xcodebuild archive \
-workspace "./${APP_NAME}.xcworkspace" \
-scheme ${SCHEMECA} \
-configuration Release \
-archivePath ${ArchivePath}
echo "+++++++++++++++++export ipa+++++++++++++++++"
APPPath="${ArchivePath}/Products/Applications/${SCHEMECA}.app"
IPAFolderPath="./${SCHEMECA}_${DATE}"
IPAPath="${WORKSPACE}/${SCHEMECA}_${DATE}.ipa"
#在上一个shell里生成的文件
myOptionsPlist="./ExportOptions.plist"
#生成xxx.ipa
echo xcodebuild -exportArchive \
-archivePath "${ArchivePath}" \
-exportPath "${IPAFolderPath}" \
-exportOptionsPlist ${myOptionsPlist}
xcodebuild -exportArchive \
-archivePath "${ArchivePath}" \
-exportPath "${IPAFolderPath}" \
-exportOptionsPlist ${myOptionsPlist}
##读取打包后的内容的Info
InfoPath="${APPPath}/Info.plist"
#取build值
bundleVersion=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" "${InfoPath}")
#取APP名称
#打包代码的分支
tagOrBranchName=${GIT_BRANCH_TAG/origin\//""}
#生成的唯一名称
UNIQUE_NAME="${APP_NAME}_${SCHEMECA}_V${bundleVersion}_${tagOrBranchName}_${DATE}"
IPANAME="${UNIQUE_NAME}.ipa"
ParentSVNPath="./svn_${DATE}"
local_svn_Path="${ParentSVNPath}/${svn_ipa_folder}/${DATEOFDAY}"
#如果没有文件夹,则创建文件夹
if [ ! -d ${local_svn_Path} ];then
mkdir -p ${local_svn_Path}
fi
echo cp "${IPAFolderPath}/${SCHEMECA}.ipa" "${local_svn_Path}/${IPANAME}"
cp "${IPAFolderPath}/${SCHEMECA}.ipa" "${local_svn_Path}/${IPANAME}"
#生成dSYM
dsymutil ${APPPath}/${SCHEMECA} -o "${local_svn_Path}/${UNIQUE_NAME}.app.dSYM"
echo "+++++++++++++++++将ipa上传到svn+++++++++++++++++"
svn import "${local_svn_Path}/${IPANAME}" \
"https://xxx/xxx/${svn_ipa_folder}/${DATEOFDAY}/${IPANAME}" \
--username ${svn_name} \
--password ${svn_pwd} \
-m "测试添加文件"
echo "+++++++++++++++++将dsYM上传到svn+++++++++++++++++"
svn import "${local_svn_Path}/${UNIQUE_NAME}.app.dSYM" \
"https://xxx/xxx/${svn_ipa_folder}/${DATEOFDAY}/${UNIQUE_NAME}.app.dSYM" \
--username ${svn_name} \
--password ${svn_pwd} \
-m "测试添加文件"
if [ ! -f "${local_svn_Path}/${IPANAME}" ];then
echo ">>>>>不存在ipa>>>>>>>"
cd ..
rm -rf "./$JOB_NAME"
exit 1
fi
if [ ! -d "${local_svn_Path}/${UNIQUE_NAME}.app.dSYM" ]; then
echo ">>>>>不存在dsYM>>>>>>>"
cd ..
rm -rf "./$JOB_NAME"
exit 1
fi
cd ..
echo $JOB_NAME
rm -rf "./$JOB_NAME"