From a3d0e0e58044eecca06ab12a57354449c5a10bbe Mon Sep 17 00:00:00 2001 From: Trent Blackburn Date: Mon, 18 May 2026 23:20:09 -0400 Subject: [PATCH 1/2] chore(build): normalize PlatyPS-generated doc line endings to LF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PowerShellBuild's BUILDHELP task invokes PlatyPS, which writes docs/en-US/*.md with CRLF on Windows. That contradicts the repo's .gitattributes (`* text=auto eol=lf`) and dirties git status after every local build with 28 phantom "modifications" (line-ending only, no content change). Two-layer prevention: 1. Build-time fix (`NormalizeDocsLineEndings` psake task): runs after BUILDHELP, rewrites any CRLF in docs/**/*.md to LF using UTF-8 without BOM. UnitTest and ScriptAnalysis now depend on this task instead of Build directly, so it always runs in the standard chain. Verified locally: 28 CRLF files were normalized to 0 after a `./build.ps1 UnitTest` run. 2. Pre-commit safety net (`mixed-line-ending --fix=lf` from pre-commit-hooks): catches CRLF from any other source — editors, ad-hoc tooling, future tasks — before it reaches the index. .gitattributes alone only acts at git operations and can't stop a tool from writing CRLF into the working tree between operations. Co-Authored-By: Claude Opus 4.7 (1M context) --- .pre-commit-config.yaml | 12 +++++++++++- build.psake.ps1 | 30 ++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 52065f4..f4f8182 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,4 +9,14 @@ repos: hooks: - id: ggshield language_version: python3 - stages: [pre-commit] \ No newline at end of file + stages: [pre-commit] + + # Safety net for line endings: the build's NormalizeDocsLineEndings task fixes + # PlatyPS-generated docs, but this catches CRLF from any other source (editors, + # ad-hoc tooling) before it reaches the index. .gitattributes already pins LF + # but only acts at git operations; this enforces it before the commit lands. + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: mixed-line-ending + args: [--fix=lf] \ No newline at end of file diff --git a/build.psake.ps1 b/build.psake.ps1 index be29e07..c497f38 100644 --- a/build.psake.ps1 +++ b/build.psake.ps1 @@ -74,7 +74,33 @@ $unitTestPreReqs = { return $result } -Task -Name 'UnitTest' -Depends 'Build' -PreCondition $unitTestPreReqs -Description 'Execute Pester tests (excluding Integration)' { +# Normalize PlatyPS-generated doc line endings to LF. +# PlatyPS (invoked by PowerShellBuild's BUILDHELP task) writes files with CRLF +# on Windows, which contradicts .gitattributes (`* text=auto eol=lf`) and +# leaves docs/en-US/*.md as dirty in `git status` after every local build. +# Inserting this between Build and the test tasks keeps the working tree clean +# without depending on a pre-commit hook firing later. +Task -Name 'NormalizeDocsLineEndings' -Depends 'Build' -Description 'Normalize generated doc files to LF (PlatyPS writes CRLF on Windows)' { + $docsPath = Join-Path -Path $PSScriptRoot -ChildPath 'docs' + if (-not (Test-Path -Path $docsPath)) { + return + } + $utf8NoBom = [System.Text.UTF8Encoding]::new($false) + $changed = 0 + Get-ChildItem -Path $docsPath -Filter '*.md' -Recurse -File | ForEach-Object { + $original = [System.IO.File]::ReadAllText($_.FullName) + $normalized = $original -replace "`r`n", "`n" + if ($original -cne $normalized) { + [System.IO.File]::WriteAllText($_.FullName, $normalized, $utf8NoBom) + $changed++ + } + } + if ($changed -gt 0) { + Write-Host " Normalized $changed doc file(s) to LF" -ForegroundColor Gray + } +} + +Task -Name 'UnitTest' -Depends 'NormalizeDocsLineEndings' -PreCondition $unitTestPreReqs -Description 'Execute Pester tests (excluding Integration)' { # Remove any previously imported project modules and import from the output dir $moduleManifest = Join-Path $PSBPreference.Build.ModuleOutDir "$($PSBPreference.General.ModuleName).psd1" Get-Module $PSBPreference.General.ModuleName | Remove-Module -Force -ErrorAction SilentlyContinue @@ -125,7 +151,7 @@ $scriptAnalysisPreReqs = { return $result } -Task -Name 'ScriptAnalysis' -Depends 'Build' -PreCondition $scriptAnalysisPreReqs -Description 'Execute PSScriptAnalyzer' { +Task -Name 'ScriptAnalysis' -Depends 'NormalizeDocsLineEndings' -PreCondition $scriptAnalysisPreReqs -Description 'Execute PSScriptAnalyzer' { # Get only .ps1 files (exclude .psd1 module manifests which are auto-generated) $ps1Files = Get-ChildItem -Path $PSBPreference.Build.ModuleOutDir -Filter '*.ps1' -Recurse From a670c0f3b1189fdd674356db1cfdc5fe8d5fe4fa Mon Sep 17 00:00:00 2001 From: Trent Blackburn Date: Tue, 19 May 2026 00:10:27 -0400 Subject: [PATCH 2/2] style: drop redundant -Path on single-arg Test-Path call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-arg named-parameter variant is the over-application pattern agreed to revert across the JsmOps#9 / YTMP#23 / Plex#55 / ReScene#18 propagation — the rule scopes to 2+ args. Fixed before this pattern lands in the template and gets copied to the other PowerShellBuild + PlatyPS consumers. Co-Authored-By: Claude Opus 4.7 (1M context) --- build.psake.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.psake.ps1 b/build.psake.ps1 index c497f38..4b52329 100644 --- a/build.psake.ps1 +++ b/build.psake.ps1 @@ -82,7 +82,7 @@ $unitTestPreReqs = { # without depending on a pre-commit hook firing later. Task -Name 'NormalizeDocsLineEndings' -Depends 'Build' -Description 'Normalize generated doc files to LF (PlatyPS writes CRLF on Windows)' { $docsPath = Join-Path -Path $PSScriptRoot -ChildPath 'docs' - if (-not (Test-Path -Path $docsPath)) { + if (-not (Test-Path $docsPath)) { return } $utf8NoBom = [System.Text.UTF8Encoding]::new($false)