From d162b6a0fd11004f966744dff6750c2ad88451d0 Mon Sep 17 00:00:00 2001 From: Quratulain-bilal Date: Sun, 26 Jul 2026 21:22:37 +0500 Subject: [PATCH] fix(powershell): resolve SPECIFY_INIT_DIR without .NET Core-only API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit common.ps1 resolved the SPECIFY_INIT_DIR root with [System.IO.Path]::TrimEndingDirectorySeparator, which only exists on .NET Core. On Windows PowerShell 5.1 (.NET Framework) the call throws 'Method invocation failed ... does not contain a method named TrimEndingDirectorySeparator', so root resolution fails before any command runs whenever SPECIFY_INIT_DIR is set. PowerShell 7+ users are unaffected, which is why it went unnoticed. The same file already documents this incompatibility ~150 lines later and uses .TrimEnd('/','\') there. Apply the same approach here, guarding the one case where the two differ — a bare drive root ('C:\'), where TrimEnd would strip the separator and yield an invalid 'C:' — by preserving the original. The existing @requires_pwsh tests in tests/test_init_dir.py already exercise this path (trailing-slash, relative, precedence, compose); they now pass on Windows PowerShell 5.1 as well as pwsh 7+. Fixes #3749 --- scripts/powershell/common.ps1 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/powershell/common.ps1 b/scripts/powershell/common.ps1 index afc226ea00..7e408e3a82 100644 --- a/scripts/powershell/common.ps1 +++ b/scripts/powershell/common.ps1 @@ -55,9 +55,15 @@ function Resolve-SpecifyInitDir { } # Resolve-Path echoes back any trailing separator from the input; trim it so # the returned root matches the bash resolver, whose `cd && pwd` never yields - # one. TrimEndingDirectorySeparator is a no-op on a bare root and on a path - # that already has no trailing separator. - $initRoot = [System.IO.Path]::TrimEndingDirectorySeparator($resolved.Path) + # one. Use TrimEnd (not [Path]::TrimEndingDirectorySeparator, which is .NET + # Core only and throws on Windows PowerShell 5.1 / .NET Framework — see the + # matching note further below). TrimEnd is a no-op on a path with no trailing + # separator; the one difference is a bare drive root (`C:\` -> `C:`), so keep + # that separator to preserve a valid rooted path. + $initRoot = $resolved.Path.TrimEnd('/', '\') + if ($initRoot -match '^[A-Za-z]:$') { + $initRoot = $resolved.Path + } if (-not (Test-Path -LiteralPath (Join-Path $initRoot '.specify') -PathType Container)) { [Console]::Error.WriteLine("ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): $initRoot") if ($ReturnNullOnError) { return $null }