From 6312d5c9043db1bfdaddc655b4cf8ab86ac462b7 Mon Sep 17 00:00:00 2001 From: alivka Date: Sun, 26 Jul 2026 15:32:57 +0200 Subject: [PATCH 1/2] ci: enforce LF for PowerShell files The published bootstrap contract is a byte-for-byte SHA-256 comparison between scripts/bootstrap/run.ps1, the wdt-site copy, and production. Without a repository-owned EOL policy, Windows checkouts with core.autocrlf=true materialize CRLF working copies (5198 bytes, sha256 ff2f4bf8...) while the Git blob and production stay LF (5071 bytes, sha256 fd1f9bc5...), so strict local hash checks fail falsely. - Add .gitattributes with the single rule '*.ps1 text eol=lf'. - Renormalization of tracked .ps1 files is a no-op: all 49 blobs are already stored as LF, and the canonical bootstrap blob (da249936c88eec0283628ebc4f07d2b94ee3748b) is unchanged. - Extend tests/bootstrap-sync.tests.ps1 to pin the policy: the .gitattributes rule must exist, the bootstrap checkout must contain no CR bytes, and its SHA-256 must remain the reviewed value. Co-Authored-By: Claude Opus 5 --- .gitattributes | 1 + tests/bootstrap-sync.tests.ps1 | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7b87302 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ps1 text eol=lf diff --git a/tests/bootstrap-sync.tests.ps1 b/tests/bootstrap-sync.tests.ps1 index 84f3244..42bfc83 100644 --- a/tests/bootstrap-sync.tests.ps1 +++ b/tests/bootstrap-sync.tests.ps1 @@ -20,6 +20,18 @@ foreach ($requiredFile in @($canonicalBootstrap, $syncScript)) { } $canonicalHash = (Get-FileHash -LiteralPath $canonicalBootstrap -Algorithm SHA256).Hash + +# The byte-for-byte bootstrap guarantee requires a repository-owned EOL policy: +# without it, core.autocrlf=true checkouts produce CRLF and a different SHA-256. +$expectedCanonicalBootstrapSha256 = 'fd1f9bc55fe8665c4a2d4706728eff16723a4c62fffd57baeb9c697b35a77bc6' +$gitAttributesPath = Join-Path -Path $repositoryRoot -ChildPath '.gitattributes' +Assert-True (Test-Path -LiteralPath $gitAttributesPath -PathType Leaf) 'Repository root is missing .gitattributes; the PowerShell LF policy must be repository-owned.' +$gitAttributesLines = @(Get-Content -LiteralPath $gitAttributesPath) +Assert-True (@($gitAttributesLines | Where-Object { $_ -match '^\s*\*\.ps1\s+text\s+eol=lf\s*$' }).Count -ge 1) '.gitattributes does not contain the active rule: *.ps1 text eol=lf' +$canonicalBootstrapBytes = [System.IO.File]::ReadAllBytes($canonicalBootstrap) +Assert-True ([System.Array]::IndexOf($canonicalBootstrapBytes, [byte]13) -lt 0) 'Canonical bootstrap checkout contains CR bytes. Re-checkout PowerShell files after the LF policy (for example: git checkout -- .) or fix .gitattributes.' +Assert-True ([string]::Equals($canonicalHash, $expectedCanonicalBootstrapSha256, [System.StringComparison]::OrdinalIgnoreCase)) "Canonical bootstrap SHA-256 changed. Expected $expectedCanonicalBootstrapSha256 but found $($canonicalHash.ToLowerInvariant()). Update the pinned value only for a deliberate, reviewed bootstrap change." + $tokens = $null $parseErrors = $null $syncAst = [System.Management.Automation.Language.Parser]::ParseFile($syncScript, [ref]$tokens, [ref]$parseErrors) From eb02ca40b161fb5401aeeeb8b4365c3a586b08ee Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Sun, 26 Jul 2026 16:06:46 +0200 Subject: [PATCH 2/2] docs: explain PowerShell LF policy --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 7b87302..ea5a2ce 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ +# Keep PowerShell files byte-stable across platforms. *.ps1 text eol=lf