iOS脚本系列之Archive Export Upload到AppStore

前言

这个系列讲主要介绍一些提高iOS开发效率的命令行工具,利用这些方便的命令,我们可以写出各种脚本用来提高我们的生产力,本篇主要介绍,利用脚本实现工程Archive,Export出Release包,然后上传到AppStore。
与传统的打包上传操作相比,我们只需要在命令行之行脚本命令即可,大大的提高了工作效率,并且,个人感觉,通过命令行上传包到AppStore的过程会更快一些(通常在试用xcode upload或者Application Loader要等很久),
并且由于使用脚本,我们可以很容易的实现批量打包上传AppStore。在这一点上节省的人力尤为明显

本系列不是专业的shell 脚本教程,而是更加面向功能,所以一些shell的用法可能并不专业,所以如果你看到本文的Shell写法感到不适,来打我啊! 可以只参考思路哈。

Archive

通过xcodebuild 命令行工具实现,xcodebuild是xcode提供的命令行工具,我们在xcode中的大部分编译操作,都能通过这个命令行工具实现

Archive命令

1
xcodebuild archive

参数

1
2
3
-workspace   指定workspace  -workspace  xxx.xcworkspace
-scheme 指定build 的scheme -scheme "xxxxx" 没有引号也可以
-archivePath 指定archive输出目录 可以用相对路径 out/aaa (在当前目录中的out 文件夹下面 输出aaa.archive文件)

完整用例

1
xcodebuild archive -workspace xxx.xcworkspace -scheme "targetScheme" -archivePath Archive/test

在当前目录的Archive文件夹里面就会出现 test.Archive文件

Export

有了archive文件,接下来需要从archive文件里Export出Release包,相当于下图的操作

同样使用xcodebuild 命令行工具进行导出

Export 命令

1
xcodebuild  -exportArchive

参数

1
2
3
4
5
6
7
8
9
-archivePath   指定archive 文件的路径  -archivePath  out/xxx.xcarchive
-exportOptionsPlist 指定export 配置 是一个plist 文件 -exportOptionsPlist /xxxx/xxx.plist
不指定 exportOptionsPlist 的 (xcodebuild -exportArchive)命令已被标记为 depread (废弃)

指定的plist 文件包含了 导出的一些配置,比如上图中是导出ad hoc类型还是AppStore类型等配置
-exportPath 指定export输出目录

//下面的命令是可选的

-exportSigningIdentity 配置export 签名文件
-exportProvisioningProfile 配置profile 文件 使用profile文件名字-exportProvisioningProfile 'abcd_adhoc'

export plist配置文件介绍

导出时需要通过-exportOptionsPlist 制定一个plist配置文件,改配置文件包含以下属性

1
2
3
4
5
6
7
8
- method: (String) The method of distribution, which can be set as any of the following:
- app-store
- enterprise
- ad-hoc
- development
- teamID: (String) The development program team identifier.
- uploadSymbols: (Boolean) Option to include symbols in the generated ipa file.
- uploadBitcode: (Boolean) Option to include Bitcode.

这里导出到App Store使用如下配置

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>method</key>
<string>app-store</string>
<key>uploadSymbols</key>
<true/>
<key>uploadBitcode</key>
<true/>
</dict>
</plist>

注意 不需要使用 teamId
开始用 -exportOptionsPlist 导出的时候 报错,无法导出 最后删除 plist 里面的teamId 导出成功

完整命令

1
2
3
xcodebuild -exportArchive -archivePath /Users/name/Desktop/xxx/Archive/test.xcarchive
-exportOptionsPlist /Users/name/Desktop/xxx/export.plist
-exportPath /Users/name/Desktop/xxx/Export/testExport

上传到AppStore

其实到了目前这一步,已经节省了我们很多工作量了,我们只需要拿到导出的ipa文件用 Application Loader上传即可。但是本着极客的心态,我们必须尝试用脚本实现。

上传到AppStore 我们无法通过xcodebuild实现,这里借助了一个非常好用的命令行工具shenzhen

该工具提供了多种发布ipa包的方式,包括发布到AppStore,蒲公英,fir等等

发布到AppStore命令

1
ipa distribute:itunesconnect

参数

1
2
3
4
5
-a itunesconnect 账号
-p itunesconnect 密码
-i itunesconnect 上对应的AppId
-f ipa文件的路径
--upload 上传

完整命令

1
ipa distribute:itunesconnect -a me@email.com -p myitunesconnectpassword -i appleid  -f xxxx/test.ipa   --upload

Shell脚本

上面把核心功能都实现了,接下来就是怎么组装成一个Shell脚本,并且方便批量化投入使用了。

全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#当前路径
CURRENT_PATH=`pwd`

#当前路径的 workspace 绝对路径 如果没有workspace 为空
WORKSPACE_PATH=`ls | grep 'xcworkspace'`
WORKSPACE_NAME=
ACHIVE_PATH=$CURRENT_PATH"/Archive/"

#导出plist文件路径
EXPORT_PLIST=$CURRENT_PATH"/export.plist"
#导出ipa路径
EXPORT_OUTPUT=$CURRENT_PATH"/Export/"

