记一次iOS自动化打包走过的坑-关于React Native-iOS项目

--引言

最近为公司前期做的一个项目做持续构建平台打包迁移支持,由于之前未参与类似工作,且我也基本未参与这个项目开发工作,所以途中磕磕碰碰遇到了很多很多的问题,而且由于项目属于React Native项目,而且代码版本较老,更是出现了很多无法预料的问题,因而结合本次Jenkins使用的shell打包脚本及自动化打包过程中遇到的各种日志和问题做一个总结,希望给有需要的人做一个参考,也给自己加深理解。本次打包环境如下:

node:V8.3.0
Xcode:xcode8,
iOS:iOS8以后环境,
macOS: macOS Sierra 10.12

思路和相关知识

首先应该明确脚本打包和用Xcode打包做的是同一件事情,因而你在xcode里面打包进行的各种操作其实都应该对应在脚本中的每一句命令,总体上对iOS打包有以下工作阶段

  1. 证书配置、项目Target及scheme相关配置
  2. 实际打包过程,包括清理、编译、build构建、Archive存档、Export导出ipa
  3. 扫描ipa包、上传或归档至指定位置供用户下载(这些属于个人操作,各有不同)

而脚本中的命令当然也是要一一完成这些配置和操作,这里不会去说明如何自动化打出各种不同类型的包,当懂得脚本打包的具体原理和命令后,自然懂得脚本打包和手动打包其实是一样的,从而懂得如何去优化脚本自动化打出各种不同类型的包。

关于xcodebuild命令的使用和说明应该去了解相关资料,其中打包常用命令如下:

xcodebuild clean // 等同于Xcode下点击Product -> Clean
xcodebuild -configuration //以指定配置编译并构建项目,等同于Xcode下点击Product -> Build
xcodebuild -xcworkspace // 等同于Xcode下点击运行xcworkspace
xcodebuild -xcodeproj // 等同于Xcode下点击运行xcodeproj
xcodebuild archive // 等同于Xcode下点击Product -> Archive
xcodebuild -exportArchive // 等同于Export导出ipa

实现和脚本

部分参数和函数定义

#!/bin/sh
###参数说明
#=======================================证书和版本配置信息 ======================================
# 网络框架环境: int; stg; prd
env=prd
# 原版本号
S_VERSION="0.0.1"
# 修改后的版本号
T_VERSION="0.0.1"
# 原程序显示名称
S_APP_DISPLAY_NAME="Example1"
# 修改后成型显示名称
T_APP_DISPLAY_NAME="Example2"
# 设备类型
device="iPhone"
# 打包描述文件Provision Profile的对应UUID
PROVISION_PROFILE_NAME="********-****-****-****-************"
# 打包证书名称
CODESIGN_INDENTITY_NAME="iPhone Distribution: **************"
# APP ID
BUNDLE_IDENTIFIER="com.*******.***"
#===================================== function =====================================
#获取包名后缀
function getPackageFix(){
    if [ "$env" == "prd" ];then
        echo "prd";
    elif [ "$env" == "int" ];then
        echo "int";
    elif [ "$env" == "stg" ];then
        echo "stg";
    fi
}
#获取时间戳,格式:yyyymmddHHMMSS
function getTime(){
    echo `date +%Y``date +%m``date +%d``date +%H``date +%M``date +%S`;
}
#获取设备类型简称
function getDeviceType(){
    if [ "$device" == "iPhone" ];then
        echo "h"
    elif [ "$device" == "iPad" ];then
        echo "a"
    fi
}
#======================================= 路径及编译参数===================================
#工作空间
workspace=`pwd`
#工程路径
projectName="*******"
projectTmpName="*******"
projectBase=*******
projectTmpBase=*******
#ipa包输出路径
outputName="*******"
output= *******
#编译参数
configuration="Release"
targetStr="******"
iphoneos="iphoneos10.0"
IPHONEOS_DEPLOYMENT_TARGET="8.0"
#ipa名称
ipaMain="*******"
ipaName=${ipaMain}-$(getPackageFix)-$(getVersion)-$(getTime).ipa
ipaShortName=$ipaMain.ipa
appName=$targetStr.app
DSYMName=$targetStr.app.dSYM

环境初始化与项目配置操作

#===================================== 初始化和备份工程 ======================================
cd $workspace
mkdir -pv $output
cd $output
rm -rf $ipaShortName

cd $workspace
echo "remove project bak start..."
rm -rf $projectTmpName
echo "remove project bak end."

