Skip to content

Commit 1bfe532

Browse files
Fix Windows (yarn berry CRLF support) and first-run real-PM CI failures after #251 (#253)
* test(hosted): match the allow-remote env var name case-insensitively On Windows env var names are case-insensitive: run_isolated blanks NPM_CONFIG_ALLOW_REMOTE before the test sets npm_config_allow_remote=none, so the child sees a single variable under the first spelling and the warning (which names the variable as the OS reports it) says NPM_CONFIG_ALLOW_REMOTE=none. The product is right; the assertion was POSIX-only. Fixes outer_npm_config_layers_are_respected on test (windows-latest) after #251. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: run the cargo matrix toolchain install under bash on every OS The cargo-vex-matrix windows-latest leg used the default pwsh shell, where "$CARGO_TEST_TOOLCHAIN" is an unset PowerShell variable, so it ran `rustup toolchain install ""` and failed. The leg was skipped on #251's own CI, so this is its first real run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e_vex_build): poetry vendored capstone asserts the manifest-free ledger The poetry vendored capstone (added in #251, written before its rebase onto #247) still asserted `scan --vendor` writes `.socket/manifest.json`. Since #247 vendored mode is manifest-free (CLI_CONTRACT `scan --vendor`): the run writes only `.socket/vendor/**`, and each ledger entry is `detached: true` with the patch record embedded. The assertion therefore failed on every Poetry release in the CI matrix the first time the leg actually ran (#253). Assert the contract instead: no manifest is written, and the ledger entry for the vendored uuid is detached and embeds its record. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): pin enableImmutableInstalls off so fixture installs work under CI yarn 3 and later turn enableImmutableInstalls on by default when ci-info detects CI (CI / GITHUB_ACTIONS). With that default, the plain `yarn install` that writes each fixture's first yarn.lock fails with YN0028 ("The lockfile would have been created by this install, which is explicitly forbidden"): exit 1, nothing on stderr. Every hosted, vendored, pnpm-linker, workspaces and yarn-3 refusal fixture on the new yarn-berry legs (4.0.2, 4.1.0, 4.6.0, 4.12.0 ubuntu+macOS, 4.18.0) failed that way. The yarn 2 refusal legs passed because yarn 2 keeps the default off. The suites were never run under CI before #251 added the legs; the main test job soft-skips them. yarn_berry_common::pin_berry_ci_defaults now sets CI=true (local runs get the runner's defaults) and YARN_ENABLE_IMMUTABLE_INSTALLS=false. It is applied in every berry suite's corepack helper, after the YARN_* scrub and cache_env::isolate. The fresh-checkout installs still pass --immutable explicitly, and yarn's flag outranks the setting, so lock enforcement is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): spawn corepack.cmd on Windows Node ships corepack on Windows as the corepack.cmd batch shim, and Command::new("corepack") only resolves corepack.exe. On the windows-latest yarn-berry leg every suite's availability probe therefore reported "`corepack yarn@4.12.0` unavailable" (yarn@2.4.3 / 3.8.7 in the refusal suite), and SOCKET_PATCH_YARN_E2E_REQUIRED=1 turned each of those into a failure. yarn_berry_common::corepack_command() picks the spawnable name. Every berry suite's has_corepack_pm probe and corepack helper uses it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): show yarn's stdout when a fixture install fails yarn berry prints its errors (YN0028, YN0018, …) on stdout and leaves stderr empty. The fixture and bootstrap skip messages printed only stderr, so all 11 failures on each yarn-berry CI leg read "fixture `yarn install` failed (registry unreachable?):" followed by nothing. The real cause was YN0028 under CI's implicit immutable default. yarn_berry_common::yarn_output formats both streams, and every berry fixture/bootstrap skip now uses it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: select the pinned bundler with BUNDLER_VERSION on every bundler leg The Ruby 3.1 legs pinned to bundler 2.1.4 and 2.2.33 never ran those bundlers. setup-ruby's `bundler:` input only `gem install`s the release; with no lockfile to read, RubyGems' `bundle` binstub then activates the HIGHEST installed bundler, which on Ruby 3.1.7 is its default gem 2.3.27. tests/common/bundler_e2e.rs correctly panicked on every test ("bundle on PATH is 2.3.27"). The 1.17.3 legs passed only because the Bundler 1.x step already exported BUNDLER_VERSION. Export BUNDLER_VERSION for every pinned-bundler leg (a new step after the 1.x install), which makes the binstub pick exactly the pinned release in every process and also turns off bundler >= 2.3's lockfile-driven self-switch. Verified locally in Docker (Ruby 3.1.7 / 3.3.10 / 3.4.9, setup-ruby layout): all 16 e2e_{redirect,vendor}_gem_build legs and the setup_matrix_gem leg green with the export; the 2.1.4/2.2.33 legs fail exactly as in CI without it. The harness panic now names the fix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(vex/deno): stop expecting a manifest from a refused vendored scan The real-deno negative capstone asserted that `scan --mode vendored` "left a manifest" for the patch it could not wire, then checked that the unapplied manifest attests nothing. Vendored mode is manifest-free: its download phase is detached (download_patch_records_with writes nothing; the vendor ledger alone carries records), and a vendor step refused with vendor_lockfile_missing records nothing. So the assertion failed on the first CI run of both deno legs (1.46.3, 2.9.7) at deno.rs:454. Assert the real contract instead: the download is detached, no ledger entry names the package, and there is nothing to attest (no manifest, exit 2 manifest_not_found, zero patch-API requests). The "unapplied manifest patch attests nothing" check moves to step 4, where the test stages the manifest itself, before `apply --vex` runs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e_vex_build): compare hatch env paths canonically on macOS The macOS hatch 1.18.1 leg failed all four cases with `".../private/var/.../six.py" outside "/var/.../app"`. The hatch bootstrap venv is built on the runner's actions/setup-python CPython, a macOS framework build, and a framework interpreter realpaths `sys.prefix` — so `six.__file__` names `/private/var/folders/...` while `hatch env find` echoes the `/var/folders/...` spelling of the same temp dir. Linux legs (and a uv-managed standalone Python locally) keep one spelling, which is why only the macOS leg tripped. Canonicalize both sides for the containment check only; the env dir handed on as VIRTUAL_ENV is unchanged. Reproduced locally by bootstrapping hatch 1.18.1 on Homebrew's framework CPython (4/4 fail before, 4/4 pass after). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(maven e2e): scrub Maven 4's CI markers so a runner logs like a laptop The ubuntu Maven 4.0.0-rc-6 leg of e2e_vendor_maven_build failed at the TAMPER probe: Maven rejected the tampered file:// jar and built Central's pristine one (the load-bearing assertion held), but the output carried no "checksum" line, so "the file:// copy was rejected on its checksum" tripped. The CI dump shows no transfer lines at all, not even the Central download that must have happened after the purge. Root cause: Maven 4's CIDetectors (generic CI, GITHUB_ACTIONS, CIRCLECI, Jenkins WORKSPACE, TEAMCITY_VERSION, TRAVIS) make MavenInvoker pick the QuietMavenTransferListener whenever a CI is detected and --force-interactive is absent, even under -B. That listener drops both "Downloading from ..." and the "Checksum validation failed" warning. Maven 3 has no such detection, so only the 4.x legs log differently on a GitHub runner (and only this probe greps a warning a successful build prints; the redirect suite's checksum greps are on failed builds, whose exception text survives the quiet listener). Scrub those markers (plus the Maven config vars run() already dropped) in one mvn_command() used by both detect() and run(). --force-interactive was rejected: it flips the run interactive (progress-bar listener) and Maven 3 refuses the flag. The checksum assertion is unchanged. Repro: CI=true GITHUB_ACTIONS=true on the unfixed tree reproduces the CI panic at e2e_vendor_maven_build.rs:353 locally; with the fix both e2e_vendor_maven_build and e2e_redirect_maven_build pass under the same env on Maven 4.0.0-rc-6 and 3.9.16. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e_nuget_dotnet_build): serialize dotnet spawns around a .NET 9 PAL race The ubuntu SDK 9 leg failed nuget_vendored_dotnet_restore_then_manifestless_vex at its first fixture restore (the hosted test passed): System.IO.IOException: The system cannot open the device or file specified. : 'NuGet-Migrations'. One or more system calls failed: mkdir("/tmp/.dotnet/shm/session2027", AllUsers_ReadWriteExecute) == -1; errno == EEXIST; at System.Threading.Mutex..ctor ... at NuGet.Common.Migrations.MigrationRunner.Run ... at Microsoft.DotNet.Configurer.DotnetFirstTimeUseConfigurer.Configure() Both tests run in parallel, each with a fresh HOME, so each first `dotnet restore` runs the first-use NuGet migrations under the named mutex `NuGet-Migrations`. On a fresh runner `/tmp/.dotnet` does not exist yet, and the .NET 9 runtime's named-mutex setup races when two processes create the shared-memory tree at once: the loser's session directory mkdir fails with EEXIST. Environment/tool race (SDK 9 PAL), exposed by the harness running two SDK processes concurrently. Reproduced in mcr.microsoft.com/dotnet/sdk:9.0 (9.0.318, the leg's SDK) with two concurrent first-run CLI commands per round, fresh HOMEs, `/tmp/.dotnet` wiped before each of 60 rounds: 9, 0, 2 and 7 of 120 processes died with the exact CI message across four batches; 0/120 with the root pre-created and 0/40 rounds run one at a time. Hold one binary-wide lock around every `dotnet` spawn (the --version probe and every restore). Only the SDK phases serialize; the socket-patch runs between them stay parallel. No assertion changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): pin hardened mode off so PR runs install from the lock yarn 4 enables hardened mode when it detects a GitHub Actions run for a public pull request and then re-resolves every lock entry against the registry. The fresh-checkout installs point the registry at an unreachable address on purpose, so the hosted berry suites failed with ECONNREFUSED 127.0.0.1:1 on PR runs only (seen in the coverage job once the fixture installs stopped failing). Reproduced locally with a simulated public-PR event: 3 failures without the pin, 11/11 with it. --immutable --check-cache still verifies every checksum. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): pin hardened mode off only where yarn has the setting The previous commit pinned YARN_ENABLE_HARDENED_MODE=false for every berry suite, including e2e_yarn_legacy_cachekey_refusal_build, which drives yarn 2.4.3 and 3.8.7. Those releases predate hardened mode and refuse every command while the variable is set: Usage Error: Unrecognized or legacy configuration settings found: enableHardenedMode so all four refusal cells failed at their fixture install under SOCKET_PATCH_YARN_E2E_REQUIRED=1 — how the yarn-berry-e2e job runs the suite — and soft-skipped everywhere else. pin_berry_ci_defaults now takes the yarn spec and pins hardened mode off for yarn 4+ only, removing the variable for yarn 2/3. Reproduced locally: 4/4 cells fail with the Usage Error before, 4/4 pass after. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(yarn-berry): redirect and vendor CRLF (Windows) berry files byte-exactly yarn berry writes a file it creates with the OS line ending and keeps an existing file's majority ending on every later write (normalizeLineEndings in yarnpkg-fslib FakeFS.ts, called by Project.persistLockfile and, through changeFilePromise's automaticNewlines, Workspace.persistManifest; the same rule from 2.4.3 through 4.18.0). On Windows a fresh yarn.lock is therefore CRLF, and so is the package.json yarn first pretty-prints; a core.autocrlf checkout produces the same on any OS. Once the Windows yarn-berry suites ran (corepack.cmd), both modes failed on those files: - hosted: rewrite_yarn_berry refused every CRLF lock (redirect_yarn_berry_crlf_unsupported, redirected 0); - vendored: package.json was re-serialized LF on both the wiring and the revert, so `vendor --revert` never restored the CRLF manifest byte-for-byte. Hosted: the rewriter works on the LF-normalized lock and re-expands its output (utils::line_endings), keeps a leading BOM, and records the lock's on-disk CRLF fragments in the ledger, so the per-purl takeover and the whole-ledger replay restore them byte-exactly. Both replays now also match yarn blocks respelled in the live lock's ending when a checkout flipped its uniform ending since the redirect (the committed ledger keeps its fragments verbatim). Vendored: package.json is re-serialized in its own layout (vendor::common::JsonLayout: BOM, indent, line ending, trailing-newline shape) and parsed past a BOM, and lock entries are spliced in the terminator of the block they replace (yarn_classic_lock::block_eol, also used by the shared revert, so a lock whose endings were mixed after vendoring keeps every other line as it was). A yarn.lock or package.json that mixes CRLF and LF, or holds a bare CR, has no single ending to keep and fails yarn's own `--immutable` check (YN0028): both modes refuse it before any write (redirect_yarn_berry_mixed_line_endings / vendor_yarn_berry_mixed_line_endings) with `yarn install` as the remedy. Reverts never refuse on line endings. Readers: is_berry_lock, the vendor flavor sniff, repair's sniff, scan_blocks and the .yarnrc.yml compressionLevel read skip a leading BOM (a header-less `__metadata:` lock is berry; a BOM'd yarnrc's first-line knob is no longer read as unset). The lock inventory and manifest-less VEX already split CRLF lines; unit tests pin both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): run every berry shape on CRLF, BOM and mixed files Hermetic CLI coverage of the Windows file shapes: `scan --mode hosted` over CRLF and BOM + CRLF locks (every line kept CRLF, the ledger's fragments the on-disk CRLF bytes, re-run a no-op, `rollback` restoring the pristine lock byte-for-byte) and its mixed-ending refusal; `vendor` + `vendor --revert` over CRLF (+ BOM) package.json / yarn.lock pairs (byte-exact round trip) and the mixed-ending failed event; both mode takeovers on CRLF files (hosted -> vendored -> revert, vendored -> hosted -> rollback, BOM kept); and a manifest-less VEX cell over a CRLF + BOM vendored lock and manifest. Real yarn: SOCKET_PATCH_YARN_BERRY_EOL=crlf respells the files each fixture's first `yarn install` wrote CRLF — what yarn itself writes on Windows (a new lockfile and a freshly pretty-printed manifest get os.EOL) — and yarn keeps them CRLF on every later write, so the hosted, vendored, pnpm-linker, workspaces, legacy-refusal and mode-migration suites run on CRLF files on macOS / Linux as they do on windows-latest. Every fixture prints `BERRY-EOL|<yarn>|<flow>|<file>|yarn=…|flow=…`, the ending yarn wrote and the one the flow ran on. Against the pre-fix code this mode reproduces both Windows CI failures (redirect_yarn_berry_crlf_unsupported on the hosted suites; "revert must restore package.json byte-identical" on the vendored ones); with the fix, yarn 4.12.0 passes all five suites in both modes (103 VEX-MATRIX cells each) and both mode takeovers. Also drops a doubled doc-comment line in yarn_berry_common. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: yarn berry line endings in the contract, changelog and a compatibility page CLI_CONTRACT: the hosted redirect keeps a CRLF lock's own line ending and BOM and records on-disk fragments; a mixed-ending lock is refused with redirect_yarn_berry_mixed_line_endings, which replaces v4's redirect_yarn_berry_crlf_unsupported (no longer emitted); the vendored yarn berry row keeps both files' layout, with the new vendor_yarn_berry_mixed_line_endings refusal in the code table; the per-purl revert and whole-ledger replay respell yarn blocks across a uniform LF <-> CRLF checkout flip. CHANGELOG [Unreleased]: the fix (hosted + vendored CRLF support, BOM tolerance) under Fixed, the two refusal codes and the CRLF test mode under Added. docs/testing/yarn-berry-compatibility.md (new): supported releases, the CI matrix, how yarn berry chooses line endings — cited to FakeFS.ts, Project.ts, Workspace.ts, Manifest.ts and syml.ts at @yarnpkg/cli/4.12.0 — the git autocrlf paths to CRLF, socket-patch's contract per mode, and how to run the suites locally in CRLF mode. docs/ecosystems.md links it from the yarn berry notes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(setup): keep a CRLF / BOM package.json's layout through setup and --remove `setup` re-serialized package.json through `serialize_json`, which always emits bare LF and never writes a BOM back. On a Windows yarn berry project (persistManifest pretty-prints the manifest with os.EOL) a two-key script edit became a whole-file CRLF -> LF diff that yarn then keeps (it follows the majority ending), and `setup --remove` could never land byte-identical on the pre-setup file. Render through the vendored backends' `JsonLayout` instead (BOM, indent, line ending, trailing-newline shape). The two BOM tests now assert the BOM survives; a new round-trip test covers LF, CRLF, BOM+CRLF, BOM+LF, no final newline and two final newlines, asserting setup keeps each shape and setup --remove restores the original bytes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(yarn-berry): run the new mode's berry gates before a takeover reverts the old one The reverts keep line endings as they are (a vendored or hosted revert never refuses on them, so a lock mixed after wiring stays mixed), while the forward hosted rewriter and vendored backend refuse a mixed file. Neither takeover checked first: - `scan`/`get --mode hosted` over a vendored berry purl reverted its wiring, ledger entry and artifact (`redirect_takeover_reverted_vendored`: "now fully hosted"), then the rewriter refused the mixed lock - `redirected: 0`, and the next `yarn install` pulled the unpatched registry package. - `vendor` / `scan --mode vendored` over a hosted berry purl reverted the hosted edits and dropped the redirect-ledger record (`vendor_takeover_reverted_redirect`), then failed `vendor_yarn_berry_mixed_line_endings`. Extract the rewriter's project gates into `redirect::preflight_yarn_berry_hosted` (mixed endings, cacheKey, `.yarnrc.yml` compressionLevel) and the backend's into `vendor::yarn_berry_vendor_preflight` (both files' endings, cacheKey, compressionLevel; berry flavor only), and run each before the matching takeover revert, mirroring the bun preflights - wet and --dry-run alike. A refused purl keeps the old mode's wiring byte-identical and is skipped / failed with the new mode's code. Tests: a hermetic in_process_vendor test drives both directions (mixed lock, mixed package.json, compressionLevel 9; wet and dry-run) and asserts the wiring snapshot is unchanged and no takeover is announced (fails on the pre-fix code in both directions); core unit tests pin that each preflight matches its forward gate's code and detail. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(yarn-berry): serialize yarn spawns on Windows around a shared-cache rename race The berry suites' parallel tests share one yarn cache folder. Two yarn processes fetching the same package both rename a .tmp over the cache zip, and on Windows the loser fails with EPERM while the winner holds the file (windows-latest yarn-berry 4.12.0: e2e_yarn4_workspaces_build hosted test, EPERM rename left-pad-npm-1.3.0-....zip-....tmp). A static lock in yarn_berry_common serializes yarn processes on Windows only (Unix rename-over is atomic), mirroring the DOTNET_SPAWN fix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: strip in-process env toggles unconditionally before spawning the CLI Test binaries that mix in-process command runs with spawned CLI runs raced: the in-process runs call apply_env_toggles, which std::env::set_var's SOCKET_OFFLINE / SOCKET_DEBUG / SOCKET_API_URL / SOCKET_PROXY_URL on the shared test process, and the spawn helpers only removed SOCKET_* vars that existed when they scanned the environment. A toggle set by a parallel test between that scan and the spawn was inherited. On test-release this made in_process_vendor's berry takeover test run its hosted scan offline ("cannot run with --offline/SOCKET_OFFLINE"). The helpers in all eight such binaries now remove those keys unconditionally; Command applies the removals to the environment captured at spawn time. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 872b591 commit 1bfe532

