一间杂货铺~

11月 27

通过xcodebuild自动构建并发布Ad Hoc测试包


通过xcodebuild自动构建并发布Ad Hoc测试包

目标是通过一个shell脚本,完成构建及发布。

I. Ad Hoc发布证书及Provisioning Profile

在『钥匙串访问』中执行:

证书助理->从证书颁发机构请求证书:

证书助理->从证书颁发机构请求证书

填写证书信息:

此处,为了方便识别证书,其常用名称,被设置为了"Ad Hoc"。

填写证书信息

点击『继续』按钮:

会弹出certSigningRequest文件的保存路径。

生成certSigningRequest文件后,『钥匙串访问』中会多出名为"Ad Hoc"的专用密钥名为"Ad Hoc"的公用密钥,然后登录苹果开发者账号,进入『Certificates, Identifiers & Profiles』,在『Certificates』中添加证书,在第一步中会让用户选择需要创建哪种类型的证书,这里我们需要选择『Ad Hoc』。

  • 如果是个人开发者账号,界面会是这样:

个人开发者

  • 如果是企业开发者账号,界面会是这样:

企业开发者

注意:一个是App Store and Ad Hoc,一个是In-House and Ad Hoc。也就是说,发布到App Store用的证书也可用于Ad Hoc发布。我这里的不可用(显示为灰色),是因为之前已经创建了相关的证书。

后面会上传之前生成的certSigningRequest文件,然后生成证书,下载并双击证书,证书就会与『钥匙串访问』中之前多出的Ad Hoc专用密钥进行配对。

接着,需要生成Ad Hoc所用的Provisioning Profile

  • 如果是个人开发者账号,界面会是这样:

个人开发者

  • 如果是企业开发者账号,界面会是这样:

企业开发者

在随后的步骤中,会让选择App ID,对于Ad Hoc发布,可以使用明确的App ID,也可以使用包含通配符(*)的App ID。如果没有相应的App ID,可以在Identifiers->App IDs中进行添加:

Snip20161122_13.png

选择App ID后,会让选择certificates,勾选在之前生成的证书,然后就可以生成Provisioning Profile文件,下载并双击,系统会自动将这个文件复制到~/Library/MobileDevice/Provisioning Profiles目录,并为其重新命名,新名称会类似这样:86ceff27-1dff-40eb-8fd7-6072af8cb03b.mobileprovision,这样的名称会在xcodebuild命令中使用到,但不会使用到.mobileprovision扩展名。

II. 使用xcodebuild构建项目

xcodebuild简介

用途

