Jenkins十一:一份代码多个APP的多Target和单Target比较

前一篇: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"

你可能感兴趣的:(Jenkins十一:一份代码多个APP的多Target和单Target比较)