ios - 如何更新 UWP appxupload 包而不重建以针对多个环境
问题描述
我们正在构建一个针对 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
新的捆绑 ID 和配置文件对应用程序进行签名,如下所示(我们使用 Azure DevOps CI/CD 管道进行构建和发布,这一步是在发布时执行命令行管道,并引用环境变量来完成它的工作)
fastlane run resign ipa:"PATH_TO.ipa" signing_identity:"$(SigningIdentity)" bundle_id:$(BundleId) provisioning_profile:"$(provisioningProfile.secureFilePath)"
- 然后只需将辞职的管道上传到 AppStore
- 在应用程序中,我们检测应用程序的捆绑 ID 是什么,并根据该 ID 选择正确的配置文件
因此,我们也在尝试为 UWP 寻找类似的解决方案。UWP 应用的输出包就是这样的包QQPad.Mobile.UWP_0.39.0.0_x86_x64_arm_bundle.appxupload
。可能,我们需要执行以下操作之一来支持我们的方案
- 和 iOS 一样——在商店中创建单独的 UWP 应用,在项目中存储多个配置文件,在
appxupload
项目准备好后更改应用的包名,并在代码端检测捆绑 ID 并选择正确的配置文件 - 或者,如果可能的话,更好的解决方案如下
- 不要创建单独的应用程序,因为它管理起来很麻烦,而是在同一个应用程序托管中创建单独的包航班
- 无需更改包名称,而是能够在其中找到配置文件
appxupload
并将其替换为我们选择的配置文件 - 重新打包应用程序并辞职
- 将其上传到正确的包裹航班
同样,要点是能够动态选择或更改配置,而无需重新构建应用程序。UWP 是否有任何与fastlane
iOS 等效的实用程序,它也允许应用程序包操作?还是我必须手动执行此操作?如果是这样,具体如何?
解决方案
经过大量试验和失败后,我设法解决了这个问题。
我已经在这里发布了进行必要转换的脚本
以下是总结基本策略的步骤
- 该脚本接受环境、
appxupload
路径、签名证书和密码 - 我们要做的第一件事是提取
appxupload
存档。为此,我们需要复制appxupload
带有扩展名的文件*.zip
,以便它成为Expand-Archive
powershell 实用程序的有效输入 - 在提取的档案中,我们有
appxbundle
. 对于这个和appx
包,还有一个很好的工具来打包/解包它们,叫做MakeAppx。首先我们使用这个工具来解压appxbundle
. - 在解压后的文件夹中,我们将找到
appx
不同架构和规模的包文件。我们将不得不解压其中的每一个并对AppxManifest.xml
文件进行必要的更改。 AppxManifest.xml
然后将文件作为对象打开xml
,我们找到该PackageName
属性,并将其替换为类似CURRENT_PACKAGE_NAME - STAGING
暂存环境的内容。- 之后,我们将不得不
appx
通过替换旧包来重新打包并签署每个包 - 然后,在
appx
重新打包所有包后,我们可以appxbundle
使用MakeAppx
实用程序打包。我们也必须签这个 - 然后我们需要
appxupload
使用Compress-Archive
powershell 实用程序重新创建存档。这种类型的捆绑不需要签名。 - 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 附加到所有文件是必要的(请参阅脚本,我已经用这些更改更新了它),但这也没有解决问题。
最终,我决定放弃试验,因为它花费了太多时间并采用了多次构建方法,并分别为每个环境重建应用程序,并将 STAGING 版本部署到不同的应用程序中,而是创建了完全独立的应用程序来托管暂存版本。
推荐阅读
- html - jQuery:如何将链接从 textarea 转换为 div 中的图像?
- reactjs - 如何使用reactjs以推荐的方式将事件处理程序和状态从父组件调用到子组件?
- node.js - 在 Mongoose 上填充和分页子文档
- c# - 如何在客户端系统上进行本地报表处理
- reactjs - 无法使用 useEffect 和 React Redux 映射数组
- java - 在水平滚动视图中自动转到最右边(最后一个水平子项)然后回到最左边(第一个子项)
- c# - 为什么我的 unity android apk 大小太大?
- c# - 将 webjob(Microsoft.Azure.WebJobs) 包更新到 3.0.16 版后,没有发生显式死信
- powershell - Windows 删除所有供应商和 node_modules
- spring-boot - 如何添加“授权:承载
" 在使用 Springdoc Open API 3 Spec 的 OAuth2 隐式流的标头中