xcodebuild最主要的用途就是『构建Xcode项目及工作空间』,也有其它用途:

  • 列举信息(项目配置、SDK、xcodebuild版本)
  • 导出存档(从xcarchive存档导出ipamanifest.plist
  • 导入/导出本地化文件(localization

xcodebuild命令的详情使用说明,请自行在命令行中执行man xcodebuildxcodebuild -help进行查看。这里给出的用法示例,基本能够满足日常的使用。

项目构建

项目构建主要会使用xcodebuild的三种构建形式:

# 1
xcodebuild [-project name.xcodeproj]
    [[-target targetname] ... | -alltargets]
    [-configuration configurationname]
    [-sdk [sdkfullpath | sdkname]] [action ...]
    [buildsetting=value ...] [-userdefault=value ...]

# 2
xcodebuild [-project name.xcodeproj] -scheme schemename
    [[-destination destinationspecifier] ...]
    [-destination-timeout value]
    [-configuration configurationname]
    [-sdk [sdkfullpath | sdkname]] [action ...]
    [buildsetting=value ...] [-userdefault=value ...]

# 3
xcodebuild -workspace name.xcworkspace -scheme schemename
    [[-destination destinationspecifier] ...]
    [-destination-timeout value]
    [-configuration configurationname]
    [-sdk [sdkfullpath | sdkname]] [action ...]
    [buildsetting=value ...] [-userdefault=value ...]

如果没有使用workspace,也就是没有使用xcworkspace文件,就使用前两种形式。

如果使用了workspace,就使用第三种形式(比如,项目使用了CocoaPods)。

生成xcarchive存档

为了构建xcarchive,需要将action指定为archive,此时必须指定一个schemescheme名称可在Xcode菜单的Product->Scheme->Manage Schemes中查找到。

比如,你的项目名称为Ad-Hoc-Demo,Scheme为Ad-Hoc-Demo-Scheme且没有使用workspace,就可以使用如下命令,对项目进行archive

xcodebuild -project Ad-Hoc-Demo.xcodeproj -scheme Ad-Hoc-Demo-Scheme archive

但有可能出错。因为项目的配置可能是不正确的(比如证书、Provisioning ProfileBundle Identifier配置错误)。

有三种途径可以查看项目的配置,:

  1. 直接在Xcode中进行查看(最直观)
  2. XXX.xcodeproj/project.pbxproj文件中进行查看
  3. 通过xcodebuild-showBuildSettings选项进行查看(建议使用这种方式)。

建议通过-showBuildSettings选项进行查看,主要原因是之前遇到的一个坑,我在Xcode中已经将证书、Provisioning Profile正确地设置了(对于Xcode来说是正确的,对于xcodebuild来说就不一定是正确的),但在使用xcodebuild时还是报出错误:Provisioning profile "XXXDis" doesn't include signing certificate "iPhone Distribution: XXX.。之所以报出这样的错误,只是因为Xcode中的配置并没有与项目的XXX.xcodeproj/project.pbxproj文件中的配置完全保持一致,该文件中的PROVISIONING_PROFILE_SPECIFIERPROVISIONING_PROFILE所对应的Provisioning Profile文件并不是同一个。

为了让构建进行得更加顺利,需要指定一些[buildsetting=value ...],比如,指定前面提到的PROVISIONING_PROFILE_SPECIFIERPROVISIONING_PROFILE

PROVISIONING_PROFILE_SPECIFIER的值可以这样找到:Xcode->Preferences...->Accounts,然后选择一个Apple ID,再点击右下角的View Details,进入详情界面,其中的Provisioning Profiles那一列就是PROVISIONING_PROFILE_SPECIFIER

PROVISIONING_PROFILE_SPECIFIER

在这个界面中,右键点击某个Provisioning Profile,可以看到Show In Finder项,选择该项,可以直接进入~/Library/MobileDevice/Provisioning Profiles目录。

如果Provisioning Profile文件名为86ceff27-1dff-40eb-8fd7-6072af8cb03b.mobileprovision,就将PROVISIONING_PROFILE的值指定为"86ceff27-1dff-40eb-8fd7-6072af8cb03b"(不带后面的.mobileprovision):

xcodebuild -project Ad-Hoc-Demo.xcodeproj -scheme Ad-Hoc-Demo-Scheme PROVISIONING_PROFILE="86ceff27-1dff-40eb-8fd7-6072af8cb03b" PROVISIONING_PROFILE_SPECIFIER="Ad Hoc" archive

这样就可以构建出xcarchive存档,其默认的存放目录为~/Library/Developer/Xcode/Archives,我们可以修改存放目录,只需要指定-archivePath选项,并在其后面指定xcarchive存档存放路径(需要包含xcarchive存档的文件名)就可以了:

xcodebuild -project Ad-Hoc-Demo.xcodeproj -scheme Ad-Hoc-Demo-Scheme PROVISIONING_PROFILE="86ceff27-1dff-40eb-8fd7-6072af8cb03b" PROVISIONING_PROFILE_SPECIFIER="Ad Hoc" archive -archivePath ~/Ad-Hoc-Demo.xcarchive

自己指定xcarchive存档的存放路径,可以方便后续处理

actionarchive时,xcodebuild默认使用的Build ConfigurationRelease,不过我们可以根据实际情况进行设置(比如,使用生产环境还是测试环境,因为在默认情况下,Release配置中是没有设置DEBUG宏的,有些团队会根据这个宏,去连接不同的服务器),此时,需要使用到-configuration选项,可以指定ReleaseDebug

xcodebuild -project Ad-Hoc-Demo.xcodeproj -scheme Ad-Hoc-Demo-Scheme PROVISIONING_PROFILE="86ceff27-1dff-40eb-8fd7-6072af8cb03b" PROVISIONING_PROFILE_SPECIFIER="Ad Hoc" archive -archivePath ~/Ad-Hoc-Demo.xcarchive -configuration Debug

导出ipa

使用-exportArchive就可以从xcarchive存档导出ipamanifest.plist

不过需要指定其它3个选项,使用方式如下:

xcodebuild -exportArchive -archivePath xcarchivepath -exportPath
    destinationpath -exportOptionsPlist path
配置-exportOptionsPlist选项所需的plist文件

对于-exportOptionsPlist,需要创建一个plist文件,通过xcodebuild -help可以查看到该文件中可用的键及其描述,常用的有:

  • compileBitcode
  • manifest
  • method
  • teamID
  • uploadBitcode
  • uploadSymbols

其中,manifestmethodteamID是最重要的。

manifest对应的值是一个字典,这个字典需要包含appURLdisplayImageURLfullSizeImageURL这三个键。通过它,就可以生成一个名为manifest.plist文件,通过网页下载应用程序时,会使用这个manifest.plist文件。

method用于指明应该导出什么样的存档。

teamID可以在『钥匙串访问』中查看到:

teamID

导出ipamanifest.plist

创建并配置好-exportOptionsPlist选项所需的plist文件后,就可以通过xcodebuild -exportArchive导出ipamanifest.plist

III. 生成下载页

在下载页面中,最重要的就是下载链接的href属性,该属性值的形式如下:

itms-services://?action=download-manifest&url=xxx/manifest.plist

其中的xxxmanifest.plist所在URL的某一部分,需要根据具体情况进行设置。

对于iOS 7.1及更高版本,manifest.plist所在URL必须使用https协议

IV. 将文件上传到服务器

只需要将下载页面、manifest.plistipa文件上传到服务器的某个目录下,并确保可以通过网站访问到该目录下的文件。

V. 将以上步骤整合为脚本

需要将脚本保存在XXX.xcodeproj所在目录下(记得加执行权限)

脚本示例:

#!/bin/sh

#
#  Created by Daniate.
#

# 项目名称
PROJECT_NAME="xxx" # 请替换
# 工作空间名称
WS_NAME="xxx.xcworkspace" # 请替换
# Scheme
SCHEME_NAME="xxx" # 请替换
# 版本号
BUNDLE_VERSION=`grep -A1 'CFBundleShortVersionString' "./$PROJECT_NAME/Info.plist" | grep '<string>' | awk -F '<\/string>' '{print $1}' | awk -F '<string>' '{print $2}'`
# 当前日期时间
CURRENT_DATE_TIME=`date +%Y.%m.%d.%H.%M.%S`
# 由版本号及当前日期时间组成的目录名
EXPORT_FOLDER_NAME="v$BUNDLE_VERSION_$CURRENT_DATE_TIME"
# 文件导出目录
EXPORT_ROOT="./ad_hoc_production"
EXPORT_FULL_FOLDER_NAME="$EXPORT_ROOT/$PROJECT_NAME/$EXPORT_FOLDER_NAME"
# 归档路径
ARCHIVE_PATH="$EXPORT_FULL_FOLDER_NAME/$PROJECT_NAME.xcarchive"
# 域名或IP地址
DL_DOMAIN="https://xxx.cn" # 请替换
# 下载页面的名称
DL_HTML="dl.html"
# 下载页面所在的URL
DL_URL="$DL_DOMAIN/$PROJECT_NAME/$EXPORT_FOLDER_NAME/$DL_HTML"

# STEP 1. 清理以前构建所留下的文件
echo "正在执行 xcodebuild clean ..."
xcodebuild -workspace "$WS_NAME" -scheme "$SCHEME_NAME" clean  1>/dev/null
echo "xcodebuild clean 执行完毕"

# STEP 2. 构建归档
PROFILE="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # 请替换
PROFILE_SPECIFIER="xxx" # 请替换
TEAM_ID="xxxxxxxxxx" # 请替换
echo "正在执行 xcodebuild archive ..."
xcodebuild -workspace "$WS_NAME" -scheme "$SCHEME_NAME" -configuration Release PROVISIONING_PROFILE="$PROFILE" PROVISIONING_PROFILE_SPECIFIER="$PROFILE_SPECIFIER" archive -archivePath "$ARCHIVE_PATH"  1>/dev/null

if [ -d "$ARCHIVE_PATH" ] # 构建归档成功
then
    echo "执行 xcodebuild archive 成功"
else
    echo "执行 xcodebuild archive 失败"
    exit 1
fi

# STEP 3. 生成 exportOptionsPlist 选项所需的 plist 文件

EXPORT_OPTS_PLIST_NAME="export_opts.plist"
EXPORT_OPTS_PLIST_PATH="$EXPORT_FULL_FOLDER_NAME/$EXPORT_OPTS_PLIST_NAME"

echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
<plist version=\"1.0\">\n\
<dict>\n\
    <key>compileBitcode</key>\n\
    <false/>\n\
    <key>method</key>\n\
    <string>ad-hoc</string>\n\
    <key>teamID</key>\n\
    <string>$TEAM_ID</string>\n\
    <key>manifest</key>\n\
    <dict>\n\
        <key>appURL</key>\n\
        <string>$DL_DOMAIN/$PROJECT_NAME/$EXPORT_FOLDER_NAME/$SCHEME_NAME.ipa</string>\n\
        <key>displayImageURL</key>\n\
        <string>$DL_DOMAIN/display.png</string>\n\
        <key>fullSizeImageURL</key>\n\
        <string>$DL_DOMAIN/fullSize.png</string>\n\
    </dict>\n\
    <key>uploadBitcode</key>\n\
    <false/>\n\
    <key>uploadSymbols</key>\n\
    <false/>\n\
</dict>\n\
</plist>" > "$EXPORT_OPTS_PLIST_PATH"

if [ -s "$EXPORT_OPTS_PLIST_PATH" ]
then
    echo "成功生成 exportOptionsPlist 选项所需的 plist 文件"
else
    echo "生成 exportOptionsPlist 选项所需的 plist 文件失败"
    rm -rdf "$EXPORT_ROOT"
    exit 1
fi

# STEP 4. 导出 ipa、manifest.plist
echo "正在由归档导出ipa、manifest.plist文件 ..."
xcodebuild -exportArchive -archivePath "$ARCHIVE_PATH" -exportPath "$EXPORT_FULL_FOLDER_NAME" -exportOptionsPlist "$EXPORT_OPTS_PLIST_PATH"  1>/dev/null

# 删除归档
rm -rdf "$ARCHIVE_PATH"
# 删除生成的export options plist
rm "$EXPORT_OPTS_PLIST_PATH"

if [ -s "$EXPORT_FULL_FOLDER_NAME/$SCHEME_NAME.ipa" -a -s "$EXPORT_FULL_FOLDER_NAME/manifest.plist" ]
then
    echo "成功导出 ipa、manifest.plist"
else
    echo "导出 ipa、manifest.plist 失败"
    rm -rdf "$EXPORT_ROOT"
    exit 1
fi

# STEP 5. 生成  ipa 下载页面
echo "正在生成 ipa 下载页面 ..."
echo "<!DOCTYPE html>\n\
<html>\n\
    <head>\n\
        <title>Ad-Hoc 内部测试</title>\n\
        <meta charset=\"UTF-8\">\n\
        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\
        <style>\n\
            p {\n\
                text-align: center;\n\
            }\n\
        </style>\n\
    </head>\n\
    <body>\n\
        <p>\n\
            <img src=\"../../Icon.png\">\n\
        </p>\n\
        <p>\n\
            $PROJECT_NAME\n\
        </p>\n\
        <p>\n\
            $EXPORT_FOLDER_NAME\n\
        </p>\n\
        <p>\n\
            <a href=\"itms-services://?action=download-manifest&url=$DL_DOMAIN/$PROJECT_NAME/$EXPORT_FOLDER_NAME/manifest.plist\">\n\
                点击安装\n\
            </a>\n\
        </p>\n\
    </body>\n\
</html>" > "$EXPORT_FULL_FOLDER_NAME/$DL_HTML"

if [ -s "$EXPORT_FULL_FOLDER_NAME/$DL_HTML" ]
then
    echo "成功生成 ipa 下载页面"
else
    echo "生成 ipa 下载页面失败"
    rm -rdf "$EXPORT_ROOT"
    exit 1
fi

echo "正在将相关文件上传至服务器 ..."

SERVER_USER="xxx" # 服务器用户名,请替换
SERVER_IP="xxx.xxx.xxx.xxx" # 服务器IP,请替换
SERVER_FOLDER="~/xxx" # 服务器某目录,请替换

scp -r "$EXPORT_ROOT/$PROJECT_NAME" "$SERVER_USER@$SERVER_IP:$SERVER_FOLDER"

# 上传完成后,删除目录
rm -rdf "$EXPORT_ROOT"

echo "成功上传,请使用 iPhone Safari 打开:$DL_DOMAIN/$PROJECT_NAME/$EXPORT_FOLDER_NAME/$DL_HTML 进行下载安装"

标签:xcodebuild, ad hoc

还不快抢沙发

添加新评论