[PR #1807] [MERGED] fix(build): preserve LF text checkouts on Windows #4829

Closed
opened 2026-10-31 00:55:29 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1807
Author: @CmsChase
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: codex/fix-windows-checkout-line-endings


📝 Commits (1)

  • f5858d5 fix(build): preserve LF text checkouts on Windows

📊 Changes

1 file changed (+5 additions, -0 deletions)

View changed files

➕ .gitattributes (+5 -0)

📄 Description

Summary

A clean Windows checkout with core.autocrlf=true fails REA's formatting gate because Git converts LF text blobs to CRLF while the existing formatter expects LF. Add a five-line repository checkout policy to keep detected text in LF, preserve CRLF for Windows CMD fixtures, and retain automatic binary detection.

Problem and expected behavior

Reproduced on Windows 11 with Git 2.54.0.windows.1, Node 24.18.0 and npm 11.16.0. The updated upstream baseline is ed084b3dbb13ec9dee1adc8f5b76e784b52c00bb; the rebased fix is f5858d595ae6f7762384da6bc87b244cb7c7fc3a, whose direct parent is that baseline.

With a clean Git status and core.autocrlf=true, the baseline rejects all 2,526 formatter inputs. The same commands pass after the policy is present at checkout. The fix lets contributors keep their existing Git configuration and use the repository's required verification commands.

Change and scope

The complete change is the following new .gitattributes:

# Keep text checkouts consistent with the repository formatter on every platform.
* text=auto eol=lf

# Windows command fixtures retain their native line endings.
*.cmd text eol=crlf

Detected text is checked out as LF. The later CMD rule overrides the default and checks CMD files out as CRLF; their Git blobs remain LF. Binary detection is unchanged. No application code, dependencies, formatter configuration, generated outputs or existing blobs change.

Existing working directories may retain old line endings until files are checked out again. Preserve local edits before refreshing files, or use a new clone. No destructive refresh command is required by the reproduction below.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Git checkout, before verification or fixture consumption.
  • CLI and MCP/tool-catalog contract: none.
  • Provider, bridge, target-format, or platform compatibility: CMD fixtures retain CRLF; provider source and authority are unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata, package, or installation impact: no published-package or installer changes; development Git checkouts only.

Evidence and regression coverage

Two complete fresh local clones used --no-local --no-checkout. Each clone received local core.autocrlf=true before its first checkout and its own npm ci. Neither reused the development working tree, index, dependencies, build outputs or Turbo cache. Only npm's download cache and the pinned installed toolchain were shared.

Command Updated baseline Rebased fix
npm ci Exit 0 Exit 0
npm run format:check Exit 1; 2,526 files rejected Exit 0
npm run check Exit 1 at formatting Exit 0; six tasks passed, zero cache hits
Git status Clean Clean

Actual file-byte checks in the fixed clone verified all 2,416 source files and four Shell scripts as LF, the CMD fixture as CRLF, and both tracked PNGs as byte-for-byte identical to their Git blobs. PNG SHA-256 hashes were identical in both clones and in the corresponding blobs. No new repository test files are added; the regression exercises actual Git checkout and the real formatter.

Reproduce on Windows

Use the pinned Node 24.18.0 and npm 11.16.0 on PATH. Run from a local checkout containing this commit; the script creates two new sibling clones and sets only their local Git configuration. Use a drive with enough free space. Baseline command failures are expected; PowerShell 7 users who have enabled PSNativeCommandUseErrorActionPreference should run this in a fresh default PowerShell session so the expected native exit 1 can be recorded.

$sourceRepo = (git rev-parse --show-toplevel).Trim()
$fixRevision = 'f5858d595ae6f7762384da6bc87b244cb7c7fc3a'
$baseRevision = 'ed084b3dbb13ec9dee1adc8f5b76e784b52c00bb'
$reviewDirectory = Join-Path (Split-Path $sourceRepo -Parent) ('rea-eol-review-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $reviewDirectory | Out-Null

foreach ($case in @(
    @{ Name = 'baseline'; Revision = $baseRevision },
    @{ Name = 'fixed'; Revision = $fixRevision }
)) {
    $cloneDirectory = Join-Path $reviewDirectory $case.Name
    git clone --no-local --no-checkout --config core.autocrlf=true $sourceRepo $cloneDirectory
    if ($LASTEXITCODE -ne 0) { throw 'Clone failed' }
    git -C $cloneDirectory checkout --detach $case.Revision
    if ($LASTEXITCODE -ne 0) { throw 'Checkout failed' }
    Push-Location $cloneDirectory
    try {
        npm.cmd ci
        if ($LASTEXITCODE -ne 0) { throw 'Dependency installation failed' }
        npm.cmd run format:check
        $formatExit = $LASTEXITCODE
        npm.cmd run check
        $checkExit = $LASTEXITCODE
        Write-Output "$($case.Name): format=$formatExit check=$checkExit"
        git status --porcelain=v1
        git ls-files --eol -- src/config.test.ts install.sh tests/fixtures/captureGhidraLaunch.cmd docs/assets/rea-hopper-analysis.png
    } finally {
        Pop-Location
    }
}

Expected output: baseline format=1 check=1; fix format=0 check=0. Both Git statuses remain clean. Source and Shell files change from i/lf w/crlf to i/lf w/lf; CMD remains i/lf w/crlf; the PNG remains i/-text w/-text.

Related work: #1101 proposed fixture-scoped attributes as part of a larger change and is closed without merging. #1704 and #1706 report the same Windows formatting failure but do not implement this policy. No dedicated issue was identified. Live checks inspected all 42 open PRs' complete file lists, the 100 most recently updated merged PRs, current main's attribute history, and searches for gitattributes, autocrlf, CRLF and line endings; no equivalent open or merged checkout-policy fix was found.

Validation performed

On the exact rebased commit above:

  • npm ci, npm run check:fast, npm run check, npm run build:cached, and npm run docs:check — passed in the development checkout.
  • npm run test:focused -- src/config.test.ts src/windows/WindowsNativeLoader.test.ts src/windows/WindowsOwnedProcess.test.ts src/domain/javascript/javascriptStaticAnalysisSourceMaps.test.ts tests/boundary/providers/ghidra/ghidraLauncher.test.ts tests/acceptance/webSourceLocationCli.test.ts — 109 tests passed across six files, including the real Windows CMD launcher fixture and compiled CLI/MCP source-map parity.
  • The fresh-clone commands and byte comparisons above — baseline RED, fix GREEN, no fixed-clone Turbo cache hits.
  • bash -n — all four Shell files passed in both fresh clones. This is syntax-only Git Bash coverage.
  • CMD fixture without a descriptor through cmd.exe — expected exit 64 in both fresh clones. Full fixture behavior is covered separately by the focused Ghidra launcher boundary tests.

Compatibility, safety, and release

  • Breaking changes or migration steps: no runtime breaking change. Existing worktrees may need a new checkout after local edits are preserved.
  • Real Hopper/Ghidra, browser, or OS coverage: real Windows checkout, CMD fixture, and Node CLI/MCP workflow; no real Hopper/Ghidra/IDA engine or Linux/macOS/Termux runtime was run.
  • Native limits: focused Windows execution used the existing package-compatible upstream CI native artifact. The current native addon was not rebuilt, and newly merged Bun/Job Object memory controls were not comprehensively verified. No claim of full current native/Bun coverage is made.
  • Package or release metadata impact: none. Submodules were not initialized for checkout regression; their contents have their own repository policies. No tracked BAT or PowerShell scripts currently require extra rules.
  • Security, privacy, process, or containment review: no changes to application authority or global Git settings.

Review checklist

  • A concrete Windows clean-checkout failure and one focused outcome.
  • Conventional Commit title and prior work linked.
  • Base/fix behavior executed on fresh clones, with binary preservation verified.
  • Rebased directly onto the checked upstream main snapshot.
  • Applicable CONTRIBUTING.md and AGENTS.md checks passed; no contracts, provider code or package distribution were changed.
  • Final diff contains only the original five-line attribute file, with no generated files, secrets or unrelated cleanup.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1807 **Author:** [@CmsChase](https://github.com/CmsChase) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `codex/fix-windows-checkout-line-endings` --- ### 📝 Commits (1) - [`f5858d5`](https://github.com/morluto/rea/commit/f5858d595ae6f7762384da6bc87b244cb7c7fc3a) fix(build): preserve LF text checkouts on Windows ### 📊 Changes **1 file changed** (+5 additions, -0 deletions) <details> <summary>View changed files</summary> ➕ `.gitattributes` (+5 -0) </details> ### 📄 Description ## Summary A clean Windows checkout with `core.autocrlf=true` fails REA's formatting gate because Git converts LF text blobs to CRLF while the existing formatter expects LF. Add a five-line repository checkout policy to keep detected text in LF, preserve CRLF for Windows CMD fixtures, and retain automatic binary detection. ## Problem and expected behavior Reproduced on Windows 11 with Git 2.54.0.windows.1, Node 24.18.0 and npm 11.16.0. The updated upstream baseline is `ed084b3dbb13ec9dee1adc8f5b76e784b52c00bb`; the rebased fix is `f5858d595ae6f7762384da6bc87b244cb7c7fc3a`, whose direct parent is that baseline. With a clean Git status and `core.autocrlf=true`, the baseline rejects all 2,526 formatter inputs. The same commands pass after the policy is present at checkout. The fix lets contributors keep their existing Git configuration and use the repository's required verification commands. ## Change and scope The complete change is the following new `.gitattributes`: ```gitattributes # Keep text checkouts consistent with the repository formatter on every platform. * text=auto eol=lf # Windows command fixtures retain their native line endings. *.cmd text eol=crlf ``` Detected text is checked out as LF. The later CMD rule overrides the default and checks CMD files out as CRLF; their Git blobs remain LF. Binary detection is unchanged. No application code, dependencies, formatter configuration, generated outputs or existing blobs change. Existing working directories may retain old line endings until files are checked out again. Preserve local edits before refreshing files, or use a new clone. No destructive refresh command is required by the reproduction below. ## Contract and boundary impact - Semantic owner and earliest changed stage: Git checkout, before verification or fixture consumption. - CLI and MCP/tool-catalog contract: none. - Provider, bridge, target-format, or platform compatibility: CMD fixtures retain CRLF; provider source and authority are unchanged. - Evidence, artifact, provenance, or reconstruction contract: none. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata, package, or installation impact: no published-package or installer changes; development Git checkouts only. ## Evidence and regression coverage Two complete fresh local clones used `--no-local --no-checkout`. Each clone received local `core.autocrlf=true` before its first checkout and its own `npm ci`. Neither reused the development working tree, index, dependencies, build outputs or Turbo cache. Only npm's download cache and the pinned installed toolchain were shared. | Command | Updated baseline | Rebased fix | | --- | --- | --- | | `npm ci` | Exit 0 | Exit 0 | | `npm run format:check` | Exit 1; 2,526 files rejected | Exit 0 | | `npm run check` | Exit 1 at formatting | Exit 0; six tasks passed, zero cache hits | | Git status | Clean | Clean | Actual file-byte checks in the fixed clone verified all 2,416 source files and four Shell scripts as LF, the CMD fixture as CRLF, and both tracked PNGs as byte-for-byte identical to their Git blobs. PNG SHA-256 hashes were identical in both clones and in the corresponding blobs. No new repository test files are added; the regression exercises actual Git checkout and the real formatter. ### Reproduce on Windows Use the pinned Node 24.18.0 and npm 11.16.0 on PATH. Run from a local checkout containing this commit; the script creates two new sibling clones and sets only their local Git configuration. Use a drive with enough free space. Baseline command failures are expected; PowerShell 7 users who have enabled `PSNativeCommandUseErrorActionPreference` should run this in a fresh default PowerShell session so the expected native exit 1 can be recorded. ```powershell $sourceRepo = (git rev-parse --show-toplevel).Trim() $fixRevision = 'f5858d595ae6f7762384da6bc87b244cb7c7fc3a' $baseRevision = 'ed084b3dbb13ec9dee1adc8f5b76e784b52c00bb' $reviewDirectory = Join-Path (Split-Path $sourceRepo -Parent) ('rea-eol-review-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $reviewDirectory | Out-Null foreach ($case in @( @{ Name = 'baseline'; Revision = $baseRevision }, @{ Name = 'fixed'; Revision = $fixRevision } )) { $cloneDirectory = Join-Path $reviewDirectory $case.Name git clone --no-local --no-checkout --config core.autocrlf=true $sourceRepo $cloneDirectory if ($LASTEXITCODE -ne 0) { throw 'Clone failed' } git -C $cloneDirectory checkout --detach $case.Revision if ($LASTEXITCODE -ne 0) { throw 'Checkout failed' } Push-Location $cloneDirectory try { npm.cmd ci if ($LASTEXITCODE -ne 0) { throw 'Dependency installation failed' } npm.cmd run format:check $formatExit = $LASTEXITCODE npm.cmd run check $checkExit = $LASTEXITCODE Write-Output "$($case.Name): format=$formatExit check=$checkExit" git status --porcelain=v1 git ls-files --eol -- src/config.test.ts install.sh tests/fixtures/captureGhidraLaunch.cmd docs/assets/rea-hopper-analysis.png } finally { Pop-Location } } ``` Expected output: baseline `format=1 check=1`; fix `format=0 check=0`. Both Git statuses remain clean. Source and Shell files change from `i/lf w/crlf` to `i/lf w/lf`; CMD remains `i/lf w/crlf`; the PNG remains `i/-text w/-text`. Related work: [#1101](https://github.com/morluto/rea/pull/1101) proposed fixture-scoped attributes as part of a larger change and is closed without merging. [#1704](https://github.com/morluto/rea/pull/1704) and [#1706](https://github.com/morluto/rea/pull/1706) report the same Windows formatting failure but do not implement this policy. No dedicated issue was identified. Live checks inspected all 42 open PRs' complete file lists, the 100 most recently updated merged PRs, current main's attribute history, and searches for gitattributes, autocrlf, CRLF and line endings; no equivalent open or merged checkout-policy fix was found. ## Validation performed On the exact rebased commit above: - `npm ci`, `npm run check:fast`, `npm run check`, `npm run build:cached`, and `npm run docs:check` — passed in the development checkout. - `npm run test:focused -- src/config.test.ts src/windows/WindowsNativeLoader.test.ts src/windows/WindowsOwnedProcess.test.ts src/domain/javascript/javascriptStaticAnalysisSourceMaps.test.ts tests/boundary/providers/ghidra/ghidraLauncher.test.ts tests/acceptance/webSourceLocationCli.test.ts` — 109 tests passed across six files, including the real Windows CMD launcher fixture and compiled CLI/MCP source-map parity. - The fresh-clone commands and byte comparisons above — baseline RED, fix GREEN, no fixed-clone Turbo cache hits. - `bash -n` — all four Shell files passed in both fresh clones. This is syntax-only Git Bash coverage. - CMD fixture without a descriptor through `cmd.exe` — expected exit 64 in both fresh clones. Full fixture behavior is covered separately by the focused Ghidra launcher boundary tests. ## Compatibility, safety, and release - Breaking changes or migration steps: no runtime breaking change. Existing worktrees may need a new checkout after local edits are preserved. - Real Hopper/Ghidra, browser, or OS coverage: real Windows checkout, CMD fixture, and Node CLI/MCP workflow; no real Hopper/Ghidra/IDA engine or Linux/macOS/Termux runtime was run. - Native limits: focused Windows execution used the existing package-compatible upstream CI native artifact. The current native addon was not rebuilt, and newly merged Bun/Job Object memory controls were not comprehensively verified. No claim of full current native/Bun coverage is made. - Package or release metadata impact: none. Submodules were not initialized for checkout regression; their contents have their own repository policies. No tracked BAT or PowerShell scripts currently require extra rules. - Security, privacy, process, or containment review: no changes to application authority or global Git settings. ## Review checklist - [x] A concrete Windows clean-checkout failure and one focused outcome. - [x] Conventional Commit title and prior work linked. - [x] Base/fix behavior executed on fresh clones, with binary preservation verified. - [x] Rebased directly onto the checked upstream main snapshot. - [x] Applicable CONTRIBUTING.md and AGENTS.md checks passed; no contracts, provider code or package distribution were changed. - [x] Final diff contains only the original five-line attribute file, with no generated files, secrets or unrelated cleanup. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:55:29 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4829
No description provided.