44 files changed

Lines changed: 3129 additions & 233 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,19 +1123,34 @@ jobs:
11231123
ruby-version: ${{ matrix.ruby || '3.2.10' }}
11241124
# The legs pin their bundler (`bundler:`) so a capstone never rides
11251125
# whatever bundler the runner's Ruby ships; bundler 1.x is not
1126-
# installable through setup-ruby, see the next step. e2e_gem keeps
1126+
# installable through setup-ruby, see the next steps. e2e_gem keeps
11271127
# the 2.5 floor.
11281128
bundler: ${{ startsWith(matrix.bundler, '1.') && 'none' || matrix.bundler || '2.5' }}
11291129
bundler-cache: false
11301130

11311131
- name: Install Bundler 1.x
11321132
if: startsWith(matrix.bundler, '1.')
11331133
shell: bash
1134+
env:
1135+
BUNDLER_TEST_VERSION: ${{ matrix.bundler }}
1136+
run: gem install bundler -v "$BUNDLER_TEST_VERSION" --no-document
1137+
1138+
# Installing a bundler does not make `bundle` run it: with no lockfile
1139+
# to read, RubyGems' binstub activates the HIGHEST installed bundler,
1140+
# so a leg pinned BELOW its Ruby's default gem (2.1.4 / 2.2.33 on Ruby
1141+
# 3.1, whose default is 2.3.27) silently runs the default instead and
1142+
# tests/common/bundler_e2e.rs rightly panics. BUNDLER_VERSION makes
1143+
# the binstub select exactly the pinned release in every process (and
1144+
# turns off bundler >= 2.3's lockfile-driven self-switch), the same
1145+
# knob tests/docker/Dockerfile.gem-b1 sets.
1146+
- name: Select the pinned Bundler
1147+
if: matrix.bundler != ''
1148+
shell: bash
11341149
env:
11351150
BUNDLER_TEST_VERSION: ${{ matrix.bundler }}
11361151
run: |
1137-
gem install bundler -v "$BUNDLER_TEST_VERSION" --no-document
11381152
echo "BUNDLER_VERSION=$BUNDLER_TEST_VERSION" >> "$GITHUB_ENV"
1153+
BUNDLER_VERSION="$BUNDLER_TEST_VERSION" bundle --version
11391154
11401155
- name: Setup PHP
11411156
if: matrix.composer != ''
@@ -1420,6 +1435,10 @@ jobs:
14201435
- name: Install Rust
14211436
run: rustup show
14221437
- name: Install the cargo under test
1438+
# bash, not the Windows default pwsh: in PowerShell
1439+
# "$CARGO_TEST_TOOLCHAIN" is an (unset) PowerShell variable, so the
1440+
# windows-latest leg ran `rustup toolchain install ""`.
1441+
shell: bash
14231442
env:
14241443
CARGO_TEST_TOOLCHAIN: ${{ matrix.toolchain }}
14251444
run: rustup toolchain install "$CARGO_TEST_TOOLCHAIN" --profile minimal

