脚本打包与上传

打包流程:

  1. 编译.xcarchive包;
  2. 生成.ipa文件;
  3. 上传到苹果商店或第三方平台。

自动化:

这里所说的自动化,主要是通过shell脚本,利用 Command Line Tools 提供的 xcodebuild 命令来编译和导出 ipa,再通过 Application Loader 提供的 altool 命令行工具,或者其他三方平台提供的命令行工具上传 ipa 和 dSYM 文件。

清理构建目录

编译前先clean,相当于在 Xcode 进行 Product -> clean。

1
2
xcodebuild \
clean -configuration ${development_mode}

-configuration 用来表示打包的方式,相当于打包前配置 Edit scheme -> info -> Build configuration,告诉编译器打出来的包是何种包,比如 Release、InHouse等。

编译xcarchive包

1
2
3
4
5
xcodebuild archive \
-workspace ${project_path}/${project_name}.xcworkspace \
-scheme ${scheme_name} \
-configuration ${development_mode} \
-archivePath ${build_path}/${project_name}.xcarchive

-workspace表示工作空间,一个项目中有多个project时,比如通过 pod 集成了三方库后都会自动创建一个新的工作空间。如果没有,可以不加这个参数。-archivePath,用来配置生成的.xcarchive的路径。

导出ipa文件

1
2
3
4
xcodebuild -exportArchive -archivePath ${build_path}/${project_name}.xcarchive \
-configuration ${development_mode} \
-exportPath ${exportIpaPath} \
-exportOptionsPlist ${exportOptionsPlistPath}

-exportPath表示 ipa 文件的输出路径;

-exportOptionsPlist表示 ExportOptions.plist 配置文件的路径。此 plist 是打包时必须的文件,在手动打包并 export 出 ipa 包时,可以在 ipa 包的同级目录下找到,可以拿过来用,或者通过 Xcode 手动创建一份。如果有多个渠道包,也可以通过脚本生成对应的 plist。

#示例:ExportOptions.plist

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>teamID</key>
<string>略略略</string>
<key>method</key>
<string>enterprise</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>

注意:Xcode9 之后,ExportOptions.plist 需要指定的信息有变,具体可以参考 这里~

#完整示例

下面是完整的 shell 脚本,实现了编译.xcarchive、导出 ipa 与 dSYM,命名为autobuild.sh,放在工程根目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/bin/sh

#works for Xcode 7.0+ only

echo ">>>>>>>>>>>> Start at $(date) <<<<<<<<<<<<<<<"

PROJECT_ROOT_PATH=$PWD
AUTOBUILD_PATH="$PROJECT_ROOT_PATH/AutoBuild"

CURRENT_DATE=$(date +%Y_%m%d_%H%M)
WORKSPACE_NAME="Demo.xcworkspace"

#导出配置
HEADER_PLIST="$AUTOBUILD_PATH/Header.plist"
EXPORT_PLIST="$AUTOBUILD_PATH/ExportOptions.plist"

#Info key
KEY_SCHEME="KEY_SCHEME"
KEY_APP_NAME="KEY_APP_NAME"
KEY_TEAM_ID="KEY_TEAM_ID"
KEY_DEV_ID="KEY_DEV_ID"

#Flavors
DemoInHouse={$KEY_SCHEME:"DemoInHouse",$KEY_APP_NAME:"InHouse",$KEY_TEAM_ID:"xxxxxxxxxx",$KEY_DEV_ID:"xxx"}
DemoRelease={$KEY_SCHEME:"DemoRelease",$KEY_APP_NAME:"Release",$KEY_TEAM_ID:"xxxxxxxxxx",$KEY_DEV_ID:"xxx"}

FLAVORS=($DemoInHouse)

#Parse method
parse_json(){
echo $1 | sed 's/.*'$2':\([^,}]*\).*/\1/'
}

#build release apps directory
BUILD_DIR="$PROJECT_ROOT_PATH/Build"
RELEASE_DIR="$BUILD_DIR/Products"

rm -rdf $BUILD_DIR

#final binarys' directory
OUT_BINARY_DIR="$PROJECT_ROOT_PATH/binarys_$CURRENT_DATE"

if [ ! -d $OUT_BINARY_DIR ]; then
mkdir $OUT_BINARY_DIR
fi

