本文整理自 GitHub 项目 microsoft/WindowsDeveloperConfig(MIT 许可)的 README、
src/windows-dev-config/README.md、wsl-comfort/readme.md、src/docs/development.md与部分configuration.winget源码。命令、注册表路径、配置清单忠于原文;章节组织、选型建议与评述为本文编辑归纳。数据截至 2026-10-02(仓库 2.9k star / 200 fork,创建于 2026-05-21)。
新到手的 Windows 11 开发机,从开机到能跑起来 hello world,中间隔着一段没人愿意承认的时间:装 Git、装 Node、装 Python、装 VS Code,改主题、改资源管理器不显示文件扩展名、改长路径、开 WSL,然后再发现 Oh My Posh 的字体没配好,终端里全是方块。
微软把这个流程做成了官方仓库:microsoft/WindowsDeveloperConfig,标语是三句话 —— Opinionated setups for Windows dev boxes. Idempotent. CI-tested.(有主张的 Windows 开发机配置;幂等;CI 验证过)。
它解决的是一个很具体的痛点:你的开发环境应该帮你交付,而不是变成另一个需要调试的项目。
一、先选路线:三条岔路,别走错
这个仓库不是单一脚本,而是三套互相独立的配置方案。走错路线会浪费不少时间,所以先看这张图:
| 路线 | 入口命令 | 需要克隆仓库吗 | 重启吗 |
|---|---|---|---|
| Windows Dev Config | irm https://aka.ms/devconfig/standard/setup.ps1 | iex | 不需要 | 会,一次 |
| WSL Comfort | .\wsl-comfort\install.ps1 | 需要 | 首次装 WSL 时会 |
| 单语言 Workloads | .\Workloads\python\install.ps1 | 需要 | 不会 |
一个容易混淆的点:Windows Dev Config 和 WinUI 3 这两条路线不需要 winget configure(用自己的引擎),其余 Workloads 依赖 winget configure,需要先启用。后面第五节会讲启用时的坑。
二、路线一:Windows Dev Config —— 一行命令,一次重启
打开任意一个 PowerShell 窗口,提权或不提权都行:
# 标准版
irm https://aka.ms/devconfig/standard/setup.ps1 | iex
# 完整版
irm https://aka.ms/devconfig/full/setup.ps1 | iex
⚠️ 它会重启你的机器,一次。 启用 WSL 需要装一个 Windows 可选功能,而那个必须重启。脚本会给 10 秒警告,然后注册一个计划任务,你重新登录并接受 UAC 之后自动接着装。跑之前先保存工作。
2.1 Standard 装什么
开发工具:Windows Terminal、PowerShell 7、Git、GitHub CLI、GitHub Copilot CLI、VS Code、.NET SDK 10、Python 3.14 + uv、Node.js LTS + nvm、Coreutils for Windows、Windows App CLI、Oh My Posh、PowerToys。
终端:PowerShell 7 设为默认 profile、Oh My Posh 进提示符、Cascadia Mono NF 作默认字体、下拉菜单里多一个 GitHub Copilot profile。
Windows 设置:深色主题、Win32 长路径、文件资源管理器默认值、开始/搜索设置、勿扰模式。
WSL:WSL 平台 + Ubuntu,含重启与之后的自动续跑。
2.2 Full 多加了什么
在 Standard 全部内容之上,追加:
- Windows 设置:开发者模式、Sudo(inline 模式)、关闭 Widgets、Edge 策略、更多开始/搜索/系统托盘设置。
- 远程桌面:启用 + 配好防火墙规则。
2.3 它到底改了哪些注册表
这是整套东西里最需要提前知道的部分。官方 windows-dev-config/README.md 给了完整清单,节选最有辨识度的几项:
| 分类 | 配置项 | 注册表路径 | 值 | Standard |
|---|---|---|---|---|
| 系统 | Sudo | HKLM\…\Windows\CurrentVersion\Sudo\Enabled | 3 | 跳过 |
| 系统 | 开发者模式 | HKLM\…\AppModelUnlock\AllowDevelopmentWithoutDevLicense | 1 | 跳过 |
| 系统 | Win32 长路径 | HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled | 1 | ✅ |
| 系统 | 远程桌面 | HKLM\…\Control\Terminal Server\fDenyTSConnections | 0 | 跳过 |
| 资源管理器 | 显示文件扩展名 | HKCU\…\Explorer\Advanced\HideFileExt | 0 | ✅ |
| 资源管理器 | 显示隐藏文件 | HKCU\…\Explorer\Advanced\Hidden | 1 | ✅ |
| 资源管理器 | 标题栏完整路径 | HKCU\…\Explorer\CabinetState\FullPath | 1 | ✅ |
| 资源管理器 | 打开"此电脑" | HKCU\…\Explorer\Advanced\LaunchTo | 1 | ✅ |
| 任务栏 | 右键"结束任务" | HKCU\…\Explorer\Advanced\TaskbarDeveloperSettings\TaskbarEndTask | 1 | ✅ |
| 开始菜单 | 无推荐项 | HKCU\…\Explorer\Advanced\Start_IrisRecommendations | 0 | ✅ |
| 通知 | 全局勿扰 | HKCU\…\Notifications\Settings\NOC_GLOBAL_SETTING_TOASTS_ENABLED | 0 | 跳过 |
| Edge | 新标签页空白 | HKLM\SOFTWARE\Policies\Microsoft\Edge\NewTabPageLocation | about:blank | 跳过 |
| 主题 | 应用/系统深色 | HKCU\…\Themes\Personalize\AppsUseLightTheme / SystemUsesLightTheme | 0 | ✅ |
WinForms / WinUI 3 的提醒:这两条会拉几个 GB 的 Visual Studio 组件。真机上没问题,小虚拟机上会很痛苦。
2.4 重启之后到底发生了什么
这是我觉得整个设计里最值得说的一段。
dev-config.ps1 把安装拆成 11 个阶段,Full 全跑,Standard 跳过 Edge 阶段:
| # | 阶段 | 说明 |
|---|---|---|
| 1 | Getting ready | 确认 PowerShell 7,必要时把 winget 更新到锁定稳定版 |
| 2 | Packages | 装全部包 + PowerToys 通知设置 |
| 3 | System settings | Sudo、开发者模式、长路径、远程桌面 |
| 4 | File Explorer tweaks | 资源管理器注册表项 |
| 5 | Taskbar, search & start | 任务栏/搜索/开始菜单/通知 |
| 6 | Microsoft Edge tweaks | Edge 策略(Standard 跳过) |
| 7 | Fonts | Cascadia Code NF / Mono NF,SHA-256 校验 |
| 8 | Windows Terminal | 默认 profile、字体、RunOnce 字体更新 |
| 9 | PowerShell profile | Oh My Posh 配置 |
| 10 | GitHub Copilot | 终端 profile、WinUI 模板、Copilot CLI 插件(best-effort) |
| 11 | WSL + Ubuntu | 故意放最后 |
第 11 阶段放在最后是有道理的:唯一需要的那次重启被推迟到其它所有事都干完之后。
要点:只重启一次。若 WSL 仍不可用,会停下说明原因,不会陷入循环重启。
2.5 幂等是怎么做出来的
官方文档原话:
It is idempotent — every change is checked before it’s made, so re-running it only fixes what has drifted. It is also resumable — if it fails, or you close the window, running it again picks up where it left off.
机制是每个 step 走 Check → Apply → Verify 三步:
- Check:先看是不是已经在目标状态。是 → 打印
already OK,什么都不做。 - Apply:不在,才改。
- Verify:再跑一遍同一个 check。仍失败就是 error,不是"静默成功"。
标记为 best-effort 的步骤失败时记为 flagged,流程继续,最后在总结里点名。进度记在 devconfig-tally.json 里,所以重启之后(或直接重跑)已成功的步骤几秒内就跳过,只重试 flagged 的。
包只有在 winget 报告 installed 且 current 时才算完成 —— 所以重跑顺带会把可用更新拉下来。
三、路线二:WSL Comfort —— 把 Shell 弄舒服
也叫 Comfort Shell。它是两半结构:
| 脚本 | 跑在哪 | 职责 |
|---|---|---|
install.ps1 | Windows(PS 5.1 或 7) | 编排:确保 WSL、挑/装发行版、在发行版里调 bootstrap、装字体、投放 WT profile |
comfort-shell-bootstrap.sh | Ubuntu(WSL 或裸机) | 配置 shell、提示符、工具、shims、Homebrew、git 默认值、dotfiles |
关键一句:bootstrap 是独立的。你可以把它 scp 到任何 Ubuntu 主机上单独跑,install.ps1 只是薄薄的编排层。灵感来自 Scott Hanselman 的 MacLikeWSLComfortShell。
3.1 五种用法
.\wsl-comfort\install.ps1 # 交互式,逐步确认
.\wsl-comfort\install.ps1 -NonInteractive # 无人值守,默认 Ubuntu
.\wsl-comfort\install.ps1 -Distro Ubuntu-24.04 # 指定已有发行版
.\wsl-comfort\install.ps1 -BootstrapArgs '--shell=bash','--no-brew'
./comfort-shell-bootstrap.sh # 交互
./comfort-shell-bootstrap.sh --non-interactive # 默认,不提问
./comfort-shell-bootstrap.sh --dry-run # 只看不改
3.2 bootstrap 的开关
| Flag | 作用 |
|---|---|
--shell=zsh|bash | 默认 shell,默认 zsh |
--no-brew | 跳过 Homebrew |
--no-shims | 跳过 pbcopy/pbpaste/open/xdg-open |
--no-prompt | 跳过 starship |
--no-tools | 跳过 apt CLI 工具 |
--minimal | 等于 --no-brew --no-shims --no-tools |
--force | 覆盖已有配置,而不是保留 |
--dry-run | 预演 |
3.3 三个我觉得很见功力的细节
① 托管块(managed blocks)写 dotfiles。 它在 ~/.zshrc 里写的是 # >>> comfort-shell >>> / # <<< comfort-shell <<< 包起来的一段,重跑时整块替换,不踩用户自己的编辑。
② 每发行版一个确定性 GUID。 Windows Terminal fragment 的 GUID 取 MD5("comfort-shell:<distro>")。效果:重跑原地更新;Ubuntu 和 Ubuntu-24.04 能作为两个 profile 共存。Fragment 落在
%LOCALAPPDATA%\Microsoft\Windows Terminal\Fragments\ComfortShell\comfort-shell-<slug>.fragment.json
然后碰一下 settings.json 的 mtime,WT 就会热重载扫描,不用重启终端。
③ 调完 wsl.exe 必须 Reset-TerminalInputMode。 wsl.exe 会给父控制台打开 Win32 Input Mode 和焦点报告,而且不总是还原。不复位,后面 Read-Host 会吐出 ^[[I 这类转义序列。这种坑不自己踩过写不进文档。
还有个 skel mode:以 root 跑时(预焙镜像),HOME 重定向到 /etc/skel,dotfiles 落进模板,Homebrew 延后到 ~/.comfort-shell-first-run 标记触发的新用户首次交互 shell。这正是"Windows 侧能在还没开过封的发行版上跑 bootstrap"的原因。
四、路线三:单语言 Workloads —— 只要一门工具链
12 个 workload,每个是一个 configuration.winget + 一个同名 install.ps1 shim:
| Workload | 装什么 |
|---|---|
| TypeScript | Node.js LTS + 全局 typescript |
| PHP | PHP 8.5 |
| .NET | .NET SDK 10 |
| Go | Go(rolling) |
| Java | Microsoft Build of OpenJDK 25 LTS |
| Rust | Rust stable via rustup |
| Python | Python 3.14 + uv |
| SQL | SQL Server + sqlcmd + VS Code 扩展 |
| PowerShell | PowerShell 7 + VS Code PowerShell 扩展 + PSScriptAnalyzer 设置 |
| WinForms | .NET SDK 10 + Windows Forms desktop workload |
| WinAppCLI | 开发者模式 + .NET SDK 10 + Windows App Development CLI |
| WinUI 3 | .NET SDK 10 + VS Community + Windows App SDK / WinUI 3 + WinAppCLI |
跑的方式:
winget configure -f .\Workloads\python\configuration.winget --accept-configuration-agreements --disable-interactivity
# 想要当前 shell 的 PATH 立刻刷新,改用 shim:
.\Workloads\python\install.ps1
WinUI 3 例外,它跟 Dev Config 同一套引擎,一行就行:
irm https://aka.ms/devconfig/winui/setup.ps1 | iex
配置长什么样?看 python/configuration.winget 全文就明白了 —— 一共 20 行:
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Python
type: Microsoft.WinGet/Package
properties:
id: Python.Python.3.14
source: winget
acceptAgreements: true
metadata:
winget:
securityContext: elevated
- name: Uv
type: Microsoft.WinGet/Package
properties:
id: astral-sh.uv
source: winget
acceptAgreements: true
metadata:
winget:
securityContext: elevated
就是一份 DSC v3 文档。理解了这一点,你自己加一门语言就是往上再加一个 resource。
改之前先验证,winget configure 有个 test 动词,跑 TestScript 判断系统当前是否已在目标状态,不装任何东西:
winget configure test --file .\Workloads\python\configuration.winget --accept-configuration-agreements --disable-interactivity
五、winget configure 的三个前置条件(踩过才懂)
除 Dev Config 外,其余 flow 全靠 winget configure。它有三个前置条件,缺任一则 install.ps1 会先报错退出:
- App Installer(winget)必须够新 —— 从 Microsoft Store 更新,或从 winget-cli releases 拉最新 MSIX。
- Configuration 特性必须打开 —— 新版 winget 上已 GA 且默认开;旧版可能要
winget settings里设experimentalFeatures.configuration = true。 - 组策略 / MDM 必须允许 —— 如果注册表
HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppInstaller\EnableWindowsPackageManagerConfiguration是0,整台机器被禁用,得先改策略。
冒烟测试:
winget configure --help | Select-Object -First 3
那个著名的内部错误
非提权环境下调 winget configure,必须先装 Visual C++ Redistributable,否则报 internal error / -2146233079:
winget install Microsoft.VCRedist.2015+.x64 # x64
winget install Microsoft.VCRedist.2015+.arm64 # ARM64
每个
install.ps1shim 会先跑Workloads/_common/assert-winget-configure.ps1,它会明确告诉你是上面三条里的哪一条没满足。仓库的enable-winget-configure.ps1也会自动把 VC Redist 装上。
六、能不能撤销?答案有点狠
能,但要先知道代价。
# 提权 PowerShell
& "$env:ProgramData\CalmOS\dev-config.ps1" -Action Uninstall
# 从源码目录(未签名开发模式)
.\src\windows-dev-config\dev-config.ps1 -AllowUnsigned -Action Uninstall
⚠️ Cleanup 是破坏性的、无确认提示。 它会永久删除 Ubuntu 发行版及其文件(等价
wsl --unregister Ubuntu),并卸载全部工具。即便你之前只跑过 Standard,清理也会按 Full 的配置来重置。
-Action Uninstall 会:禁用 Sudo / 开发者模式 / 远程桌面,重置资源管理器、开始菜单、搜索、通知、Widgets、Edge 策略、长路径;主题切回 light;移除 Terminal 里的 defaultProfile、各 profile 项与 Copilot fragment;移除托管的 Oh My Posh profile block、WinUI 模板;卸载 17 个工具包(uv、NVM、Node.js、Python 3.14、Git、GitHub CLI、Oh My Posh、.NET SDK 10、PowerToys、VS Code、Windows App CLI、PowerShell 7 等)。
不会恢复的:字体、Windows Terminal 本体、VC++ runtime、WinGet 及 Windows 可选功能、setup 文件与日志。
想手动清理:
winget uninstall --id <id> # 按 winget id 卸包
wsl --unregister Ubuntu # 不可逆
Remove-Item "$env:LOCALAPPDATA\Microsoft\Windows Terminal\Fragments\DevConfig" -Recurse -Force
Remove-Item "$env:ProgramData\CalmOS" -Recurse -Force # 需管理员
结论:别在你唯一的生产机上试。 用一次性 Windows Sandbox / Hyper-V / Dev Box 镜像。
七、安全视角:irm | iex 到底在执行什么
看到 irm <url> | iex 就皱眉是对的 —— 这是在内存里执行远端下载的脚本。这条链路的实际防护是:
bootstrap.ps1验证 Microsoft Corporation 签名,逐个校验仓库windows-dev-config/下每一个.ps1。- 落到
%ProgramData%\CalmOS,只有 Administrators / SYSTEM 可写,普通用户只读可执行(防篡改:下载者 = 执行者 ≠ 写入者)。 - 用进程级
RemoteSigned,不往 trusted publishers 里加东西。组织强制的AllSigned仍可能弹窗。
这比"官网下载 exe 双击"其实更严格一些,但本质承诺是:你信任 Microsoft 的这个签名。第三方 fork 的 aka.ms 链接不是一回事。
八、贡献者必读:仓库有两棵树
这条如果不提前知道,PR 一定会被打回。
规则:
- 改
src/,别改顶层那三个目录。 CI 跑的是未签名的src/副本 —— 这是刻意的:CI 测贡献者改的东西,签名是发布时才关心的事。 - 两份副本不会字节相同。 签名副本带
# SIG # Begin signature block块;块以上的正文应与src/一致,但在"src 改动进 main"到"下一次签名周期"之间会短暂不一致。 - 删除文件是唯一必须动两棵树的情况。 签名流水线只增改、不删除 —— 从
src/删掉的文件会永远留在顶层。所以删或重命名时,同一个 PR 里必须git rm两处。 - 有个
Signed copy guardPR 检查(.github/workflows/signed-copy-guard.yml),只在 PR 触及那三个顶层目录时运行,用src/tools/check-signed-drift.ps1做比较器判定漂移,漂移即 fail。
提交前可以跑这几个静态检查(不动机器状态):
# PowerShell 语法检查(不执行)
Get-ChildItem -Recurse -Filter *.ps1 | ForEach-Object {
$errs = $null
[void][System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$errs)
if ($errs) { Write-Error "$($_.FullName): $errs" } else { "OK: $($_.Name)" }
}
Invoke-ScriptAnalyzer -Recurse -Path ./Workloads, ./tests/_harness
python3 -c "import yaml; yaml.safe_load(open('manifest.yml'))"
跑单个 flow 的端到端测试就是 CI 做的事:
./tests/_harness/run-flow.ps1 -Id typescript `
-Build 'tsc tests/typescript/hello.ts' `
-Run 'node tests/typescript/hello.js' `
-Expected tests/typescript/expected.txt
# 期望输出尾部:"INSTALL_OK: typescript"
九、排障速查表
| 现象 | 原因与处理 |
|---|---|
Unrecognized command: configure | 跑 winget configure --enable;仍不行用 Workloads/_common/assert-winget-configure.ps1 判断是 App Installer 太旧、策略禁用还是别的原因 |
internal error / -2146233079 | 非提权环境缺 VC++ Redist,装对应架构的 Microsoft.VCRedist.2015+.x64 / .arm64 |
装成功了但 python / node 不在 PATH | 新开终端,或跑同名 install.ps1 shim 在当前会话刷新 PATH |
| Dev Config 重启后像卡住 | WindowsDevConfigResume 任务在登录后约 30 秒自动继续。等两分钟没动静就再跑一次原命令 —— 幂等,会跳过已完成的 |
某几步显示 flagged | best-effort 项未完成/未确认,多因依赖包刚装 PATH 还没注册(WinUI 模板需要 .NET SDK 在 PATH,Copilot 插件需要 Copilot CLI)。重跑即可,成功的秒过 |
wsl --install ... failed with exit code -1 | 硬件虚拟化没开。物理机进 BIOS/UEFI 开 VT-x / AMD-V;VM 在 Hyper-V 宿主上(客户机关机):Set-VMProcessor -VMName <VM_NAME> -ExposeVirtualizationExtensions $true |
| WSL Comfort 因缺 WSL 失败 | 在 Windows 侧跑 .\wsl-comfort\install.ps1,它会先装 WSL |
| “Setup is already running in another window” | 切到另一个窗口;确认没在跑说明上次进程没干净退出,注销再登录后重试 |
| “Windows Terminal settings 无法解析为 JSON” | settings.json 有语法错误,按提示修或重命名后重跑(脚本会保留原文件并跳过该步) |
| 下载失败/超时 | 多为代理。winget 和 WinHTTP 都要配代理:netsh winhttp show proxy |
| 都不适用 | 看日志 devconfig-log.txt(通常在 %ProgramData%\CalmOS),带着 winver、命令、相关片段去 Issues |
十、它适合你吗
适合
- 每次重装系统都要重来一遍装机的 Windows 开发者。
- 想要现成的现代化终端体验(Oh My Posh + Nerd Font + Starship),但懒得自己配。
- 需要 Repeatable 环境的场景:Dev Box、实验室镜像、CI runner 镜像、
src/里加一门语言就能批量铺开。 - 教学/布道:一行命令让学员拿到和讲师一致的環境。
不适合
- 想精细控制每一个选项的人 —— 它明确写着 opinionated。Full 会开 Sudo、远程桌面、改 Edge 策略,不是所有人都想要。
- 日常主力机直接跑 —— 卸载会把自己装的 Python/Node/Git 一并卸掉,还会删 Ubuntu。先在 Sandbox 或虚拟机里跑一遍。
- 离不开 zsh 全套自定义的人 —— WSL Comfort 会整块替换托管块内的 dotfiles 内容,块外的编辑没事,块内的会丢。
- 非 Ubuntu 用户 —— Comfort Shell 两半都只认 Ubuntu,其它发行版在 preflight 就被拒。
我的建议上手路径
- 建一个 Windows Sandbox 或 Hyper-V 一次性虚拟机(若是 Hyper-V 客户机,先开嵌套虚拟化)。
- 先跑
winget configure test --file .\Workloads\python\configuration.winget之类的 dry-check,只看不改,感受一下 DSC 资源的粒度。 - 再跑
.\Workloads\python\install.ps1,确认 shim 的 PATH 刷新行为。 - 最后再考虑
irm https://aka.ms/devconfig/standard/setup.ps1 | iex—— 记住会重启一次,会顺手帮你把 WSL + Ubuntu 装好。 - 觉得哪里不爽,去
src/里改 —— 这才是仓库希望你做的。
最后说句实在的:这类"官方装机脚本"最大的价值从来不是"省下那两小时",而是把一套经过验证的环境定义变成可版本控制、可 review、可 CI 测试的文本。今天它帮你装的是 Python 3.14 和 .NET 10,明天你往
manifest.yml里加一条,团队二十台机器就同步了。这才是 IaC(Infrastructure as Code)在个人开发机上的正确用法。