‎CHANGELOG.md‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ into the new version's section — see docs/releasing.md.
150150

151151
### Added
152152

153+
- **`redirect_yarn_berry_mixed_line_endings` and
154+
`vendor_yarn_berry_mixed_line_endings`.** A `yarn.lock` (or, vendored, a
155+
root `package.json`) that mixes CRLF and LF line endings — or holds a bare
156+
CR — has no single ending to keep, and yarn itself rejects such a lock
157+
under `--immutable` (YN0028) and rewrites it wholesale on its next plain
158+
install. Both modes now refuse it before any write with a code naming the
159+
line endings and the `yarn install` remedy; a revert never refuses on line
160+
endings (a restored lock entry takes the terminator of the entry it
161+
replaces). The real-yarn berry suites gained `SOCKET_PATCH_YARN_BERRY_EOL=crlf`
162+
to run on CRLF files on macOS / Linux as yarn writes them on Windows, and
163+
print a `BERRY-EOL|<yarn>|<flow>|<file>|yarn=…|flow=…` line per fixture
164+
file — see [yarn berry compatibility](docs/testing/yarn-berry-compatibility.md).
153165
- **Hosted npm redirects configure npm 12's `allow-remote` for you.** npm 12
154166
defaults to `allow-remote=none` and refuses (EALLOWREMOTE) a lock that
155167
resolves patched packages from the Socket patch host. When `scan --mode
@@ -559,6 +571,49 @@ into the new version's section — see docs/releasing.md.
559571
proxy, sending private module paths off the machine. It is now refused
560572
(`vendor_fetch_unverifiable`, then the usual `package_not_installed`
561573
skip) unless `SOCKET_GOPROXY` names a proxy.
574+
- **yarn berry projects on Windows (CRLF files) are redirected and vendored
575+
instead of refused.** yarn berry (2.x–4.x) writes a file it creates with
576+
the OS line ending (`os.EOL`) and keeps an existing file's majority ending
577+
on every later write (`normalizeLineEndings` in yarnpkg-fslib's
578+
`FakeFS.ts`, used by `Project.persistLockfile` and
579+
`Workspace.persistManifest`) — so on Windows a fresh `yarn.lock` and the
580+
`package.json` yarn first pretty-prints are CRLF, and a `core.autocrlf`
581+
checkout makes them CRLF on any OS. `scan --mode hosted` / `get --mode
582+
hosted` refused every such lock (`redirect_yarn_berry_crlf_unsupported`,
583+
redirected 0); a CRLF lock is now rewritten in its own line ending — every
584+
untouched byte, a leading BOM included, round-trips — and the ledger records
585+
the lock's on-disk CRLF fragments, so `rollback`, `remove` and the hosted →
586+
vendored takeover restore it byte-for-byte (they also replay a ledger
587+
recorded on a checkout whose uniform line ending has since flipped, LF ↔
588+
CRLF). `vendor` / `scan --mode vendored` now keep `package.json`'s layout
589+
(BOM, indent, line ending, trailing-newline shape) on both the wiring and
590+
the revert: `vendor --revert` wrote a CRLF manifest back LF, never
591+
byte-identical to the pre-vendor file. A BOM'd `package.json` (and a
592+
BOM'd `.yarnrc.yml`, whose first-line `compressionLevel` was read as
593+
unset) no longer fails the vendored backend, and every berry reader skips
594+
a BOM in front of a header-less `__metadata:`. Verified on real yarn
595+
4.12.0 (hosted, vendored, workspaces, pnpm linker, both mode takeovers)
596+
with the fixtures re-spelled CRLF, and on yarn 2.4.3 / 3.8.7 (still
597+
refused for their cacheKey, never for their endings).
598+
- **A yarn berry mode takeover no longer strips the old mode's patch before
599+
the new mode refuses the project.** `scan` / `get --mode hosted` over a
600+
vendored berry purl reverted its vendored wiring, ledger entry and
601+
artifact (`redirect_takeover_reverted_vendored`: "now fully hosted") and
602+
only then ran the rewriter, which refused a lock with mixed line endings
603+
(or an unsupported `cacheKey` / `.yarnrc.yml` `compressionLevel`) —
604+
`redirected: 0`, and the next `yarn install` pulled the unpatched registry
605+
package. `vendor` / `scan --mode vendored` over a hosted berry purl did the
606+
same in reverse (`vendor_takeover_reverted_redirect`, then `failed`
607+
`vendor_yarn_berry_mixed_line_endings`). Both takeovers now run the new
608+
mode's berry gates first — wet and `--dry-run` alike — and a refused purl
609+
keeps the old mode's wiring byte-identical.
610+
- **`setup` keeps a CRLF `package.json` CRLF.** `setup` and `setup --remove`
611+
re-serialized `package.json` with bare LF and dropped a leading BOM, so on
612+
a Windows yarn berry project (yarn pretty-prints the manifest with CRLF) a
613+
two-key script edit became a whole-file diff that yarn then kept, and
614+
`setup --remove` could not land byte-identical on the pre-setup file.
615+
`package.json` is now written in its own layout (BOM, indent, line ending,
616+
trailing-newline shape), the same helper the vendored backends use.
562617
- **A vendoring-service outage no longer re-vendors packages.** An npm
563618
re-run (every lock flavor, `bun.lockb` included) re-acquired its tarball
564619
from whichever source answered — the service's prebuilt, or a local pack

0 commit comments

Comments
 (0)