echo "make project bak start......"
cp -rf $projectName $projectTmpName
echo "make project bak end."

#====================================== 字符替换 ===============================
#1.修改plist中Bundle identifier,app显示名,设置版本号
fn="info.plist"
cd $projectTmpBase/ios/Example
sed "s/<string>com.*<\/string>/<string>${BUNDLE_IDENTIFIER}<\/string>/g" $fn>1.plist
sed "s/<string>${S_APP_DISPLAY_NAME}<\/string>/<string>${T_APP_DISPLAY_NAME}<\/string>/g" 1.plist>2.plist
sed "s/<string>${S_VERSION}<\/string>/<string>${T_VERSION}<\/string>/g" 2.plist>3.plist

cat 3.plist>$fn
rm -rf 3.plist
rm -rf 2.plist
rm -rf 1.plist

echo "print ID: ${BUNDLE_IDENTIFIER} "
echo "print $fn start..."
cat $fn
echo "print $fn end."

#2、修改project.pbxproj
fn="project.pbxproj"
cd $projectTmpBase/ios/*****.xcodeproj
sed "s/PRODUCT_BUNDLE_IDENTIFIER = .*;/PRODUCT_BUNDLE_IDENTIFIER = ${BUNDLE_IDENTIFIER};/g" $fn>1.pbxproj
sed "s/DevelopmentTeam = .*//g" 1.pbxproj>2.pbxproj
sed "s/DEVELOPMENT_TEAM = .*/DEVELOPMENT_TEAM = \"\";/g" 2.pbxproj>3.pbxproj

cat 3.pbxproj>$fn
rm -rf 3.pbxproj
rm -rf 2.pbxproj
rm -rf 1.pbxproj

echo "print $fn start..."
cat $fn
echo "print $fn end."

编译、打包与归档导出

#====================================== 编译与打包 ==========================================
#clean and compile
appPath="$projectTmpBase/ios/build/$configuration-iphoneos/$appName"
ipaPathWithResigned="$projectTmpBase/ios/${ipaName}"
archivPath="$projectTmpBase/ios/build/$configuration-iphoneos/*****.xcarchive"
cd $projectTmpBase/ios

echo "clean start..."
 xcodebuild clean -configuration $configuration -target $targetStr
echo "clean end."

echo "compile start......"
xcodebuild -configuration $configuration -sdk $iphoneos -target $targetStr IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET CODE_SIGN_IDENTITY="$CODESIGN_INDENTITY_NAME" PROVISIONING_PROFILE=$PROVISION_PROFILE_NAME
echo "compile end."

echo "xcodebuild archive start...."
xcodebuild archive -project "${ipaMain}.xcodeproj" -scheme $ipaMain -configuration $configuration -archivePath "${archivPath}" CODE_SIGN_IDENTITY="$CODESIGN_INDENTITY_NAME" PROVISIONING_PROFILE=$PROVISION_PROFILE_NAME
xcodebuild -exportArchive -archivePath "${archivPath}" -exportPath "${ipaPathWithResigned}" -exportFormat IPA -exportProvisioningProfile "*****"
echo "xcodebuild archive end...."

#====================================== 归档 ===============================================

#output the ipa file to special target
echo "move ipaFile to special target start......"

cd $output
timeDir=$(getTimeFormat)
if [ ! -d "$timeDir" ];then
mkdir $timeDir
fi

#归档.app,ipa,dSYM文件
cd $projectTmpBase/ios
cp -rf $ipaPathWithResigned $output/$timeDir/$ipaShortName
mv $ipaPathWithResigned $output/$timeDir
echo "move ipaFile to special target done."

#删除.svn文件
cd $projectTmpBase
find . -type d -name ".svn" |xargs rm -rvf

#拷贝*.ipa到根目录
cp -rf $output/$timeDir/$ipaShortName $workspace

修改脚本和编写脚本时,在网上看了很多相关资料,同样的在网上也能找到很多关于iOS打包的shell脚本,在这里我的建议是,不要把他人的脚本直接拿去使用,因为各个环境不同,情况不同,项目不同等原因,他人提供的脚本不可能直接能在你的环境中也能跑批成功,应该以他人脚本为参考,从而结合自己的环境、实际操作修改和写出一份自己的脚本来实现自己的自动化打包脚本。

可能遇到问题与解决方案

由于本次打包是React Native的iOS项目,因而其中有一些问题是项目特殊问题,通常原生项目并不会出现,但是可以当作一种参考。

