mirror of
https://github.com/github/spec-kit.git
synced 2026-08-03 06:26:30 +08:00
`Resolve-SpecifyInitDir` normalized the resolved path with
`[System.IO.Path]::TrimEndingDirectorySeparator`, which is .NET Core only.
Windows PowerShell 5.1 runs on .NET Framework, so on every 5.1 host the call
throws at that line and root resolution fails before the requested command runs:
$ $env:SPECIFY_INIT_DIR = "C:\repo\web"
$ .specify\scripts\powershell\check-prerequisites.ps1 -Json
check-prerequisites.ps1 : Method invocation failed because
[System.IO.Path] does not contain a method named
'TrimEndingDirectorySeparator'.
The same file already documents this exact incompatibility and avoids it
correctly in `Get-FeaturePathsEnv` (~150 lines below), which uses `TrimEnd`
with a comment naming `TrimEndingDirectorySeparator` as .NET Core only.
Worse than a clean failure when the resolver is called directly: the throw is
non-terminating, so `$initRoot` stays `$null` and the very next `Join-Path`
throws too, `Get-RepoRoot` returns `$null`, and the shell exits **0**. A caller
that checks the exit code sees success with an empty root.
Switched to the `TrimEnd('/', '\')` the file already endorses. Note the obvious
swap is not quite enough on its own: a bare `TrimEnd` turns `C:\` into `C:`,
which is not the drive root but a drive-relative reference that later path APIs
re-resolve against the *current directory* — so validation would probe the wrong
tree and, from a cwd that happens to contain `.specify/`, could silently accept
`C:` as the project root. A `GetPathRoot` length check keeps a path that is its
own root intact. Both `GetPathRoot` and `TrimEnd` exist on .NET Framework.
Trailing-separator trimming (the reason the call was there — bash's `cd && pwd`
never yields one, so the two resolvers must agree) is unchanged, as are all
error paths and messages.
Tests in `tests/test_init_dir.py`:
- A static check that no shipped `.ps1` calls a .NET Core-only
`[System.IO.Path]` member (`TrimEndingDirectorySeparator`,
`EndsInDirectorySeparator`, `GetRelativePath`, `Join`). This one runs on all
platforms and is what actually guards CI: the matrix runs the PowerShell
tests under `pwsh`, which is .NET Core, so a .NET Framework-only regression
is otherwise invisible to it. Anchored to the `Path` type so `[string]::Join`
is not flagged.
- Two runtime tests under `powershell.exe` specifically (never `pwsh`),
covering resolution and trailing-separator parity.
- A drive-root test asserting the reported root survives the trim intact.
Test-the-test: reverting the source change fails all four (the runtime pair
with the `does not contain a method named` throw, the static check by locating
the call). Applying only the naive `TrimEnd` fails the drive-root test, which
reports `C:` instead of `C:\`. Verified on Windows PowerShell 5.1.19041.6456,
including the previously-crashing `check-prerequisites.ps1 -Json` end to end.
Also fixes six pre-existing `test_ps_*` failures on 5.1-only hosts, which were
this bug rather than test-harness issues.
Fixes #3749
Assisted-by: Claude Code (model: claude-opus-5, under direct human supervision)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>