winget-cli:Windows原生包管理的硬核胶水层

96 次阅读 0 点赞 0 评论 10 分钟原创开源项目

微软官方Windows包管理器winget-cli,C++17实现,采用策略+工厂+命令模式架构,支持多源(社区YAML/MS Store/私有REST)、组策略管控、JSON CI集成。实测8秒静默装Git,无后台进程,纯CLI哲学。

#windows #package-manager #cli #cpp #devops #system-admin
winget-cli:Windows原生包管理的硬核胶水层

哈喽,各位Windows摸鱼战友——哦不,是生产力战士们!我是周小码,一个被Spring Boot自动配置折磨到凌晨三点、却在Win10上连个Notepad++都要手动下exe的Java老兵。今天咱们不聊JVM调优,来盘一盘微软这个悄悄改变Windows生态的‘包管理平权运动’发起者:winget-cli

说真的,第一次看到winget install firefox时我手抖了一下——这哪是命令行?这是Windows版的apt-get啊!它不像Chocolatey那样需要PowerShell魔改环境变量,也不像Scoop那样得先装Git再配代理,它就静静地躺在App Installer里,像你妈给你装好的微信一样自然。

痛点引入:Windows安装,还在石器时代?

你有没有经历过:

  • 下载一个.exe,双击→下一步→下一步→完成→桌面弹出5个快捷方式;
  • 想卸载?去「设置→应用和功能」里翻3页,还找不到;
  • 新员工入职?IT发个Excel清单,让ta手动点开20个官网链接,逐个下载安装;
  • CI构建镜像?Dockerfile里写满curl -O https://... && sha256sum -c ... && msiexec /i ...,脆弱得像纸糊的防火墙。

这不是DevOps,这是考古现场。

解决方案:winget-cli —— 一个不喧哗的Windows原生包管理胶水层

winget-cli不是又一个PowerShell脚本集合,它是微软以C++17重写的、深度集成Windows Runtime(WinRT)的原生命令行工具。它的定位非常清醒:不做操作系统,只做连接器;不替代Installer,只抽象Installer

它把软件安装这件事,拆解为三个正交维度:

  1. 源(Source):从哪拉元数据?默认winget(GitHub YAML清单库)和msstore(微软商店API);
  2. 包契约(Manifest):每个软件用YAML定义ID、版本、下载URL、校验和、安装器类型(MSI/EXE/MSIX等)、静默参数;
  3. 执行上下文(Context):作用域(machine/user)、是否静默、覆盖参数、日志级别——全由Command对象封装。

这种分层,让它既轻量(单二进制<10MB),又可审计(所有YAML manifest开源可查),更可嵌入(无后台服务,无注册表污染,无开机自启)。

核心代码解析:策略模式如何落地?

src/AppInstallerCLICore/Commands/InstallCommand.cpp里的关键逻辑:

cpp 复制代码
// InstallCommand::Execute() 中的策略分发逻辑(简化版)
void InstallCommand::Execute()
{
    // 1. 根据manifest.installerType选择策略
    auto installer = InstallerFactory::Create(manifest.InstallerType());
    
    // 2. 将上下文(scope, silent, override)注入策略
    installer->SetScope(m_scope);
    installer->SetSilent(m_silent);
    installer->SetOverrideArguments(m_override);
    
    // 3. 执行安装 —— 具体逻辑由MSIInstaller/ExeInstaller等子类实现
    installer->Install(manifest);
}

再看InstallerFactory::Create()的骨架:

cpp 复制代码
// src/AppInstallerCLICore/Installer/InstallerFactory.cpp
std::unique_ptr<IInstaller> InstallerFactory::Create(InstallerTypeEnum type)
{
    switch (type)
    {
    case InstallerTypeEnum::msix:
        return std::make_unique<MsixInstaller>();
    case InstallerTypeEnum::msi:
        return std::make_unique<MsiInstaller>();
    case InstallerTypeEnum::exe:
        return std::make_unique<ExeInstaller>();
    case InstallerTypeEnum::inno:
        return std::make_unique<InnoSetupInstaller>();
    default:
        THROW_HR(E_NOT_SUPPORTED);
    }
}

