前言
去年年底,Snap Genshin 项目被归档并被 Snap Hutao 替代。在新一代的应用中,项目使用了更新式的技术,使用winui3 替代了原始的 WPF,并最终打包为 msix 安装包,使其上架到了微软商店。由于商店的审核机制,应用不能再像过去自分发模式一样随时推送更新,因此构建为开发人员测试的 CI/CD 通道版本变得更加重要了。为此,我使用 Azure DevOps 中的 Pipelines 通过其和 GitHub 的集成创建了 CI/CD 管道。
创建 DevOps 帐号
- 来到微软 Azure Pipelines 主页,点击 Start free with GitHub 来通过登录你的微软帐号来进入 Azure DevOps 面板并绑定 GitHub 帐号。
如果你是为团队或公司创建这个环境,建议使用团队微软帐号并在绑定 GitHub 过程中授权 Azure DevOps 使用团队库数据
- 同时,你需要为你的团队创建一个 DevOps 团队名称
- 对于需要免费额度的开源项目而言,需要添加这个表格或发送邮件给
[email protected]来申请免费额度
- 你需要等待1-2天等待微软官方为你的组织添加免费额度以运行 CI/CD 管道。在此之前,你仍然可以继续构建你的 CI/CD 管道,只是它们会因为额度不足而无法实际运行
创建项目
- 进入 DevOps 面板,进入组织页面,点击右侧的
New Project来创建一个新的项目 创建成功后选择
GitHub作为你的代码库![GitHub as repo GitHub as repo]()
- 在选择代码库时,在筛选中选择
All repositories后找到你需要创建 CI 管道的项目并确认
![Select GitHub Repository Select GitHub Repository]()
- 之后页面会跳转到 GitHub 授权页面,在这里同意 Azure DevOps 编辑该库
- 完成授权后,你会被跳转到一个
YAML文件编辑器上,这个就是 Azure Pipelines 的配置文件
构建管道
兼容性问题
在微软官方文档中,他们推荐了由微软官方维护的 Azure Pipelines 插件,但该插件在写本文时对 VS 2022 存在兼容性问题。所以在构建 Snap Hutao 项目时,我通过 PowerShell 以命令行的方式打包了应用程序。因此,你也可以通过本文了解 MSIX 打包的细节流程。准备工作
在一切开始之前,我们先为管道环境设置好一些环境变量
build_date通过调用环境中的 PowerShell 返回了一个形如2023.2.28的时间格式,我们将这样的日期作为 CI/CD 测试通道中软件的版本号
variables:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
solution: '$(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao.sln'
project: $(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj'
buildPlatform: 'x64'
buildConfiguration: 'Release'
build_date: $[ format('{0:yyyy}.{0:M}.{0:d}', pipeline.startTime) ]我们将 CI/CD 环境设置为 Windows Server 2022,该环境下已包含 Visual Studio 2022 企业版
pool:
vmImage: 'windows-2022'编译二进制文件
- 接下来我们为 .NET Framework 程序创建打包环境
- task: UseDotNet@2
displayName: Install dotNet
inputs:
packageType: 'sdk'
version: '7.x'
includePreviewVersions: true
- task: NuGetToolInstaller@1
name: 'NuGetToolInstaller'
displayName: 'NuGet Installer'
- task: NuGetCommand@2
displayName: NuGet restore
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'select'随后,我们就可以使用 Visual Studio 2022 中包含的 MSBuild 来编译代码了
- 在这一步,大多数情况下我们都只需要
msbuildLocationMethod设置为version并将msbuildVersion设置为lastest或其它指定版本。但是,由于对 VS2022 的兼容性问题,我们不得不使用location和MSBuild.exe的路径作为打包参数
- task: MsixPackaging@1
displayName: Build binary package
inputs:
outputPath: '$(Build.ArtifactStagingDirectory)/'
solution: '$(solution)'
clean: false
generateBundle: false
buildConfiguration: 'Release'
buildPlatform: 'x64'
updateAppVersion: false
appPackageDistributionMode: 'SideloadOnly'
msbuildLocationMethod: 'location'
msbuildLocation: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Msbuild\Current\Bin\MSBuild.exe'MSIX 打包
在打包之前,我们要准备好打包后的软件包版本号。在已编译的二进制程序中,软件版本号是跟随代码中的设置的,但我们是不可能会为了一个 CI/CD 的临时版本而修改它的,所以我们需要让 CI/CD 管道为我们准备这个临时的版本号。在 CI/CD 管道最初运行时,我们已经通过环境变量获得了一个格式形为2023.2.28这样的日期,但缺少第四位的 revision number。非常巧合的是, Azure Pipelines 会为每一个 CI/CD 任务创建一个版本号,其格式形如20230228.1,其实小数点后的数字代表该日期下的第几次任务,这个数字就非常适合用于 revision number,因为它可以保证版本号一定是有序递增的。
在 Azure DevOps Market 中我们可以添加一款名为 GetRevision 的插件。使用它,我们就可以添加下面这段脚本将 revision number 设置到 rev_number 变量上。
- task: GetRevision@1
displayName: get Pipelines revision number
inputs:
VariableName: 'rev_number'除非额外指定参数,编译后的程序会被储存在项目目录下的\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\目录中
Windows 系统会通过读取 MSIX 应用的 Identity Name 来作为应用的唯一标识,因此我们需要更改一些信息以让系统和我们自己来区分 CI/CD 版本应用和正式版应用。在这里,我们使用 MagicChunks来修改管理这些属性的AppxManifest.xml文件
transformations中包含了我们覆盖的属性数值Package/Identity/@Name就是系统用于分辨软件包唯一标识的名称Package/Identity/@Publisher是关键的开发者信息,它的值必须和你MSIX应用包签名完全一致,否则会产生签名错误- 如果你的代码签名证书是购买来的,有时候它会包含多重信息,比如公司名称、组织名称以及邮箱。这种情况下,一些标点符号就会和
xml格式发生冲突。因此,我们需要对这些信息中有冲突的符号进行转义,并且需要包含这些全部信息,而不仅仅是CN。
- 如果你的代码签名证书是购买来的,有时候它会包含多重信息,比如公司名称、组织名称以及邮箱。这种情况下,一些标点符号就会和
"Package/Identity/@Version": "$(build_date).$(rev_number)"这一行将我们之前准备好的应用包版本写入属性
- task: MagicChunks@2
inputs:
sourcePath: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\AppxManifest.xml'
fileType: 'Xml'
targetPathType: 'source'
transformationType: 'json'
transformations: |
{
"Package/Identity/@Name": "7f0db578-026f-4e0b-a75b-d5d06bb0a74c",
"Package/Identity/@Publisher": "CN=DGP Studio CI",
"Package/Identity/@Version": "$(build_date).$(rev_number)",
"Package/Properties/DisplayName": "胡桃 Alpha",
"Package/Properties/PublisherDisplayName":"DGP Studio CI",
"Package/Applications/Application/uap:VisualElements/@DisplayName": "胡桃 Alpha"
}接下来,我们将应用程序中使用的静态资源也加入二进制程序目录下。这些资源在代码中是一种外部调用,所以在二进制编译过程中,它们是不会被自动添加到编译后的目录中的。
- task: CmdLine@2
displayName: Create resources folder
inputs:
script: |
mkdir Assets
mkdir Resource
workingDirectory: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64'
- task: CopyFiles@2
displayName: Copy Assets Folder
inputs:
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Assets'
Contents: '**'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Assets'
- task: CopyFiles@2
displayName: Copy Resource Folder
inputs:
SourceFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\Resource'
Contents: '**'
TargetFolder: '$(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64\Resource'一切就绪,我们现在可以通过makeappx将二进制文件打包为 MSIX 应用包了
- 其中,
$(Build.ArtifactStagingDirectory)是 Azure Pipelines 默认的导出资源目录
- task: CmdLine@2
displayName: Build MSIX
inputs:
script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\makeappx.exe" pack /d $(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.18362.0\win10-x64 /p $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'签名 MSIX 应用包
MSIX 应用包要求所有应用都需要代码签名,否则应用无法安装。所以接下来,我们将要为已打包的 MSIX 包签名。
点击 Pipelines 菜单中的 Library,进入 Secure files,点击按钮上传设有密码的 pfx 证书文件
回到 Pipelins YAML 文件编辑器,我们需要将 pfx 证书的密码作为变量储存在这个管道任务中。点击右上角的 Variables,点击添加。在添加变量的窗口中,为变量设置一个名称,并在Value中填写密码,最后勾选 Keep this value secret
现在我们就可以使用微软官方的MsixSigning插件来签名 Msix 应用包
- 其中
certificate为你在Secure Files中上传的证书文件名 passwordVariable为你储存 pfx 证书密码的变量名称
- task: MsixSigning@1
name: signMsix
displayName: Sign MSIX package
inputs:
package: '$(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
certificate: 'DGP_Studio_CI.pfx'
passwordVariable: 'pw'如果你希望使用 PowerShell 或 CMD 来签名可以参考以下的命令
SignTool sign /fd SHA256 /a /f C:\Users\i\Documents\GitHub\Snap.Hutao.Output\Snap.Hutao_TemporaryKey.pfx /p defaultpassword C:\Users\i\Documents\GitHub\Snap.Hutao.Output\Snap.Hutao.signed.msix发布 CI/CD 应用
在 Snap Hutao 项目中,我决定将侧载包和 CI/CD 侧载证书一起以 pre-release 的方式在 GitHub 主项目库中发布。其中,
Download Root CA任务将储存在Secure Files中的侧载证书下载到 CI/CD 环境中,并将该文件以cerFile名称储存在环境变量中。在 GitHub Release 发布流程中,我们可以通过$(cerFile.secureFilePath)来引用该文件gitHubConnection是集成的 GitHub 帐号
- task: DownloadSecureFile@1
name: cerFile
displayName: Download Root CA
inputs:
secureFile: 'Snap.Hutao.CI.cer'
- task: GitHubRelease@1
inputs:
gitHubConnection: 'github.com_Masterain'
repositoryName: 'DGP-Studio/Snap.Hutao'
action: 'create'
target: '$(Build.SourceVersion)'
tagSource: 'userSpecifiedTag'
tag: '$(build_date).$(rev_number)'
title: '$(build_date).$(rev_number)'
releaseNotesSource: 'inline'
releaseNotesInline: |
## 普通用户请勿下载
该版本是由 CI 程序自动打包生成的 `Alpha` 测试版本,**仅供开发者测试使用**
普通用户请[点击这里](https://github.com/DGP-Studio/Snap.Hutao/releases/latest/)下载最新的稳定版本
assets: |
$(Build.ArtifactStagingDirectory)/*
$(cerFile.secureFilePath)
isPreRelease: true
changeLogCompareToRelease: 'lastFullRelease'
changeLogType: 'commitBased'成果
点击 YAML 编辑器右上角的保存,你的 CI/CD 配置文件就会被添加到 GitHub 代码库中,并立刻执行一次。在 Azure DevOps 中你可以看到 Pipelines 的全部任务记录。
在 GitHub Release 中你可以看到已发布的 CI/CD 版本
在对应的 Commit 中,你也可以看到对应 CI/CD 任务的信息
转载请标注来源

