前言
在使用 Azure Pipelines 为 C# .NET MSIX 应用构建 CI/CD 管道文中,我介绍了为开源项目创建 Azure Pipelines CI/CD管道的方法。这一方法使用的是 Azure 的云端虚拟机映像环境,每次编译时都需要重新构建环境后再进行编译;而桌面端应用通常又需要很长的编译时间故效率并不高。因此,我最近在自己的 Homelab 中开了一个虚拟机,专门用于编译管道,通过固定的运行环境和更高的性能来减少管道运行时间。
作为参照,由微软托管的云端虚拟机的硬件配置是:2x CPU, 7GB RAM, 14GB SSD, Windows Server 2022 10.0.20348
构建环境
编译环境
在微软提供的虚拟机映像中,不同的映像都提前配置了常见的软件环境,包括常见编程语言的运行时和编程工具,而我们在安装好自托管系统后需要手动安装这些环境。对于 Snap Hutao 项目而言,这包括了Visual Studio 2022
,.NET Framework 7
和 Windows SDK
代理池权限设置
回到 Azure DevOps 网站,在组织设置里进入 Pipelines Agent pools 设置。进入 Default
代理池,并选择 Security
,将CI/CD 管理员的帐号添加进用户权限。如果该帐号已经是组织管理员,则无需设置。
添加主机进 Azure Pipelines
在 Azure DevOps网站中,在登录的状态下点击右上角个人设置里的 Personal Access Token
。点击 New Token
来创建一个新的密钥,为其设置一个名称,将组织选择为即将添加主机需要服务的组织,在权限范围内将 Agent Pools
设置为 Read & manage
。创建成功后,将获得的 Token 保留备用。
再次进入组织设置的 Default
代理池页面,点击右上角的 New agent
,我们将会获得创建 Azure Pipelines 代理主机的软件和命令。按照页面的指示,在代理主机上下载管理程序并执行 PowerShell 命令。在这里,我选择的是64位 Windows 并且执行 .config.cmd
以将代理软件以服务运行于主机上。
在 config.cmd
脚本中,你会被要求输入刚刚创建的 Token 并为当前主机代理命名,其余要求的设置我建议完全默认,因为这会和Azure Pipelines 云端映像环境保持一致,这有助于在未来遇到环境时更容易地找到通用的解决方案。
当你看到服务已启用的提示后,说明管道代理程序已经在运行,你可以关掉 PowerShell 命令行了。而在 Azure DevOps 组织代理池设置中,你也可以看到刚刚创建的主机了。
修改 CI/CD 配置
最基本的设置
我们需要将配置文件里的代理池指定为刚刚创建的主机上;因为不再使用云端映像,所以
vmImage: 'windows-2022'
也可以删除了pool: name: Default demands: agent.name -equals Hutao-Server
- 修正环境目录。对于云端映像而言,使用环境变量目录是更安全的解决方案。但是在前文中我们说过 Azure Pipelines 对于 .NET 7 的兼容性有问题,所以在配置文件中我使用了很多绝对路径。在迁移到新的系统环境中后,我们需要更新这些路径。比如说云端映像中的 Windows SDK 版本为
10.0.22000.0
而我本地版本为10.0.22621.0
。因此,我需要将 MSIX 构建的命令修改为
- task: CmdLine@2
displayName: Build MSIX
inputs:
script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\makeappx.exe" pack /d $(Build.SourcesDirectory)\src\Snap.Hutao\Snap.Hutao\bin\x64\Release\net7.0-windows10.0.19041.0\win10-x64 /p $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix'
可选的更改
将机密文件持久化存储在代理主机上
在胡桃项目的管道配置中,我将输出的文件用 Rclone 上传到云端用于分发。在使用云端代理时,我们需要将 rclone 配置文件作为机密文件储存并在管道运行时由 Azure 以加密的形式发送到主机上。现在我们可以将配置文件添加到主机的代理目录里,然后将 configPath
里从机密文件指向该文件
- task: rclone@1
displayName: Upload CI via Rclone
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
inputs:
arguments: 'copy $(Build.ArtifactStagingDirectory)/Snap.Hutao.Alpha-$(build_date).$(rev_number).msix downloadDGPCN:/releases/Alpha/'
configPath: 'C:\agent\_work\_tasks\rclone.conf'
自定义插件
在胡桃项目的流水线作业中,我使用了 MagicChunks
插件来实现对包信息的更改。但是这个插件有一个 warning 提示使用了过时的 PowerShell 指令,原作者因不再积极维护该插件而没有合并 PR。 对于这种情况,我们可以直接将修正分支打包下后自行编译,然后将插件文件覆盖于 C:\agent\_work\_tasks\
目录中。这种做法并不会消除警告(因为插件元信息是由云端储存的),但能在实际上解决插件的问题。
运行速度对比
根据下面的对比,我们可以看到使用自托管的主机后,我们规避了大量的环境安装流程;得益于更高的虚拟机性能,编译和打包阶段所费时间也大大减少。流水线运行单次总时间由原本的5分钟左右,降低到现在稳定的2分30秒左右。
转载请标注来源
图片好像挂了