如何在不重建目标多个环境的情况下更新 UWP appxupload 包
How to update UWP appxupload package without rebuilding to target multiple environments
我们正在构建面向 iOS 和 UWP 的 Xamarin.Forms 应用。我们有一个要求,即该应用程序应作为独立安装用于登台和生产。基本上每个环境都有自己的设置(服务器 URL、应用程序配置等),这些设置存储在外部 Configuration.json
文件中。要点是,为了为每个环境动态选择正确的 Configuration.json
文件,不应重新构建应用程序。我们应该只构建应用程序一次,并且只更改配置。我们能够为 iOS
解决这个问题
为了支持 iOS 的动态配置,我们做了以下工作
- 将所有环境配置文件存储在项目中,如
StagingConfiguration.json
、ProductionConfiguration.json
。
- 在每个环境的 iTunes 连接中注册单独的应用程序,并使用其自己的捆绑包 ID - 例如
com.mycompany.app.staging
用于暂存,com.mycompany.app
用于生产
- 构建应用程序并生成
ipa
文件后,我们使用 fastlane
使用新的 bundle id 和配置文件重新注册应用程序,就像这样(我们使用 Azure DevOps CI/CD 用于构建和发布的管道,这一步是在发布管道中执行命令行,并引用环境变量来完成它的工作)
fastlane run resign ipa:"PATH_TO.ipa" signing_identity:"$(SigningIdentity)" bundle_id:$(BundleId) provisioning_profile:"$(provisioningProfile.secureFilePath)"
- 然后只需将已辞职的管道上传到 AppStore
- 在应用内,我们检测应用的 bundle ID,并根据它选择正确的配置文件
因此,我们也在尝试为 UWP 寻找类似的解决方案。 UWP 应用程序的输出包是这样的包QQPad.Mobile.UWP_0.39.0.0_x86_x64_arm_bundle.appxupload
。我们可能需要执行以下操作之一来支持我们的场景
- 与 iOS 中的操作相同 - 在商店中创建单独的 UWP 应用程序,在项目中存储多个配置文件,在
appxupload
项目准备就绪后更改应用程序的包名称,等等代码端,检测bundle ID并选择正确的配置文件
- 或者,如果可能的话,更好的解决方案如下
- 不要创建单独的应用程序,因为它管理起来很麻烦,而是在同一个应用程序托管中创建单独的包航班
- 不用更改包名称,而是能够在
appxupload
中找到配置文件并将其替换为我们选择的配置文件
- 重新打包应用并退出
- 上传到正确的包flight
同样,重点是能够动态选择或更改配置,而无需重建应用程序。是否有任何适用于 UWP 的实用程序等效于 iOS 的 fastlane
,这也将允许应用程序包操作?或者我必须手动执行此操作?如果是,具体如何?
经过多次尝试和失败,我设法解决了这个问题。
我已经发布了执行必要转换的脚本 IN HERE
下面是总结基本策略的步骤
- 脚本接受环境、
appxupload
路径、签名证书和密码
- 我们做的第一件事是提取
appxupload
存档。为此,我们需要复制带有 *.zip
扩展名的 appxupload
文件,这样它将成为 Expand-Archive
powershell utility 的有效输入
- 在提取的存档中,我们有
appxbundle
。对于这个和 appx
包,还有一个很好的实用程序 pack/unpack 它们叫做 MakeAppx。首先我们使用这个工具解压appxbundle
.
- 在解压后的文件夹中,我们会找到
appx
不同架构和规模的包文件。我们将不得不解压它们中的每一个并对 AppxManifest.xml
文件进行必要的更改。
AppxManifest.xml
文件然后作为 xml
对象打开,我们找到 PackageName
属性,并将其替换为类似 CURRENT_PACKAGE_NAME - STAGING
的暂存环境。
- 之后,我们将不得不通过替换旧包来重新打包并签署每个
appx
包
- 然后,在所有
appx
包重新打包后,我们可以使用 MakeAppx
实用程序打包 appxbundle
。我们也必须签署这个
- 然后我们需要使用
Compress-Archive
powershell 实用程序重新创建 appxupload
存档。这种类型的捆绑包不需要签名。
- YEAAAH,我们已准备好将
appxupload
推送到 UWP 商店!
以上步骤比较复杂。为了在 UWP 运行时读取包名称并提取环境名称,我不得不编写几行代码
var name = Windows.ApplicationModel.Package.Current.DisplayName;
string[] parts = name.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 1) return "Production";
string environmentName = parts.Last().Trim().ToLower();
我假设如果环境说明符在“-”之后不存在,它就是生产。
其余配置已经由您决定。我个人使用 Microsoft.Extensions.Configuration
和 appsettings.json
文件作为 EmbeddedResources
嵌入程序集
var configs = new ConfigurationBuilder();
var fileProvider = new EmbeddedFileProvider(typeof(Bootstrapper).Assembly, typeof(Startup).Namespace);
configs.AddJsonFile(fileProvider, "appsettings.json", false, false)
.AddJsonFile(fileProvider, $"appsettings.{environmentName.ToLower()}.json", true, false);
更新
不幸的是,我不得不放弃这个想法...为什么我认为它首先起作用,是因为我只执行了脚本并将其上传到 STAGING 航班。在那之后,我们的 CI 管道,一旦 QA 完成测试,就尝试将原始未修改的 appxupload
文件上传到生产 Alpha 测试航班,这就是问题出现的地方,我收到了这样的错误
InvalidParameterValue: Appxbundles (including previously published and currently uploaded) must be uniquely identified by their full names. You have provided two packages with the full name ****_0.55.3.0_X86_ which have different contents. Please remove one of these packages, or increment current package versions to continue.
门户网站似乎不能接受具有相同身份名称和相同版本号的相同包。
我想我也可以更新 appx 文件名并将 STAGING 附加到所有文件是必要的(参见 script,我已经用这些更改更新了它),但这并没有解决问题之一。
最终,我决定放弃试验,因为它花费了太多时间,转而采用多重构建方法,并分别为每个环境重建应用程序,并且不将 STAGING 版本部署到同一个应用程序中,而是创建托管 STAGING 版本的完全独立的应用程序。
我们正在构建面向 iOS 和 UWP 的 Xamarin.Forms 应用。我们有一个要求,即该应用程序应作为独立安装用于登台和生产。基本上每个环境都有自己的设置(服务器 URL、应用程序配置等),这些设置存储在外部 Configuration.json
文件中。要点是,为了为每个环境动态选择正确的 Configuration.json
文件,不应重新构建应用程序。我们应该只构建应用程序一次,并且只更改配置。我们能够为 iOS
为了支持 iOS 的动态配置,我们做了以下工作
- 将所有环境配置文件存储在项目中,如
StagingConfiguration.json
、ProductionConfiguration.json
。 - 在每个环境的 iTunes 连接中注册单独的应用程序,并使用其自己的捆绑包 ID - 例如
com.mycompany.app.staging
用于暂存,com.mycompany.app
用于生产 - 构建应用程序并生成
ipa
文件后,我们使用fastlane
使用新的 bundle id 和配置文件重新注册应用程序,就像这样(我们使用 Azure DevOps CI/CD 用于构建和发布的管道,这一步是在发布管道中执行命令行,并引用环境变量来完成它的工作)
fastlane run resign ipa:"PATH_TO.ipa" signing_identity:"$(SigningIdentity)" bundle_id:$(BundleId) provisioning_profile:"$(provisioningProfile.secureFilePath)"
- 然后只需将已辞职的管道上传到 AppStore
- 在应用内,我们检测应用的 bundle ID,并根据它选择正确的配置文件
因此,我们也在尝试为 UWP 寻找类似的解决方案。 UWP 应用程序的输出包是这样的包QQPad.Mobile.UWP_0.39.0.0_x86_x64_arm_bundle.appxupload
。我们可能需要执行以下操作之一来支持我们的场景
- 与 iOS 中的操作相同 - 在商店中创建单独的 UWP 应用程序,在项目中存储多个配置文件,在
appxupload
项目准备就绪后更改应用程序的包名称,等等代码端,检测bundle ID并选择正确的配置文件 - 或者,如果可能的话,更好的解决方案如下
- 不要创建单独的应用程序,因为它管理起来很麻烦,而是在同一个应用程序托管中创建单独的包航班
- 不用更改包名称,而是能够在
appxupload
中找到配置文件并将其替换为我们选择的配置文件 - 重新打包应用并退出
- 上传到正确的包flight
同样,重点是能够动态选择或更改配置,而无需重建应用程序。是否有任何适用于 UWP 的实用程序等效于 iOS 的 fastlane
,这也将允许应用程序包操作?或者我必须手动执行此操作?如果是,具体如何?
经过多次尝试和失败,我设法解决了这个问题。
我已经发布了执行必要转换的脚本 IN HERE
下面是总结基本策略的步骤
- 脚本接受环境、
appxupload
路径、签名证书和密码 - 我们做的第一件事是提取
appxupload
存档。为此,我们需要复制带有*.zip
扩展名的appxupload
文件,这样它将成为Expand-Archive
powershell utility 的有效输入
- 在提取的存档中,我们有
appxbundle
。对于这个和appx
包,还有一个很好的实用程序 pack/unpack 它们叫做 MakeAppx。首先我们使用这个工具解压appxbundle
. - 在解压后的文件夹中,我们会找到
appx
不同架构和规模的包文件。我们将不得不解压它们中的每一个并对AppxManifest.xml
文件进行必要的更改。 AppxManifest.xml
文件然后作为xml
对象打开,我们找到PackageName
属性,并将其替换为类似CURRENT_PACKAGE_NAME - STAGING
的暂存环境。- 之后,我们将不得不通过替换旧包来重新打包并签署每个
appx
包 - 然后,在所有
appx
包重新打包后,我们可以使用MakeAppx
实用程序打包appxbundle
。我们也必须签署这个 - 然后我们需要使用
Compress-Archive
powershell 实用程序重新创建appxupload
存档。这种类型的捆绑包不需要签名。 - YEAAAH,我们已准备好将
appxupload
推送到 UWP 商店!
以上步骤比较复杂。为了在 UWP 运行时读取包名称并提取环境名称,我不得不编写几行代码
var name = Windows.ApplicationModel.Package.Current.DisplayName;
string[] parts = name.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 1) return "Production";
string environmentName = parts.Last().Trim().ToLower();
我假设如果环境说明符在“-”之后不存在,它就是生产。
其余配置已经由您决定。我个人使用 Microsoft.Extensions.Configuration
和 appsettings.json
文件作为 EmbeddedResources
var configs = new ConfigurationBuilder();
var fileProvider = new EmbeddedFileProvider(typeof(Bootstrapper).Assembly, typeof(Startup).Namespace);
configs.AddJsonFile(fileProvider, "appsettings.json", false, false)
.AddJsonFile(fileProvider, $"appsettings.{environmentName.ToLower()}.json", true, false);
更新
不幸的是,我不得不放弃这个想法...为什么我认为它首先起作用,是因为我只执行了脚本并将其上传到 STAGING 航班。在那之后,我们的 CI 管道,一旦 QA 完成测试,就尝试将原始未修改的 appxupload
文件上传到生产 Alpha 测试航班,这就是问题出现的地方,我收到了这样的错误
InvalidParameterValue: Appxbundles (including previously published and currently uploaded) must be uniquely identified by their full names. You have provided two packages with the full name ****_0.55.3.0_X86_ which have different contents. Please remove one of these packages, or increment current package versions to continue.
门户网站似乎不能接受具有相同身份名称和相同版本号的相同包。
我想我也可以更新 appx 文件名并将 STAGING 附加到所有文件是必要的(参见 script,我已经用这些更改更新了它),但这并没有解决问题之一。
最终,我决定放弃试验,因为它花费了太多时间,转而采用多重构建方法,并分别为每个环境重建应用程序,并且不将 STAGING 版本部署到同一个应用程序中,而是创建托管 STAGING 版本的完全独立的应用程序。