for ((i = 0; i < ${#FLAVORS[@]}; i++))
do
#parse elements
TARGET_SCHEMES=$(parse_json ${FLAVORS[$i]} $KEY_SCHEME)
TARGET_APP_NAMES=$(parse_json ${FLAVORS[$i]} $KEY_APP_NAME)
TARGET_TEAM_IDS=$(parse_json ${FLAVORS[$i]} $KEY_TEAM_ID)
TARGET_DEV_IDS=$(parse_json ${FLAVORS[$i]} $KEY_DEV_ID)

#reset export options plist
rm $EXPORT_PLIST

cat $HEADER_PLIST >> $EXPORT_PLIST

echo "<plist version=\"1.0\">" >> $EXPORT_PLIST
echo "<dict>" >> $EXPORT_PLIST
echo "<key>teamID</key>" >> $EXPORT_PLIST
echo "<string>$TARGET_TEAM_IDS</string>" >> $EXPORT_PLIST
echo "<key>method</key>" >> $EXPORT_PLIST

echo ">>>>>>>>>>>>>>>>>>>>>>>> target scheme $TARGET_SCHEMES"

if [ $TARGET_SCHEMES = "DemoInHouse" ]; then
echo "<string>enterprise</string>" >> $EXPORT_PLIST
echo ">>>>>>>>>>>>>>>>>>>>>>>> enterprise Done"
else
echo "<string>app-store</string>" >> $EXPORT_PLIST
echo ">>>>>>>>>>>>>>>>>>>>>>>> app-store Done"
fi

echo "<key>uploadSymbols</key>" >> $EXPORT_PLIST
echo "<true/>" >> $EXPORT_PLIST
echo "</dict>" >> $EXPORT_PLIST
echo "</plist>" >> $EXPORT_PLIST

echo ">>>>>>>>>>>>>>>>>>>>>>>> Reset export options Done"

#替换素材
cd $AUTOBUILD_PATH
./replace_res.sh $TARGET_SCHEMES

# return root
cd $PROJECT_ROOT_PATH
echo ">>>>>>>>>>>>>>>>>>>>>>>> Reset res Done"

#export files
ARCHIVE_FILE="$OUT_BINARY_DIR/$TARGET_APP_NAMES.xcarchive"
DSYMS_FILE="$TARGET_APP_NAMES.dSYMs.zip"

#clean prject
xcodebuild -workspace $WORKSPACE_NAME -scheme $TARGET_SCHEMES -configuration $TARGET_APP_NAMES clean

#build & archive
xcodebuild archive -workspace $WORKSPACE_NAME -scheme $TARGET_SCHEMES -configuration $TARGET_APP_NAMES -archivePath $ARCHIVE_FILE

cd $ARCHIVE_FILE
zip -r $DSYMS_FILE "dSYMs"
mv $DSYMS_FILE $OUT_BINARY_DIR

# return root
cd $PROJECT_ROOT_PATH

#export
xcodebuild -exportArchive -archivePath $ARCHIVE_FILE -exportPath $OUT_BINARY_DIR -exportOptionsPlist $EXPORT_PLIST

#rename ipa file failed since creating ipa file is asynchronized
echo "apple id paired" >> "$OUT_BINARY_DIR/$TARGET_APP_NAMES_$TARGET_DEV_IDS"

echo ">>>>>>>>>>>>>>>>>>>>>>>> $TARGET_SCHEMES single loop Done"

done

echo ">>>>>>>>>>>> Finish at $(date) <<<<<<<<<<<<<<<"

使用时先根据需要打包的版本,修改FLAVORS=($DemoInHouse)中的内容。如果只打一个包,更换$后的命名即可;如果需要打多个包,则在输入多个包名,以逗号隔开,如:FLAVORS=($DemoInHouse,$DemoRelease)。最后,在终端中./autobuild.sh,直接执行脚本即可。

验证并上传到商店

Xcode 集成了 Application Loader,你可以用它来手动上传 App 的二进制文件。这里继续使用脚本的方式,通过 Application Loader 的命令行工具 altool 来验证二进制文件并上传到商店。

1
2
3
4
5
6
//altool工具路径
altoolPath="/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
//验证
"$altoolPath" --validate-app -f ${exportIpaPath}/${scheme_name}.ipa -u appleID -p password -t ios --output-format xml
//上传
"$altoolPath" --upload-app -f ${exportIpaPath}/${scheme_name}.ipa -u appleID -p password -t ios --output-format xml
参数 详细说明
–validate-app 您要验证指定的 App。
–upload-app 您要上传指定的 App。
-f file 正在验证或上传的 App 的路径和文件名。
-u username 您的用户名。
-p password 您的用户密码。
–output-format [xml or normal] 您想让 Application Loader 以结构化的 XML 格式还是非结构化的文本格式返回输出信息。默认情况下,Application Loader 以文本格式返回输出信息。

上传到Fir

1
2
firApiToken="xxxxxxxxxxx"
fir publish $exportIpaPath/$scheme_name.ipa -T "$firApiToken"

注意:使用脚本上传到Fir平台前,需要先安装 fir-cli:

1
gem install fir-cli

上传到蒲公英

1
curl -F "file=@$exportIpaPath/$scheme_name.ipa" -F "uKey=xxx" -F "_api_key=xxx https://qiniu-storage.pgyer.com/apiv1/app/upload

请根据开发者自己的账号,将其中的 uKey 和 _api_key 的值替换为相应的值。完整的使用规则请看 这里~

上传符号表

如果应用接入了崩溃分析工具,会要求将dSYM文件上传到对应平台的后台中,比如接入bugly时,可以通过以下脚本上传符号表文件:

1
java -jar buglySymboliOS.jar -d -i $dSYM -u -id "xx" -key "xx" -package "com.xx.xx" -version "$version" -o "xx.zip"

bugly 上传 dSYM 文件需要事先下载一个工具包,具体的配置和使用方法可以参考 这里~

其他工具

当然,还有一些更专业化的自动化部署和发布工具,如:

  • Jenkins
  • FastLane

这些工具极大地简化了我们手动发布时的一些枯燥、重复的工作,比如截图、代码签名以及发布。这里就不一一介绍了,可以自行到其官网查询~


相关参考:

#©JiaJung-自动打包发布到Fir和AppStore

#©zackzheng-详解Shell脚本实现iOS自动化编译打包提交

#©Github.webfrogs-xcode_shell

#©FastLane-自动化打包发布

#©Application Loader-altool工具

#©树叶有砖攻-Xcode9 xcodebuild export plist 配置


脚本打包与上传
https://davidlii.cn/2019/03/23/shell-ipa.html
作者
Davidli
发布于
2019年3月23日
许可协议