iOS证书及相关配置错误

Check dependencies
****** requires a provisioning profile. Select a provisioning profile for the "Release" build configuration in the project editor.
Code signing is required for product type 'Application' in SDK 'iOS 10.0'

** BUILD FAILED **
Check dependencies
No certificate matching 'iPhone Distribution: *****************' for team '*******':  Select a different signing certificate for CODE_SIGN_IDENTITY, a team that matches your selected certificate, or switch to automatic provisioning.
Provisioning profile "******" belongs to team "**************", which does not match the selected team "*********".
Code signing is required for product type 'Application' in SDK 'iOS 10.0'

** BUILD FAILED **

这种问题都是出现在执行xcodebuild命令的时候,证书配置问题错误,请首先检查证书是否有效,脚本中关于证书的设置项是否正确,jenkins的证书相关设置是否正确,在网上的相关问题中,有在脚本中进行对project.pbxproj项目文件进行字符串替换和修改等操作来实现证书配置等操作,这里建议不要使用这种方法,每一代的xcode升级对project.pbxproj项目也许会有些许不同。更好的方式应该使用xcodebuild的相关参数来对进行直接指定证书配置打包,如上面的脚本。同时确保你的项目关闭证书自动管理。如下:

或命令行中以字符串替换方式修改project.pbxproj时直接进行设置

sed "s/ProvisioningStyle = .*/ProvisioningStyle = Manual;/g" 0.pbxproj>1.pbxproj

关于第三方框架和Build Phases问题

clang: error: no such file or directory: '/Users/admin/jenkins/new_slave8/workspace/CI_PIPELINE_1302_8285_ios_build/tmp/node_modules/react-native/React/build/Release-iphoneos/libReact.a'
......
clang: error: no such file or directory: '/Users/admin/jenkins/new_slave8/workspace/CI_PIPELINE_1302_8285_ios_build/tmp/node_modules/react-native/Libraries/WebSocket/build/Release-iphoneos/libRCTWebSocket.a'

React Native的特殊问题,在编译时没有链接上官方的React Native相关静态库文件,查阅了相关资料,一般来说,正常也不会出现的,也许是我工作的jenkins平台环境不一样,居然有与众不同的问题出现,但是总归出问题了,世界很大,还真的是也有人也有类似情况,这里介绍两种方法,都是谷歌到的类似情况,我是使用第一种方法解决了,第二种方法是类似问题的参考:

  1. Target -> Build Phases -> Target Dependencies
  1. Target -> Build Setting -> Search Paths -> Library Search Paths

![](https://github.com/cuiyueshuai/MD-Image-Repository/raw/master/xcode-build setting.png)

ld: library not found for -lRNDeviceInfo
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是脚本打包编译时才会偶尔出现的问题,如你已经确定这个库确定已在Link Binary With Libraries中添加,请查看是否出现该库的前面小图标不是很正常,如下:

这也许是一种缓存问题,也许是React Native引入其他库,npm第三方库链接等等多种原因引起的,你只需要在Link Binary With Libraries中手动删除了静态库,然后重新添加,库的标志和链接等都会和官方库一样正常,问题自然解决。

权限控制问题

   /bin/sh -c /Users/admin/jenkins/new_slave8/workspace/CI_PIPELINE_1302_8285_ios_build/tmp/ios/build/*****.build/Release-iphoneos/******.build/Script-00DD1BFF1BD5951E006B06BC.sh
/Users/admin/jenkins/new_slave8/workspace/CI_PIPELINE_1302_8285_ios_build/tmp/ios/build/******.build/Release-iphoneos/*****.build/Script-00DD1BFF1BD5951E006B06BC.sh: line 3: ../node_modules/react-native/packager/react-native-xcode.sh: Permission denied

这又是打包环境引起的问题,对React Native下的绑定脚本执行失去了读写权限,直接在脚本中在开始编译前,对路径下进行读写赋权就好了。

#手动赋予权限给node_modules下的相关依赖
cd $workspace
chmod -R 777 tmp/node_modules/react-native

总结

花了接近一个星期的时间,我完成了本次自动化打包的工作,其实东西搞懂了也不是很难,中途脚本打包快接近80次了,终于解决了所有问题,感觉还是值得的。途中遇到的那些问题,如果对你也有帮助,那很高兴可以帮助到你。上面的命令很多还是很有趣的,喜欢的可以自己查一下资料,就会懂得脚本如何写,还有什么命令是很有趣的。

努力学习,共同进步,付出才有回报!!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容