写在前面
相信很多做APP开发的朋友都经历过这种情况:开发完成进入测试阶段会有很多各种的ipa包需要build,如果公司有个好的体贴的QA的话,那可以把打包的工作交给他,如果没有就只能开发来做了!什么开发版的正式服、测试服ipa,什么企业版的正式服、测试服ipa,什么...... 如果用XCode自带Archive的话我们需要点点点,期间还得等等等,然后还得上传各种ftp,蒲公英,etc. 所以有个脚本来帮助一次性打包并上传是极好的。
XCode打包
首先
在开发者网站注册APPID并且生成称对应的证书,描述文件,并且将证书和描述文件下载下来,证书导入钥匙串,描述文件导入XCode。至于怎么生成证书和描述文件就不啰嗦啦。
然后
在xcodeproj
- ->General 配置好设置的APPID,选择对应的Team(如果不勾选xcode自动管理签名可不用填)
- ->BuildSettings->Signing 配置好对应的Debug、Release证书
- ->Scheme->Edit Scheme 选择对应的的Debug或者Release
- ->调整好其他一些配置属性,比如:版本号,一些自定义宏等
最后
- ->Product-Archive wait。。。。。。
- ->Archive完成一般会有选择是Upload App Store还是选择Export,通常来说还是Export个ipa到本地
- ->Export 选择是生成何种类型的ipa,有四个选项根据需要选择,如果需要多种选择可以直接重复Export,不用重新Archive
- ->选择ipa类型,选择对应的Team,Next,Next,wait。。。选择存储位置,Export
Shell脚本
像这样一步一步配置,一步一步点击,还得wait,还得rebuild,again,again。。。像这种频繁手动修改配置是很不安全滴,作为一个开发者,时间很宝贵的好伐,这种重复的劳动完全可以省略嘛。
是时候说到正题了
首先
分析一下脚本需要做的工作有哪些:
- 修改General配置
- 修改自定义的一些配置
- 修改BuildSetting配置
- 修改Scheme-Archive-Build Configuration
- 执行Archive
- 选择Export类型 设置存储位置
然后
针对各种工作做出应对
- 利用PlistBuddy
General配置和一些自定义配置都可以放置在Plist文件里面进行,这样看起来清晰明了减少出错。在修改这些配置的时候我们可以利用Mac自带的PlistBuddy。
针对不同的配置我们可以创建不同的配置文件,然后遍历这些文件,一个配置文件打次包就好了 - xcodebuild
xcodebuild命令是本脚本的主要部分,通过修改其参数可以对BuildSettings以及Scheme、Target等进行配置
干就完了
- 熟悉了解PlistBuddy用法
在终端中输入/usr/libexec/PlistBuddy --help
查看,比较简单,细心可看懂。 -
创建文件结构
其中build.sh是执行脚本,module文件夹很显然里面放置的是不同的配置啦,各个配置下的config.plist是要修改的配置,里面包含各个要修改的属性;exportPlist.plist是必须要有的配置文件,可以为空,但必须有;xxxx.mobileprovision是各个配置对应的描述文件,如果使用xcode里面配置的可以没有。
- 开始Coding
- 创建一些全局方便使用,比如PlistBuddy位置,xcodebuild位置等
plistBuddy="/usr/libexec/PlistBuddy" #PlistBuddy位置
xcodebuild="/usr/bin/xcodebuild" #xcodebuild位置
security="/usr/bin/security" #security 用来访问钥匙串
codesign="/usr/bin/codesign" #签名工具
shellPath=`pwd` #当前路径
projectPath="$shellPath/.." #工程路径
pbxprojFile='' #xcodeproj文件路径
projTypeIsWorkSpace=true #工程文件类型 默认.xcworkspace
buildTime=$(date +%Y-%m-%d-%H-%M) #时间
- 既然我们要对xcode项目进行打包,那么我们首先要得有.xcodeproj文件吧,工程文件都没有还打个毛线包啊!
if [[ "$xcodeProject" == '' ]]; then
xcodeProject=`find "$projectPath" -maxdepth 1 -type d -name "*.xcodeproj"`
fi
projectExtension=`basename "$xcodeProject" | cut -d'.' -f2`
if [[ "$projectExtension" != "xcodeproj" ]]; then
echo "Xcode project 应该带有.xcodeproj文件扩展,.${projectExtension}不是一个Xcode project扩展!"
exit 1
else
pbxprojFile="$xcodeProject/project.pbxproj"
if [[ ! -f "$pbxprojFile" ]]; then
echo "项目文件:\"$pbxprojFile\" 不存在"
exit 1;
fi
echo "发现pbxproj:$pbxprojFile"
fi
- 由于现在许多工程都是通过workSpace来进行管理的,所以需要先检查判断是否是workspace工程
# 这里通过在.xcodeproj文件所在文件夹下是否有以.xcworkspace后缀的文件来判断
xcworkspace=`find "$xcodeProject/.." -maxdepth 1 -type d -name "*.xcworkspace"`
if [[ -d "$xcworkspace" ]]; then
projTypeIsWorkSpace=true
echo "发现xcworkspace:$xcworkspace"
else
projTypeIsWorkSpace=false;
fi
- update到最新代码(手动更新可以删除此段)
cd ..
echo ${pwd}
svn update
svnVersion=`svnversion |sed 's/^.*://' |sed 's/[A-Z]*$//'`
echo $svnVersion
先cd到工程所在路径,由于我们是svn管理代码的所以执行svn update 可能需要输入svn密码,然后获取到svnVersion是用来标记代码版本。
- 是时候表演真正的技术了
此处是脚本的主要部分,主要有一下几个部分:
遍历module文件夹获取各个配置里面的config文件;
根据配置文件利用PlistBuddy修改配置;
clean工程;
Build工程;
export
for y in $shellPath/module/*;
do
#==============读取config文件====================#
configFile=$y/config.plist
if [[ "$configFile" == '' ]]; then
echo "未找到config.plist文件!"
exit 1
fi
echo "找到config.plist文件"
configurations=`"$plistBuddy" -c "print :" "$configFile"`
echo "$configurations"
appVersion=`"$plistBuddy" -c "print :appVersion:" "$configFile"`
上面是遍历module和读取config.plist以及其中各个key的值。
projectName=$(basename "$xcodeProject" .xcodeproj)
infoPlistPath=$projectPath/$projectName/Info.plist
echo "infoPlistPath = $infoPlistPath"
#修改Info.plist内容
if [[ -f "$infoPlistPath" ]]; then
if [[ "$appVersion" != '' ]]; then
$plistBuddy -c "Set :CFBundleShortVersionString $appVersion" "$infoPlistPath"
fi
fi
上面是根据config.plist来修改Info.plist的例子
# =========== keychain 授权 =====================#
#允许访问证书
$security unlock-keychain -p $loginPwd "$HOME/Library/Keychains/login.keychain" 2>/tmp/log.txt
if [[ $? -ne 0 ]]; then
echo "security unlock-keychain 失败!请检查配置密码是否正确"
exit 1
fi
$security unlock-keychain -p $loginPwd "$HOME/Library/Keychains/login.keychain-db" 2>/tmp/log.txt
if [[ $? -ne 0 ]]; then
echo "security unlock-keychain 失败!请检查配置密码是否正确"
exit 1
fi
# =================================================#
上面是用来获取钥匙串的授权的,其中loginPwd是在config里面配置读取的。不进行授权有可能会影响证书的访问,导致打包失败。
#==============检查证书和描述文件====================#
profileUUID=''
if [[ "$profileName" != "" ]]; then
profilePath=$y/$profileName.mobileprovision
if [[ ! -f "$profilePath" ]]; then
echo "描述文件 $profilePath 不存在!"
exit 1
fi
profileUUID=`$plistBuddy -c 'Print :UUID' /dev/stdin <<< $($security cms -D -i "$profilePath" 2>/tmp/log.txt)`
echo "描述文件UUID $profileUUID"
fi
# =================================================#
对于自定义证书和描述文件的如果查找不到就会构建失败。
# ===============检查构建ipa存放路径=================#
savePath=$projectPath/build/$buildTime
if [[ ! -d "$projectPath/build" ]]; then
mkdir $projectPath/build
fi
if [[ ! -d "$savePath" ]]; then
mkdir $savePath
fi
# =================================================#
马上就要出货了,出货之前一定要准备好放货的地方吧。我是根据时间生成一个文件夹,然后里面的ipa根据不同配置命名不同。
exportPlistPath=$y/exportPlist.plist
if [[ "$exportPlistPath" == '' ]]; then
echo "未找到exportPlist.plist文件!"
exit 1
fi
再检查下必须的exportPlist.plist文件在不在。
if [[ $projTypeIsWorkSpace != true ]]; then
echo '*** 正在 清理工程 ***'
xcodebuild clean -project "${xcodeProject}" -scheme "${schemeName}" -configuration "${buildConfiguration}" -sdk $sdk
echo '*** 清理完成 ***'
echo '*** 正在 archive ***'
cmd="xcodebuild archive -archivePath ${savePath}/${schemeName}-${buildConfiguration}.xcarchive -project "${xcodeProject}" -scheme "${schemeName}" -configuration "${buildConfiguration}" -sdk $sdk"
if [[ $profileUUID != '' ]];then
cmd="$cmd" PROVISIONING_PROFILE="${profileUUID}"
fi
if [[ $certificateName != '' ]];then
cmd="$cmd" PROVISIONING_PROFILE="${profileUUID}" CODE_SIGN_IDENTITY="${certificateName}"
fi
if [[ $teamID != '' ]];then
cmd="$cmd" DEVELOPMENT_TEAM="${teamID}"
fi
$cmd
echo '*** archive done***'
echo '*** 正在 exportArchive ***'
xcodebuild -exportArchive -archivePath ${savePath}/${schemeName}-${buildConfiguration}.xcarchive \
-configuration "${buildConfiguration}" \
-exportPath ${savePath} \
-exportOptionsPlist ${exportPlistPath} \
-quiet || exit
echo '*** exportArchive done***'
上面是利用xcodebuild对xcodeproj打包的主要部分,包括clean,修改Archive参数,exportArchive。
如果是workspace工程只需要修改project参数为workspace即可,如下例
xcodebuild clean -workspace "${xcworkspace}" -scheme "${schemeName}" -configuration "${buildConfiguration}" -sdk $sdk
关于更多xcodebuild的参数配置可以通过在终端中输入xcodebuild -help
查看。
if [[ $ftp != '' ]];then
echo "***开始上传ipa到ftp服务器***"
ftp -niv<<-!
open 192.168.255.22 8888
user xxxx xxxxx
binary
hash
cd ${ftp}
lcd ${savePath}
prompt
put ${schemeName}${buildConfiguration}.ipa
close
bye
!
echo "***完成上传ipa到ftp服务器***"
fi
最后一步上传ftp了,由于我们使用ftp提供测试下载安装。其他上传蒲公英或者App Store的可以自己添加。
要结束了
脚本比较长,格式有点乱,主要是给大家提供个思想,大家可以参考下写出自己的更完美更漂亮的。
如果有了更好的请一定要@我去学啊。[笑脸]
- 附:
package脚本下载地址