首页
/ PowerToys 本地 winget 安装测试实战:构建、哈希、自托管与清单验证全流程

PowerToys 本地 winget 安装测试实战:构建、哈希、自托管与清单验证全流程

2026-09-04 15:30:31作者:牧宁李

Microsoft PowerToys 每次发版前,都需要在 microsoft/winget-pkgs 清单仓库提交 winget 清单(manifest),而清单中的下载链接与 SHA256 哈希在提交前是无法直接对线上 Release 做端到端验证的。官方开发文档 test-winget-install-locally.md 为此提供了一套完整流程:本地构建或取回安装程序 → 计算哈希 → 自托管安装包 → 修改本地清单 → 用 winget install --manifest 走真实安装链路。读完本篇,你将掌握如何用 winget 的本地清单(Local Manifest)机制,在不污染线上发布的情况下验证 PowerToys 的 winget 安装场景是否可用。

为什么需要本地测试 winget 安装

PowerToys 的 winget 清单定义在独立的 winget 清单仓库中(对应 manifests/m/Microsoft/PowerToys 路径,包标识为 Microsoft.PowerToys),每个版本一个目录,目录内包含 3 个 yml 文件。由于清单提交前需要保证:

  • InstallerUrl 指向的安装包可正常下载;
  • Sha256 与安装包逐字节匹配;
  • winget install 能完整拉起 WiX Bootstrapper 并完成安装;

因此开发者会在本地搭建一个“模拟发布环境”:用自建端点托管安装程序,再用 winget 的 --manifest 参数直接指定本地清单文件夹安装。这套机制不依赖清单是否已合入官方仓库,是发布前验证的标准手段。

第一步:获取安装程序产物

文档给出两条获取途径:

  1. 从发布 CI 管线取产物:在 “Pipelines - Runs for PowerToys Signed YAML Release Build” 流水线中获取发布产物;
  2. 本地自行构建:执行 tools\build\build-installer.ps1 脚本。

本地构建脚本 build-installer.ps1 是完整的本地打包管线(restore、构建、MSIX 签名、WiX v5 MSI/Bootstrapper 生成),其参数定义如下(见 build-installer.ps1):