这就是教科书级的策略模式 + 工厂模式组合:

  • IInstaller 是抽象策略接口;
  • 每个具体Installer(MsiInstaller等)只关心自己那一套静默参数和退出码逻辑;
  • InstallerFactory 负责根据YAML中声明的InstallerType动态创建实例;
  • InstallCommand 作为命令对象,完全不耦合任何安装细节,只负责调度和上下文传递。

这种设计,让winget-cli能在不改核心逻辑的前提下,轻松支持未来新增的安装器类型(比如.appxbundle或企业私有打包格式)。

实战演示:从零到CI预装

第一步,确保winget可用(别跳过!):

text 复制代码
## 官方推荐安装方式(通过Microsoft Store)
在 Microsoft Store 中搜索并安装 "App Installer"
## 安装后若终端找不到winget,手动添加PATH:
## %LOCALAPPDATA%\Microsoft\WinGet\Scripts

第二步,快速初始化开发环境:

bash 复制代码
## 一行搞定新员工基础工具链
winget install google.chrome visualcppbuildtools vscode powertoys

## 静默安装Python(CI友好)
winget install --id Python.Python.3.11 --silent

第三步,企业级备份与恢复:

bash 复制代码
## 导出当前机器所有winget安装的软件(含版本)
winget export --output installed.json

## 在另一台机器上批量重装(支持--force跳过已存在)
winget import installed.json --force

## 查看导出的JSON结构(供CI解析)
## {
##   "createdTime": "2026-02-05T09:42:18.123Z",
##   "imports": [
##     {"PackageIdentifier": "Mozilla.Firefox", "Version": "122.0"},
##     {"PackageIdentifier": "Microsoft.VisualStudioCode", "Version": "1.85.1"}
##   ]
## }

踩坑指南:那些README没明说、但你一定会撞上的墙

  • Windows Server 2019 不支持:不是bug,是设计约束。因为winget依赖Microsoft Store运行时组件(Microsoft.DesktopAppInstaller),而Server默认不带Store。解决方案:手动安装App Installer Framework Package 并启用App Installer可选功能。
  • PATH问题:安装后winget命令不可用?别急着重装。重启终端(或refreshenv if using Cmder/ConEmu),或手动将%LOCALAPPDATA%\Microsoft\WinGet\Scripts加入PATH——这路径里藏着winget.ps1winget.cmd两个入口,后者才是CMD兼容的关键。
  • 私有源调试:自建REST源时,winget source add -n mycorp -a https://intranet/winget 后,务必用 winget source update -n mycorp 强制刷新缓存,否则可能拉到旧YAML。

个人评价:克制,才是最高级的工程美学

作为Java人,我最欣赏winget-cli的「三不原则」:

  • 不用.NET:避免Framework绑定,降低部署门槛(Server Core也能跑);
  • 不搞GUI:坚守CLI哲学,拒绝「半吊子图形化」,所有交互可通过--accept-package-agreements --silent自动化;
  • 不堆功能:没有内置补全(但zsh/fish插件已由社区实现),没有本地索引(靠HTTP缓存和ETag),没有Web UI(你真需要?用winget search --source winget | ConvertTo-Html > index.html自己生成)。

它清楚自己的边界:一个Windows原生的、可嵌入的、可审计的包管理胶水层。不是平台,是管道;不是OS,是协议转换器。

如果你写C++,学它怎么用CMake组织跨平台构建、怎么用winrt::com_ptr玩COM API;如果你是运维,学它怎么用YAML定义包契约、怎么设计REST源协议;如果你是Java后端,学它如何把复杂安装逻辑抽象成‘策略+上下文’——这比看Spring源码还接地气。

最后送大家一句我最近悟的:所有伟大的开发者工具,都不是为了炫技,而是为了让下一个开发者,少踩一次你踩过的坑。 winget-cli,正在这么做。

(P.S. 别问我为啥不用WSL装apt……因为我同事的笔记本连WSL都没开,而他明天就要跑通Spring Cloud Alibaba demo 😅)

最后更新:2026-02-05T10:01:41

评论 (0)

发表评论

blog.comments.form.loading
0/500
加载评论中...