The introduction

Recently, I did the support of continuous construction platform packaging and migration for a project in the early stage of the company. Since I had not participated in similar work before, and I was basically not involved in the development of this project, I encountered many problems on the way. Besides, the project belongs to the React Native project and the code version is old. Therefore, a summary is made based on Jenkins’ shell packaging script and various logs and problems encountered in the automatic packaging process, hoping to provide a reference for those who need it and deepen their understanding. The packaging environment is as follows:

Node: V8.3.0 Xcode: Xcode8, iOS: post-ios8, macOS: macOS Sierra 10.12Copy the code

Ideas and related knowledge

First of all, it should be clear that script packaging and Xcode packaging do the same thing, so every action you pack in Xcode should correspond to each command in the script. In general, there are the following phases of iOS packaging

  1. Certificate configuration, project Target and Scheme configuration
  2. The actual packaging process includes cleaning, compiling, building, archiving, exporting and exporting IPA
  3. Scan ipA packages, upload or archive them to a designated location for users to download (these are personal operations and vary from one to another)

And commands in the script, of course, is to finish the configuration and operation one by one, it won’t go to illustrate how to automate play different types of bags, when know the specific principle of script packages and command, natural understand script packaging and manual packing is the same, so as to know how to optimize the automated script with a variety of different types of bags.

The xcodebuild command contains the following commands:

Xcodebuild clean = "Product -> clean Xcodebuild-configuration" Xcode: Product -> Build xcodebuild-xcworkspace // xcodeBuild-xcodeProj // Xcodeproj xcodeBuild archive // Is equivalent to xcodeProj xcodeBuild archive // is equivalent to xcodeBuild-exportArchive // Export ipACopy the code

Implementation and scripts

Partial arguments and function definitions

#! /bin/sh
### Parameter description
#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = certificate and version configuration information = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#Network framework environment: int; stg; prd
env=prd
#The original versionS_VERSION = "0.0.1"#New version numberT_VERSION = "0.0.1"#The original program displays the name
S_APP_DISPLAY_NAME="Example1"
#Modified shape display name
T_APP_DISPLAY_NAME="Example2"
#Device type
device="iPhone"
#UUID corresponding to the Provision Profile of the package description file
PROVISION_PROFILE_NAME="********-****-****-****-************"
#Package certificate name
CODESIGN_INDENTITY_NAME="iPhone Distribution: **************"
# APP ID
BUNDLE_IDENTIFIER="com.*******.***"
#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =function= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#Gets the package name suffixfunction getPackageFix(){ if [ "$env" == "prd" ]; then echo "prd"; elif [ "$env" == "int" ]; then echo "int"; elif [ "$env" == "stg" ]; then echo "stg"; fi }#Get the timestamp in the format yyyymmddHHMMSS
function getTime(){
    echo `date +%Y``date +%m``date +%d``date +%H``date +%M``date +%S`;
}
#Obtain the device type abbreviationfunction getDeviceType(){ if [ "$device" == "iPhone" ]; then echo "h" elif [ "$device" == "iPad" ]; then echo "a" fi }#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = path and compile parameter = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#The workspace
workspace=`pwd`
#Engineering path
projectName="*******"
projectTmpName="*******"
projectBase=*******
projectTmpBase=*******
#Ipa packet output path
outputName="*******"
output= *******
#Compile parametersThe configuration = "Release" targetStr = "* * * * * *" iphoneos = "iphoneos10.0 IPHONEOS_DEPLOYMENT_TARGET =" 8.0 ""#The name of the ipa
ipaMain="*******"
ipaName=${ipaMain}-$(getPackageFix)-$(getVersion)-$(getTime).ipa
ipaShortName=$ipaMain.ipa
appName=$targetStr.app
DSYMName=$targetStr.app.dSYMCopy the code

Environment initialization and project configuration operations

#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = initialization and backup project = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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."

#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = character to replace = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#1. Modify the Bundle identifier in plist,app display name, and set the version number
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. Modify project. Pbxprojfn="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."Copy the code

Compile, package, and archive export

#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = compilation and packaged = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
#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...."

#= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = file = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

#output the ipa file to special targetecho "move ipaFile to special target start......" cd $output timeDir=$(getTimeFormat) if [ ! -d "$timeDir" ]; then mkdir $timeDir fi
#Archive. App, IPA,dSYM files
cd $projectTmpBase/ios
cp -rf $ipaPathWithResigned $output/$timeDir/$ipaShortName
mv $ipaPathWithResigned $output/$timeDir
echo "move ipaFile to special target done."

#Delete the. SVN file
cd $projectTmpBase
find . -type d -name ".svn" |xargs rm -rvf

#Copy *. Ipa to the root directory
cp -rf $output/$timeDir/$ipaShortName $workspaceCopy the code

When modifying scripts and writing scripts, I read a lot of relevant materials on the Internet, and I can also find a lot of shell scripts about iOS packaging on the Internet. My advice here is not to use others’ scripts directly, because each environment is different, the situation is different, and the project is different. Scripts provided by others can not be directly successful in your environment, should be based on others’ scripts as a reference, so as to combine their own environment, actual operation to modify and write a script of their own to achieve their own automatic packaging script.

Problems and solutions may be encountered

As this packaging is the React Native iOS project, some of the problems are special problems of the project. Native projects usually do not occur, but they can be used as a reference.

The iOS certificate and related configurations are incorrect

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 **Copy the code
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 **Copy the code

Please first check whether the certificate is valid, whether the Settings of the certificate in the script are correct, and whether the Settings of Jenkins certificate are correct. In the related questions on the Internet, Pbxproj is not recommended to use this method. Each generation of Xcode updates may be slightly different for the project. Pbxproj project. A better approach would be to use xcodeBuild parameters to package the directly specified certificate configuration, as shown in the script above. Also make sure your project turns off certificate auto-management. As follows:

Or modify project. Pbxproj with string substitution on the command line

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

About third party frameworks and 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/Rel ease-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/WebSo cket/build/Release-iphoneos/libRCTWebSocket.a'Copy the code

There is a special problem with React Native. The official static library file related to React Native was not linked during compilation, so I checked relevant materials. Generally speaking, it would not occur normally. The world is big, and it is true that there are also people who have similar situations. Here are two methods, both of which are similar situations found by Google. I used the first method to solve them, and the second method is a reference for similar problems:

  1. Target -> Build Phases -> Target Dependencies

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

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

This is an occasional problem when compiling scripts. If you are sure that the library has been added to the Link Binary With Libraries, check whether the small icon at the front of the library is not normal:

React Native introduces other Libraries, NPM third-party library links, etc. You can delete static Libraries in the Link Binary With Libraries manually, and then add them again. The problem will solve itself.

Permission Control Issues

/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 deniedCopy the code

React Native binding scripts lose read/write permissions. You can write/write permissions in the script before compiling.

#Manually grant permissions to dependencies in node_modules
cd $workspace
chmod -R 777 tmp/node_modules/react-nativeCopy the code

conclusion

It took me nearly a week to complete the automatic packaging. In fact, it was not very difficult to understand things. The script packaging was nearly 80 times in the middle of the process, and finally solved all the problems. If the problems you encountered along the way helped you, then I’m glad they helped you. Many of the above commands are very interesting, like you can check the information, you will understand how to write scripts, and what commands are very interesting.

Study hard, make progress together, pay to be rewarded !!!!