参数 默认值 说明
-Platform x64(未指定时自动检测) 目标平台,x64 / arm64
-Configuration Release 构建配置,Release / Debug
-PerUser true true 构建 per-user 安装程序,false 构建 machine-wide 安装程序
-Version 取自 src/Version.props 中的 Version 属性(当前为 0.0.1 显式指定 PowerToys 版本号,内部通过 versionSetting.ps1 写入
-Force 工作区不干净时跳过交互提示继续构建
-Clean 构建前清理 bin/obj 与 MSBuild 输出
-SkipBuild 跳过主解决方案构建(假定已构建过)
-EnableCmdPalAOT 启用 CmdPal 的 AOT 编译(构建更慢)

脚本的典型调用方式(见 BUILD-GUIDELINES.md):

.\tools\build\build-installer.ps1 -Platform x64 -Configuration Release -PerUser true

构建完成后,安装程序按脚本注释(见 build-installer.ps1)输出到相对于仓库根目录的路径:

installer/PowerToysSetupVNext/<Platform>/<Configuration>/UserSetup      # -PerUser true
installer/PowerToysSetupVNext/<Platform>/<Configuration>/MachineSetup   # -PerUser false

这里与 winget 清单中的安装作用域(--scope user 还是 --scope machine)直接对应。从 doc/devdocs/core/installer.md 可知,两种安装方式功能完全一致,差异仅在于:

  • Per-User:安装到 %LOCALAPPDATA%\PowerToys,注册表写入 HKCU,不同用户可各自持有独立安装;当前默认即 Per-User;
  • Per-Machine:安装到 Program Files\PowerToys,注册表写入 HKLM,全机共享一份安装。

因此,若要测试 --scope user 安装,应使用默认 -PerUser true 构建出的 UserSetup 目录中的 Bootstrapper .exe;若要测试 machine 作用域,则需 -PerUser false 重新构建 MachineSetup 产物。

注意:首次在本机构建安装程序时,建议以管理员身份运行构建流程,以便 WiX 工具将 wix.target 移动到指定位置并信任用于签名 MSIX 包的证书(见 doc/devdocs/core/installer.md)。由于本地构建的 MSIX 使用本地开发证书签名,若要在其他机器上运行该安装程序,需先用 cert-management.ps1 导出签名证书并在目标机器上信任(见 build-installer.ps1 的 NOTES 说明)。

第二步:计算安装程序的 SHA256 哈希

winget 清单强制要求安装包哈希,winget 在下载后会校验,不匹配即拒绝安装。文档给出的计算方式为:

cd /path/to/your/directory/contains/installer
Get-FileHash -Path ".\<Installer-name>.exe" -Algorithm SHA256

<Installer-name>.exe 替换为第一步产物目录中实际的 Bootstrapper 文件名,Get-FileHash 输出的 Hash 值(64 位十六进制串)就是要写入清单 Sha256 字段的值。注意哈希必须针对最终托管出去的那一份文件计算,任何重新打包、重签名都会使其失效。

第三步:托管安装程序(Self-Host)

文档特别强调一条限制:不能直接使用 staged 的 GitHub Release 产物或发布管线中的产物(原文:Attention: staged github release artifacts or artifacts in release pipeline is not OK in this step),必须提供一个可公开访问的下载端点。

文档推荐的最简自托管方式是 Python 内置 HTTP 服务:

python -m http.server 8000

在仓库根目录(或产物目录)执行后,安装程序的下载地址形如 http://<本机IP>:8000/installer/PowerToysSetupVNext/x64/Release/UserSetup/<Installer-name>.exe,该 URL 将作为清单中的 InstallerUrl。实测时还需要注意:

  • python -m http.server 是单线程简单服务,仅供本机/局域网验证使用,不要用于任何生产场景;
  • 若从其他机器测试,需放行 Windows 防火墙的 8000 端口入站;
  • 服务进程存活期间不要移动或修改产物目录,否则下载到的文件与哈希计算时的文件不一致,winget 校验将失败。

第四步:准备本地清单文件夹

从 winget 清单仓库下载一个已发布的版本目录(文档以 PowerToys 0.92.1 版本目录为例),该目录包含 3 个 yml 文件

文档原文提醒:Do not put any files other than these three in this folder —— 该文件夹内除这 3 个 yml 文件外不得放任何其他文件,否则 winget 在解析本地清单时会报错。

这 3 个文件是 winget 清单的标准组成:版本清单(声明包标识、版本、发行信息等)、本地化清单(显示名称、发布者等本地化字段)与安装程序清单(声明 InstallerUrlSha256、架构与作用域)。本地测试中只需改动其中的版本相关字段与安装程序字段,不需要改动包标识。

第五步:修改清单的版本、URL 与哈希

针对你的测试场景,对 3 个 yml 文件做如下修改:

  1. 版本号:将 3 个文件中的版本字段统一改为与你的安装程序一致的版本(清单内版本字段需保持一致;若该版本号在本地机器已装过 winget 包,winget 可能提示版本不更高,此时可用更高的测试版本号);
  2. InstallerUrl:替换为第三步自托管端点的完整 URL;
  3. Sha256:替换为第二步计算出的哈希值。

版本与清单中声明的架构/作用域也需与产物匹配:清单若声明 x64 + user scope,就应使用 -Platform x64 -PerUser true 构建的 UserSetup 产物。

第六步:执行 winget 本地清单安装

文档给出的安装命令(需以管理员身份运行):

#execute as admin
winget settings --enable LocalManifestFiles
winget install --manifest "<folder_path_of_manifest_files>" --architecture x64 --scope user

各参数含义:

  • winget settings --enable LocalManifestFiles:开启“允许本地清单文件”开关,这是 --manifest 生效的前提;该设置是机器级配置,测试完成后建议用 winget settings --disable LocalManifestFiles 关闭;
  • --manifest "<folder_path_of_manifest_files>":指向第四步准备的、只含 3 个 yml 的清单文件夹;
  • --architecture x64:与清单声明及安装程序架构一致;arm64 机器上构建并使用 arm64 产物时改为对应值;
  • --scope user:以 per-user 作用域安装(产物需为 UserSetup);machine 作用域测试则改为 --scope machine(产物需为 MachineSetup,且安装过程需要管理员凭据)。

安装过程即真实复现了用户侧的 winget 安装链路:winget 解析清单 → 从 InstallerUrl 下载 → 校验 SHA256 → 拉起 WiX Bootstrapper(依赖检查、关闭已有 PowerToys)→ 安装 MSI → 注册组件。

验证与排错

安装完成后可按以下路径确认结果:

  • 安装目录:per-user 安装位于 %LOCALAPPDATA%\PowerToys,per-machine 位于 Program Files\PowerToys(见 doc/devdocs/core/installer.md);
  • 安装日志:Bootstrapper/MSI 日志位于 %LOCALAPPDATA%\Temp\PowerToys_bootstrapper_*.log,自定义安装动作日志位于 %LOCALAPPDATA%\Temp\PowerToys_*.log,是定位安装失败的首要依据(见 doc/devdocs/core/installer.md);
  • 包状态winget list --id Microsoft.PowerToys 查看 winget 视角的安装状态;验证完毕可用 winget uninstall --id Microsoft.PowerToys 清理环境。

结合构建侧脚本还能解释几类常见失败:

  • 哈希不匹配:通常源于托管文件与计算哈希的文件不是同一份,或自托管目录在托管过程中被重新构建(build-installer.ps1 在构建前会清理 installer/ 下部分输出,见 BUILD-GUIDELINES.md),重新计算哈希即可;
  • 构建日志:构建阶段失败时查看解决方案/项目旁的 build.<configuration>.<platform>.errors.log 等日志文件(见 BUILD-GUIDELINES.md);
  • 作用域与产物不匹配:用 --scope machine 安装了 UserSetup 产物(或反之)会因清单声明与 Bootstrapper 类型不一致而失败,需重新构建对应 -PerUser 值的产物。

小结

这套本地 winget 安装测试流程覆盖了从产物生成到清单验证的完整链路:

  1. build-installer.ps1 本地构建(或从发布管线获取)安装程序产物;
  2. Get-FileHash -Algorithm SHA256 计算产物哈希;
  3. python -m http.server 8000 等自托管端点暴露产物,避免使用管线/Release 暂存产物;
  4. 取一份官方版本清单目录(严格保持 3 个 yml 文件);
  5. 修改版本、InstallerUrlSha256
  6. 管理员下 winget settings --enable LocalManifestFiles + winget install --manifest ... --architecture x64 --scope user 完成真实链路验证。

掌握该流程后,任何涉及 winget 清单变更(URL、哈希、作用域、架构)的 PowerToys 发版动作,都可以在合入清单仓库前完成端到端验证,把安装失败问题拦截在发布之前。

登录后查看全文
热门项目推荐
相关项目推荐