iOS应用程序的组装和交付功能

在本文中,Plarium Krasnodar工作室的DevOps工程师Maxim Shestakov分享了他在CI / CD调试期间积累的iOS应用程序的构建和交付方面的经验。



训练


与Apple设备开发应用程序相关联的每个人都已经设法体会到了有争议的基础设施的可用性。困难无处不在:从开发人员资料菜单到调试和组装工具。

网上有很多有关基本知识的文章,因此我们将尝试重点介绍。这是成功构建应用程序所需要的:

  • 开发者账户 ;
  • 基于macOS的设备充当构建服务器;
  • 生成的开发人员证书,该证书将用于对应用程序进行签名;
  • 创建的具有唯一ID的应用程序(应注意捆绑标识符的重要性,因为使用通配符ID使得无法使用应用程序的许多功能,例如:关联域,推送通知,Apple登录等);
  • 应用程序签名档案

开发人员证书应通过任何macOS设备上的钥匙串生成。证书的类型非常重要。根据应用程序环境(开发,质量保证,分段,生产),它会有所不同(开发或分发)以及应用程序签名配置文件的类型。

配置文件的主要类型:

  • 开发-旨在使用开发证书(iPhone开发人员的名称形式:XXXXX)来签署开发团队的应用程序;
  • 特设-用于使用开发人员的发行证书(iPhone发行类型的名称:XXXXX)由质量检查部门签署测试应用程序并进行内部验证;
  • App Store-发布版本,用于通过TestFlight进行外部测试,并使用开发者的发行证书上传到App Store。

生成“开发”和“临时”配置文件时,还会显示可在其上安装构建的设备列表,这使您可以进一步限制用户的访问。App Store配置文件没有设备列表,因为TestFlight负责封闭Beta测试期间的访问控制,这将在后面进行讨论。

为了清楚起见,您可以在下面的平板电脑形式显示开发者资料。更容易了解我们所需的装配参数以及从何处获取它们。



部件


为了使按项目和环境分离程序集更加容易,我们使用视图的概要文件${ProjectName}_${Instance}的名称,即项目+实例的名称(取决于应用程序环境:Dev,QA,GD,Staging,Live等)。

导入到构建服务器时,概要文件将其名称更改为唯一ID,然后移至该文件夹/Users/$Username/Library/MobileDevice/Provisioning Profiles该文件夹$Username与构建服务器用户帐户名相对应)。

生成* .ipa文件有两种方法-过时的(PackageApplication)和现代的(通过创建XcAchive并导出)。第一种方法被认为已过时,因为从8.3版开始,应用文件打包模块已从Xcode发行版中删除。要使用它,您需要将模块从旧的Xcode(8.2和更早版本)复制到文件夹中:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/

然后运行命令:

chmod +x /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/*

接下来,您需要收集应用程序的* .app文件:

xcodebuild \
-workspace $ProjectDir/$ProjectName.xcworkspace \
-scheme $SchemeName \
-sdk iphoneos \
build \
-configuration Release \
-derivedDataPath build \
CODE_SIGN_IDENTITY=”$DevAccName”\
PROVISIONING_PROFILE=”$ProfileId
DEPLOYMENT_POSTPROCESSING=YES \
SKIP_INSTALL=YES \
ENABLE_BITCODE=NO

其中:

-workspace-项目文件的路径。

-scheme-项目中指定的二手电路。

-derivedDataPath-卸载组合应用程序(* .app)的路径。

CODE_SIGN_IDENTITY-可以在钥匙串中验证的开发者帐户的名称(iPhone开发者:XXXX XXXXXXX,括号中没有TeamID)。



PROVISIONING_PROFILE-用于签署应用程序的配置文件ID,可通过以下命令获得:

cd "/Users/$Username/Library/MobileDevice/Provisioning Profiles/" && find *.mobileprovision -type f | xargs grep -li ">${ProjectName}_${Instance}<" | sed -e 's/.mobileprovision//'

如果应用程序使用其他配置文件(例如,用于“推送通知”),则请PROVISIONING_PROFILE指示:

APP_PROFILE=”$AppProfile” \
EXTENSION_PROFILE=”$ExtProfile” \

接下来,应将生成的* .app文件打包在* .ipa中。为此,您可以使用以下形式的命令:

/usr/bin/xcrun --sdk iphoneos PackageApplication \
-v $(find "$ProjectDir/build/Build/Products/Release-iphoneos" -name "*.app") \
-o "$ProjectDir/$ProjectName_$Instance.ipa"

但是,从Apple的角度来看,此方法已过时。通过从应用程序存档导出获取* .ipa是很重要的。

首先,您需要使用以下命令收集存档:

xcodebuild \
-workspace $ProjectDir/$ProjectName.xcworkspace \
-scheme $SchemeName \
-sdk iphoneos \
-configuration Release \
archive \
-archivePath $ProjectDir/build/$ProjectName.xcarchive \
CODE_SIGN_IDENTITY=”$DevAccName” \
PROVISIONING_PROFILE=”$ProfileId
ENABLE_BITCODE=NO \
SYNCHRONOUS_SYMBOL_PROCESSING=FALSE

区别在于汇编方法和SYNCHRONOUS_SYMBOL_PROCESSING在汇编过程中禁用字符卸载的选项

接下来,我们需要生成一个带有导出设置的文件:

ExportSettings="$ProjectDir/exportOptions.plist"

cat << EOF > $ExportSettings
<?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>compileBitcode</key>
<false/>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<false/>
<key>method</key>
<string>$Method</string>
<key>provisioningProfiles</key>
<dict>
<key>$BundleID</key>
<string>$ProfileId</string>
</dict>
<key>signingCertificate</key>
<string>$DevAccName</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>$TeamID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
EOF

其中:

$Method-交付方式与应用程序签名配置文件的类型相对应,即对于开发,对于Ad Hoc-ad-hoc,对于App Store-app-store,值将为development。

$BundleID-在应用程序设置中指定的应用程序ID。您可以使用以下命令进行检查:

defaults read $ProjectDir/Info CFBundleIdentifier

$DevAccName $ProfileId-先前使用的开发人员名称和签名配置文件ID的设置,必须与导出设置中的值匹配。

$TeamID-开发人员名称后方括号中的十位数ID,例如:iPhone开发人员:.......(XXXXXXXXXX);可以签入钥匙串。

接下来,使用export命令,获得必要的* .ipa文件:

xcodebuild \
-exportArchive \
-archivePath $ProjectDir/build/$ProjectName.xcarchive \
-exportPath $ProjectDir \
-exportOptionsPlist $ExportSettings

交货


现在,已组装的文件必须交付给最终用户,即安装在设备上。

对于Development和Ad Hoc构建的分发,有很多服务,例如HockeyApp,AppBlade和其他服务,但是在本文中,我们将讨论用于分发应用程序的独立服务器。

安装适用于iOS的应用程序分为两个阶段:

  1. 通过Items Service获取应用程序安装清单。
  2. 根据清单中通过HTTPS指定的信息安装* .ipa文件。

因此,对于初学者来说,我们需要使用以下命令生成安装清单(文件类型* .plist):

cat << EOF > $manifest
<?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>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>$ipaUrl</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>$BundleID</string>
<key>bundle-version</key>
<string>$AppVersion</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>$ProjectName_$Instance</string>
<key>subtitle</key>
<string>$Instance</string>
</dict>
</dict>
</array>
</dict>
</plist>
EOF

如您所见,清单几乎包含了应用程序组装中涉及的所有参数。可以使用以下命令检查

应用程序版本($AppVersion):

defaults read $ProjectDir/Info CFBundleVersion

该参数$ipaUrl包含下载* .ipa文件的直接链接。从iOS的第七版开始,必须通过HTTPS安装该应用程序。在第八版中,清单格式略有变化:删除了带有表单应用程序图标设置的块

<images>
   <image>...</image>
</images>

因此,要安装该应用程序,一个带有表单链接的简单html页面就足够了:

itms-services://?action=download-manifest&url=https://$ServerUrl/$ProjectName/$Instance/iOS/$AppVersion/manifest.plist

为了满足开发和测试部门的需求,Plarium创建了其构建安装应用程序,该应用程序为我们提供:

  • 自治与独立
  • 通过“临时”动态创建的链接进行集中式访问控制和安全安装应用程序,
  • 可扩展的功能(也就是说,如有必要,开发团队可以将缺少的功能集成到现有应用程序中)。

测试中


现在,我们将讨论使用TestFlight的预发布应用程序测试

下载的先决条件是App Store签名配置文件类型和是否存在生成的API密钥。

有几种下载应用程序的方法:

  • 通过Xcode(整理器),
  • 通过altool,
  • 通过Application Loader(适用于旧版本的Xcode)(现在称为Transporter)。

对于自动加载,使用altool,它也有两种授权方法:

  • 应用专用密码,
  • API密钥

最好使用API​​密钥下载应用程序。

要获取API密钥,请点击链接并生成密钥。除了* .p8格式的密钥本身之外,我们还将需要两个参数:IssuerID和KeyID。



接下来,我们将下载的密钥导入到构建服务器:

mkdir -p ~/.appstoreconnect/private_keys
mv ~/Downloads/AuthKey_${KeyID}.p8 ~/.appstoreconnect/private_keys/

在将应用程序加载到TestFlight之前,您需要验证应用程序,请使用以下命令执行此操作:

xcrun altool \
--validate-app \
-t ios \
-f $(find "$ProjectDir" -name "*.ipa") \
--apiKey “$KeyID” \
--apiIssuer “$IssuerID

API密钥生成页面中的 where apiKeyapiIssuer具有字段值。

然后,在成功验证后,我们使用--upload-app具有相同参数的命令加载应用程序

Apple会在一到两天内对应用程序进行测试,之后,外部测试人员可以使用该应用程序:他们将通过电子邮件发送安装链接。

通过altool下载应用程序的另一种方法是使用特定于应用程序的密码。

要获取应用专用密码,您需要点击链接并在“安全性”部分生成该密码



接下来,使用此密码在Keychain中创建构建服务器记录。从Xcode版本11开始,可以使用以下命令完成此操作:

xcrun altool --store-password-in-keychain-item "Altool" -u "$DeveloperName" -p $AppPswd

其中:

$DeveloperName是用于登录Apple服务的iOS开发人员帐户的名称。

$AppPswd-生成的应用专用密码。

接下来,我们获取asc-provider参数的值,并使用以下命令验证导入密码的成功:

xcrun altool --list-providers -u "$DeveloperName" -p "@keychain:Altool"

我们得出结论:

Provider listing:
- Long Name - - Short Name -
XXXXXXX        XXXXXXXXX

如您所见,所需的简称(asc-provider)值与构建应用程序时使用的$ TeamID参数匹配。

要在TestFlight中验证并加载应用程序,请使用以下命令:

xcrun altool \
--(validate|upload)-app \  
-f $(find "$ProjectDir" -name "*.ipa") \
-u "$DeveloperName" \
-p "@keychain:Altool" \

-p您可以采用$AppPswd未加密(显式)形式的值 作为参数值

但是,正如已经提到的那样,从可操作性的角度来看,最好选择用于授权工具的API密钥,因为在不同版本的Xcode中会遇到各种问题(它看不到钥匙串,卸载过程中的授权错误等)。

实际上,仅此而已。我希望所有参与App Store成功构建和无故障发布的人。

All Articles