#上传配置文件路径
UPLOAD_CONFIG_PLIST_PATH=$CURRENT_PATH"/upload.plist"

#读取命令行输入参数
SHOULD_UPLOAD=
SHOULD_ARCHIVE=
SHOULD_EXPORT=
SHOULD_WAIT=

#多个Scheme
BUILD_SCHEME=("Scheme1" "Scheme2" "Scheme3")

尤其是读取命令行输入的四个参数,可以用来分别控制本次命令,组合执行 archive,export,upload,三种命令。

另外看到 下面的写法

1
WORKSPACE_PATH=`ls | grep 'xcworkspace'`

两个`号用来执行一段shell命令,其执行结果保存到WORKSPACE_PATH中

获取输入参数

1
2
3
4
5
6
7
8
9
while getopts ":uaew" OPTION 
do
case $OPTION in
u ) SHOULD_UPLOAD="1";;
a ) SHOULD_ARCHIVE="1";;
e ) SHOULD_EXPORT="1";;
w ) SHOULD_WAIT="1";;
esac
done

upload.plist

由于上传的时候需要体统itunesconnect 账号密码,以及appid所以,这里我创建了一个上传的配置文件,以对应的scheme名字为key 保存itunesconnect 账号密码,以及appid

然后通过/usr/libexec/PlistBuddy来读取plist文件的内容

主循环

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
for scheme in ${BUILD_SCHEME[*]}

do
ACHIVE_OUTPUT=$ACHIVE_PATH""$scheme".xcarchive"
OUTPUT=$EXPORT_OUTPUT""$scheme

if [ $SHOULD_ARCHIVE ]; then
hilightlog "**********CLEAN scheme $scheme*****************"
CLEAN_CMD="xcodebuild clean -workspace $WORKSPACE_PATH"
CLEAN_CMD=$CLEAN_CMD" -scheme $scheme"
CLEAN_CMD=$CLEAN_CMD" -configuration release"
eval $CLEAN_CMD

XCODE_BUILD_CMD="xcodebuild archive"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -workspace $WORKSPACE_PATH"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -scheme $scheme"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -archivePath $ACHIVE_OUTPUT"

hilightlog "**********ARCHIVE $WORKSPACE_NAME scheme $scheme*****************"
eval $XCODE_BUILD_CMD
BUILD_RESULT=$?
if [ $BUILD_RESULT != 0 ]; then
errlog "ERROR : 编译失败"
exit
fi
fi


#export
if [ $SHOULD_EXPORT ]; then
XCODE_BUILD_CMD="xcodebuild -exportArchive"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -archivePath $ACHIVE_OUTPUT"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -exportOptionsPlist $EXPORT_PLIST"
XCODE_BUILD_CMD=$XCODE_BUILD_CMD" -exportPath $OUTPUT"

hilightlog "**********EXPORT*****************"
eval $XCODE_BUILD_CMD
BUILD_RESULT=$?
if [ $BUILD_RESULT != 0 ]; then
errlog "ERROR : EXPORT失败"
exit
fi
fi

#####UPLOAD####

if [ $SHOULD_UPLOAD ]; then
hilightlog "**********UPLOAD $scheme.ipa*****************"
IPA_PATH=$OUTPUT"/"$scheme".ipa"
#/usr/libexec/PlistBuddy 后面的参数本来是用单引号 'Print $scheme:acount'
#但是后来发现 在''里面引用变量的话不解包,所以改成了"" 双引号
UPLOAD_ACCOUNT=`/usr/libexec/PlistBuddy -c "Print $scheme:acount" $UPLOAD_CONFIG_PLIST_PATH`
UPLOAD_PASSWOLD=`/usr/libexec/PlistBuddy -c "Print $scheme:password" $UPLOAD_CONFIG_PLIST_PATH`
UPLOAD_APPID=`/usr/libexec/PlistBuddy -c "Print $scheme:appid" $UPLOAD_CONFIG_PLIST_PATH`


UPLOAD_CMD="ipa distribute:itunesconnect"
UPLOAD_CMD=$UPLOAD_CMD" -a $UPLOAD_ACCOUNT"
UPLOAD_CMD=$UPLOAD_CMD" -p $UPLOAD_PASSWOLD"
UPLOAD_CMD=$UPLOAD_CMD" -i $UPLOAD_APPID"
UPLOAD_CMD=$UPLOAD_CMD" -f $IPA_PATH"
UPLOAD_CMD=$UPLOAD_CMD" --upload --verbose"
eval $UPLOAD_CMD
UPLOAD_RESULT=$?
if [ $UPLOAD_RESULT != 0 ]; then
errlog "ERROR : UPLOAD失败"
exit
fi
fi

if [ $SHOULD_WAIT ]; then
echo "Press Enter to continue...";
read
fi

done

当两个’ ‘引号包裹起来的语句如果包含$xx 引用变量,这个变量是不会被解开的

1
2
3
4
5
6
7
PARA="test"
echo '$PARA hello'
echo "$PARA hello"

输出
$PARA hello
test hello