Repository navigation
Conversation
Git for Windows checks files out with CRLF line endings by default (core.autocrlf=true), which made the JSON document scanner reject every manifest and lockfile, since it didn't treat `\r` as whitespace. - The JSON scanner now follows the spec and accepts `\r` as whitespace; the content it injects into CRLF documents uses CRLF as well. - `DataDocument::update_document_field` normalizes CRLF documents to LF before editing them (the YAML editor only understands LF), then restores their original line endings.
Makes zpm and Yarn Switch build and run natively on Windows (x86_64-pc-windows-msvc). Paths - `Path` keeps a portable representation internally on Windows (drives are exposed as `/C:/…`, UNC shares as `/unc/server/share/…`), so the existing POSIX-style logic keeps working. Paths are converted at the OS boundary: `to_path_buf` returns native paths, `to_file_string` returns `C:/…` (valid everywhere, roundtrips), and `to_native_string` returns `C:\…` for environment variables. Native, verbatim (`\\?\`), and UNC inputs are all accepted when parsing. - Upward directory walks stop at the drive root instead of `\`. - Permission bits are no-ops on Windows; `HOME` falls back to `USERPROFILE`. Links - Directory links honor `winLinkType`: real symlinks (with relative targets) by default, falling back to NTFS junctions when the user lacks the privilege to create symlinks (Developer Mode disabled), or junctions when configured explicitly. - `node_modules/.bin` entries are `.cmd` + sh shims rather than symlinks, like npm does, since Windows ignores shebangs. Scripts - Scripts run through the bash shipped with Git for Windows (or any bash in the PATH except the WSL launcher), with its arguments quoted the way the MSYS runtime expects (it treats `\\` as an escaped backslash inside quotes). - The PATH shims are generated as both `.cmd` files and sh scripts, and programs are resolved through PATHEXT so `.cmd` tools (npm, pnpm, our shims) can be spawned. - Binaries with a shebang are run through their interpreter. - NODE_OPTIONS uses a `file://` URL for the ESM loader, and quotes paths containing spaces. Processes - Process liveness, tree termination (`taskkill /T`), and Ctrl-C delegation are implemented through windows-sys. - Standard handles aren't inherited implicitly anymore, so detached processes (the daemon) don't keep the caller's pipes open. - The daemon doesn't lock its project folder (Windows can't delete a process' working directory) and shuts down when the folder is removed. - The main thread reserves 8 MiB of stack, like on Linux (Windows defaults to 1 MiB, which deeply nested futures overflowed). Misc - Yarn Switch installs and runs `yarn-bin.exe`, skips the POSIX shell profiles on Windows, and `yarn switch update` uses the PowerShell installer. - The builtin Node.js provisioning supports the win-x64 and win-arm64 distributions.
- Build x86_64-pc-windows-msvc on windows-latest (natively, without cross) in the Build and Releases workflows; the release archive is generated with Compress-Archive since the runners lack `zip`, and the npm package declares `os: win32`. - Add install-script.ps1, the Windows counterpart of install-script.sh. It installs Yarn Switch into ~/.yarn/switch/bin and adds it to the user PATH through the registry, preserving REG_EXPAND_SZ entries. It's meant to be served as https://repo.yarnpkg.com/install.ps1, which is what `yarn switch update` fetches on Windows. - Document the Windows install and the Git Bash requirement.
Adds x86_64-pc-windows-msvc to the test matrix, using the Windows binaries built by the build job. The pinned Yarn release doesn't know about the Windows builds of the builtin Node.js and fails to install lockfiles listing them, so the committed lockfile can't include them yet; the Windows job refreshes the lockfile at install time instead, through a new `install-args` input of the prepare-node action. The job is also bounded to an hour, so that a hanging test can't hold a Windows runner for the default six.
❌ Deploy Preview for yarn-v6 failed. Why did it fail? →
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Release unlinks yarn without .exe
- Added the ext variable to the unlinkSync call so it correctly deletes yarn.exe on Windows instead of yarn, matching the subsequent renameSync and chmodSync operations.
Or push these changes by commenting:
@cursor push 5b517ba983
Preview (5b517ba983)
diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml
--- a/.github/workflows/releases.yml
+++ b/.github/workflows/releases.yml
@@ -184,7 +184,7 @@
const ext = includes(targetName, `windows`) ? `.exe` : ``;
// We don't need to distribute Yarn Switch itself
- fs.unlinkSync(path.join(destinationPath, `yarn`));
+ fs.unlinkSync(path.join(destinationPath, `yarn${ext}`));
// Rename the binary to `yarn`
fs.renameSync(path.join(destinationPath, `yarn-bin${ext}`), path.join(destinationPath, `yarn${ext}`));You can send follow-ups to the cloud agent here.
⏱️ Benchmark Resultsgatsby install-full-cold
📊 Raw benchmark data (gatsby install-full-cold)Base times: 3.785s, 3.832s, 3.789s, 3.875s, 3.847s, 3.791s, 3.732s, 3.887s, 3.857s, 3.891s, 3.785s, 3.919s, 3.741s, 3.843s, 3.722s, 3.785s, 3.725s, 3.712s, 3.697s, 3.725s, 3.697s, 3.741s, 3.744s, 3.745s, 3.748s, 3.637s, 3.679s, 3.715s, 3.768s, 3.713s Head times: 3.715s, 3.792s, 3.761s, 3.812s, 3.848s, 3.902s, 3.813s, 3.783s, 3.809s, 3.846s, 3.792s, 3.814s, 3.822s, 3.822s, 3.824s, 3.750s, 3.772s, 3.796s, 3.795s, 3.775s, 3.816s, 3.905s, 3.788s, 3.858s, 3.846s, 3.844s, 3.774s, 3.826s, 3.813s, 3.781s gatsby install-cache-only
📊 Raw benchmark data (gatsby install-cache-only)Base times: 1.070s, 1.119s, 1.067s, 1.075s, 1.100s, 1.067s, 1.148s, 1.147s, 1.144s, 1.090s, 1.112s, 1.079s, 1.121s, 1.093s, 1.107s, 1.116s, 1.108s, 1.097s, 1.084s, 1.075s, 1.094s, 1.384s, 1.193s, 1.074s, 1.084s, 1.104s, 1.097s, 1.101s, 1.096s, 1.111s Head times: 1.135s, 1.166s, 1.114s, 1.150s, 1.095s, 1.098s, 1.105s, 1.125s, 1.115s, 1.115s, 1.113s, 1.111s, 1.118s, 1.137s, 1.069s, 1.079s, 1.087s, 1.098s, 1.164s, 1.094s, 1.155s, 1.697s, 1.607s, 1.480s, 1.168s, 1.111s, 1.095s, 1.105s, 1.102s, 1.126s gatsby install-cache-and-lock (warm, with lockfile)
📊 Raw benchmark data (gatsby install-cache-and-lock (warm, with lockfile))Base times: 0.288s, 0.285s, 0.280s, 0.273s, 0.273s, 0.267s, 0.267s, 0.283s, 0.284s, 0.289s, 0.284s, 0.283s, 0.278s, 0.321s, 0.306s, 0.282s, 0.290s, 0.290s, 0.290s, 0.283s, 0.289s, 0.289s, 0.289s, 0.291s, 0.298s, 0.293s, 0.290s, 0.295s, 0.300s, 0.312s Head times: 0.299s, 0.297s, 0.296s, 0.301s, 0.298s, 0.298s, 0.296s, 0.300s, 0.299s, 0.303s, 0.676s, 0.514s, 0.505s, 0.773s, 0.300s, 0.297s, 0.300s, 0.293s, 0.295s, 0.289s, 0.298s, 0.299s, 0.300s, 0.291s, 0.288s, 0.285s, 0.291s, 0.286s, 0.286s, 0.293s |
The import was dropped from the top of the file when the OsStr conversions became portable, but `fs_clonefile` still relies on it on macOS.
Lockfiles checked out on Windows use CRLF line endings by default (core.autocrlf=true). Since the lockfile was always serialized with LF, immutable installs (and hardened mode, which enables them on public pull requests) reported an unmodified lockfile as changed, and regular installs rewrote it on every run. The existing line endings are now kept. This commit also adds the Windows variants of the builtin Node.js to the lockfile, which the Windows CI jobs need; the Windows test job doesn't have to refresh the lockfile anymore. Note that the Yarn release pinned by `packageManager` can't install this lockfile, as it doesn't know these variants; a follow-up commit will point it to a build of this branch.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Lockfile breaks pinned Yarn installs
- Removed Windows Node variants from yarn.lock and restored --refresh-lockfile flag for Windows CI until packageManager is upgraded to a version supporting these variants.
Or push these changes by commenting:
@cursor push 0ac3b74969
Preview (0ac3b74969)
diff --git a/.github/actions/prepare-node/action.yml b/.github/actions/prepare-node/action.yml
--- a/.github/actions/prepare-node/action.yml
+++ b/.github/actions/prepare-node/action.yml
@@ -8,6 +8,10 @@
switch:
description: 'Path to a locally built yarn-switch binary to use in place of the released one'
required: false
+ install-args:
+ description: 'Extra arguments to pass to yarn install'
+ required: false
+ default: ''
runs:
using: composite
@@ -42,4 +46,4 @@
- name: Install dependencies
shell: bash
run: |
- yarn install
+ yarn install ${{inputs.install-args}}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -98,6 +98,9 @@
- target: x86_64-pc-windows-msvc
os: windows-latest
ext: .exe
+ # The lockfile can't list the Windows builds of the builtin Node.js
+ # yet, as the Yarn release we pin doesn't know about them
+ install-args: --refresh-lockfile
timeout: 60
name: "Testing ${{matrix.target}}"
@@ -122,6 +125,7 @@
with:
link: artifacts/yarn-bin${{matrix.ext}}
switch: artifacts/yarn${{matrix.ext}}
+ install-args: ${{matrix.install-args}}
- name: Generate the test report
run: |
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -6781,9 +6781,7 @@
"@yarnpkg/node-linux-x64@builtin:24.12.0",
"@yarnpkg/node-linux-arm64@builtin:24.12.0",
"@yarnpkg/node-darwin-x64@builtin:24.12.0",
- "@yarnpkg/node-darwin-arm64@builtin:24.12.0",
- "@yarnpkg/node-win-x64@builtin:24.12.0",
- "@yarnpkg/node-win-arm64@builtin:24.12.0"
+ "@yarnpkg/node-darwin-arm64@builtin:24.12.0"
]
}
},
@@ -6847,36 +6845,6 @@
}
}
},
- "@yarnpkg/node-win-arm64@builtin:24.12.0": {
- "checksum": null,
- "resolution": {
- "resolution": "@yarnpkg/node-win-arm64@builtin:24.12.0",
- "version": "24.12.0",
- "requirements": {
- "cpu": [
- "arm64"
- ],
- "os": [
- "win32"
- ]
- }
- }
- },
- "@yarnpkg/node-win-x64@builtin:24.12.0": {
- "checksum": null,
- "resolution": {
- "resolution": "@yarnpkg/node-win-x64@builtin:24.12.0",
- "version": "24.12.0",
- "requirements": {
- "cpu": [
- "x64"
- ],
- "os": [
- "win32"
- ]
- }
- }
- },
"@yarnpkg/parsers@npm:^3.0.3": {
"checksum": "df317636a26d25b2c9ee6829252dc20b4d978cf20de08be59439e4e8ed8afad240fca0d4d091dd9e89eb786bea49d6ac1b241ecfdc892c160c7a735388d1683a",
"resolution": {You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit d42d00b. Configure here.
| "@yarnpkg/node-darwin-arm64@builtin:24.12.0" | ||
| "@yarnpkg/node-darwin-arm64@builtin:24.12.0", | ||
| "@yarnpkg/node-win-x64@builtin:24.12.0", | ||
| "@yarnpkg/node-win-arm64@builtin:24.12.0" |
There was a problem hiding this comment.
Lockfile breaks pinned Yarn installs
High Severity
The lockfile now lists @yarnpkg/node-win-x64 and @yarnpkg/node-win-arm64, while this repo still pins yarn@6.0.0-rc.20. That release treats unknown builtin variants as Unsupported code path instead of skipping them, so yarn install fails for the pinned manager. Dropping --refresh-lockfile also means CI jobs that use setup-action rather than the freshly built binary hit the same failure.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit d42d00b. Configure here.
Windows runners default to PowerShell, which doesn't understand the step's script. The test matrix also stops failing fast, so that a failure on one platform doesn't cancel the report of the others.



This PR makes zpm and Yarn Switch build and run natively on Windows (
x86_64-pc-windows-msvc), and adds Windows to the CI build, release, and test matrices.What changes
Paths. On Windows,
Pathkeeps a portable representation internally (C:\foois stored as/C:/foo, UNC shares as/unc/server/share), so all the existing POSIX-style logic (is_absolute,relative_to, joins, …) keeps working unchanged. Conversions happen at the boundaries:to_path_buf()returns native paths for the std APIs;to_file_string()returnsC:/foo(accepted by Win32, Node, and Git Bash, and roundtrips throughfrom_file_string);to_native_string()returnsC:\foo, used for env variables (PROJECT_CWD,INIT_CWD,npm_package_json,PATHentries, …);\\?\C:\…, as returned bycanonicalize), and UNC paths.Upward directory walks stop at the drive root rather than visiting
\, permission bits are no-ops, andHOMEfalls back toUSERPROFILE.Links.
winLinkType(which existed but wasn't read) is now honored: directory links are real symlinks with relative targets by default, falling back to NTFS junctions when the user can't create symlinks (Developer Mode disabled), or junctions when configured explicitly.node_modules/.binentries are.cmd+ sh shims, like npm does, since Windows ignores shebangs.Scripts. Scripts run through the bash shipped with Git for Windows (or any
bash.exein thePATH, except the WSL launcher in System32), with a clear error when none is found. A few things needed care:\\as an escaped backslash inside quotes), so arguments given to bash are quoted accordingly — otherwise any script containing\\got corrupted;.cmdfiles (for cmd, PowerShell, andchild_processwithshell: true) and sh scripts (for Git Bash);PATHEXT, since Rust only looks for.exefiles (which missed npm, pnpm, and our shims);file://URL (Node rejectsC:as a URL scheme), andNODE_OPTIONSpaths are quoted when they contain spaces.Processes.
taskkill /T), and Ctrl-C delegation (SetConsoleCtrlHandler) throughwindows-sys; this replaces the previouswinapicode, which was never compiled.child_process.execFile, CI runners, IDE tasks) hung until the daemon died.yarn workspace <name> <cmd>overflowed.CRLF. Git for Windows checks files out with CRLF by default, which the JSON document scanner rejected (it didn't treat
\ras whitespace, contrary to the spec). Edits now preserve CRLF line endings in both JSON and YAML documents.Yarn Switch. Uses
yarn-bin.exe, skips the POSIX shell profiles inpostinstall(aC:/…entry would break Git Bash'sPATH), andyarn switch updateruns the new PowerShell installer.install-script.ps1installs into~/.yarn/switch/binand adds it to the userPATHthrough the registry (preservingREG_EXPAND_SZentries).Builtin Node.js. Adds the
win-x64andwin-arm64variants, fetched from the.zipdistributions.CI. Builds and releases
x86_64-pc-windows-msvc(natively with cargo, archived withCompress-Archive, published to npm withos: win32), and runs the acceptance tests onwindows-latest.Things to be aware of
install-script.ps1must be served ashttps://repo.yarnpkg.com/install.ps1. That's the URL used by the install docs and byyarn switch updateon Windows.yarn.lockisn't updated here and why the Windows test job refreshes it at install time (install-args: --refresh-lockfile). Making unknown variants non-fatal would prevent this from happening again when other platforms get added.C:/…rather than Berry's/C:/…(e.g.portal:resolutions added byyarn link). Both are parsed correctly; the native form is the one other tools understand.Testing
All of this was exercised on Windows 11 with Git for Windows:
yarncalls, builtin Node.js, Yarn Switch (link, proxying,postinstall), the daemon, and tasks;pwdprinting/tmp/…paths, theportal:/C:/…and backslash spellings,.binentries being symlinks, executable bits, POSIX signals and process groups, the fakeyarn-binbash script served by the test registry, the OS error message for missing files, a multiline template literal picking up the CRLF line endings of its test file, and a checksum snapshot of tarballs generated on Windows;cargo testpasses for the touched crates on Windows, except for threepath_iteratorstests that fail on every platform already.Note
Medium Risk
Broad platform port touching path resolution, linkers, script spawning, and daemon lifecycle; regressions could affect installs and CI on all OSes, though changes are largely gated behind Windows cfg.
Overview
Adds native Windows support for Yarn Switch and zpm (
x86_64-pc-windows-msvc), including CI build/test/release matrix entries, a PowerShell installer (install-script.ps1), and npm packages taggedos: win32.Paths and I/O:
Pathstores a portable form on Windows (/C:/…,/unc/…) while exposingto_native_string()/to_path_buf()for subprocesses and env vars; link creation honorswinLinkType(symlinks with junction fallback) viafs_symlink_withandLinkType.Install/linking/scripts: Node-modules
.binentries become.cmd+ shell shims; package scripts run through Git Bash (or PATHbash), with PATHEXT-aware program resolution, shebang handling, and MSYS-safe quoting. JSON/YAML editors and lockfile writes preserve CRLF on Windows.Processes: Daemon/switch changes cover hidden detached children,
taskkilltrees, Ctrl-C handling, non-inherited std handles, larger main-thread stack, and project-folder lifecycle checks without Unix inodes.Other: Builtin Node adds
win-x64/win-arm64zip dists; formats/sync/linker code drops Unix-only permission APIs in favor of cross-platform helpers.Reviewed by Cursor Bugbot for commit 6d21abc. Bugbot is set up for automated code reviews on this repo. Configure here.