mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-22 10:32:00 +00:00
test(install): add ConvertTo-LongPath helper for 8.3 short paths
Adds a ConvertTo-LongPath helper to install.ps1 that expands a Windows 8.3 short path (e.g. C:\Users\FIRST~1.LAS) back to its long form via Scripting.FileSystemObject. Paths without a "~<digit>" component are returned unchanged (no COM round-trip), and any COM failure falls back to the input. Adds an AST-loaded unit test that exercises the helper without executing the installer body (pass-through, null/empty, and graceful fallback).
This commit is contained in:
parent
5a53e0f0f4
commit
e74033b39b
2 changed files with 120 additions and 0 deletions
|
|
@ -88,6 +88,40 @@ try {
|
|||
# Mojibake on output is then cosmetic-only, install still works.
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 8.3 short-path normalization
|
||||
# ============================================================================
|
||||
# When the Windows user-profile folder name contains a space (e.g.
|
||||
# "First Last"), Windows generates an 8.3 short alias for it (e.g. FIRST~1.LAS)
|
||||
# and may expose %TEMP%/%TMP% in that short form:
|
||||
# C:\Users\FIRST~1.LAS\AppData\Local\Temp
|
||||
# PowerShell's FileSystem provider mishandles the "~1.ext" component when such a
|
||||
# path is handed to a provider cmdlet like `Tee-Object -FilePath` /
|
||||
# `Out-File -FilePath`, throwing:
|
||||
# "An object at the specified path C:\Users\FIRST~1.LAS does not exist."
|
||||
# Every Node/Electron build+install stage streams its log to %TEMP% via
|
||||
# Tee-Object, so they all abort with that error, while the Python/uv stages --
|
||||
# which never write a side log to %TEMP% through a provider cmdlet -- complete
|
||||
# fine. Expanding %TEMP%/%TMP% back to their long form once, up front, lets
|
||||
# every downstream cmdlet (and child process) see a path the provider can
|
||||
# resolve. (GH: Windows desktop installer fails at Node/Electron stages.)
|
||||
|
||||
function ConvertTo-LongPath {
|
||||
param([string]$Path)
|
||||
if ([string]::IsNullOrWhiteSpace($Path)) { return $Path }
|
||||
# Only 8.3 short names carry a tilde+digit ("~1"); skip the COM round-trip
|
||||
# for ordinary long paths.
|
||||
if ($Path -notmatch '~\d') { return $Path }
|
||||
try {
|
||||
$fso = New-Object -ComObject Scripting.FileSystemObject
|
||||
if ($fso.FolderExists($Path)) { return $fso.GetFolder($Path).Path }
|
||||
if ($fso.FileExists($Path)) { return $fso.GetFile($Path).Path }
|
||||
} catch {
|
||||
# COM unavailable / locked-down host: fall back to the original path.
|
||||
}
|
||||
return $Path
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
|
|
|||
86
scripts/tests/test-install-ps1-longpath.ps1
Normal file
86
scripts/tests/test-install-ps1-longpath.ps1
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Unit tests for install.ps1's ConvertTo-LongPath helper.
|
||||
#
|
||||
# Run from a PowerShell prompt:
|
||||
#
|
||||
# powershell -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-install-ps1-longpath.ps1
|
||||
#
|
||||
# Background: on a Windows profile whose folder name contains a space (e.g.
|
||||
# "First Last"), %TEMP%/%TMP% can be exposed as an 8.3 short path
|
||||
# (C:\Users\FIRST~1.LAS\...). PowerShell's FileSystem provider chokes on the
|
||||
# "~1.ext" component when it reaches a provider cmdlet (Tee-Object -FilePath),
|
||||
# aborting the Node/Electron install+build stages. install.ps1 expands such
|
||||
# paths to their long form up front; this verifies the helper's contract.
|
||||
#
|
||||
# We extract just the function from install.ps1 via the AST so the installer's
|
||||
# top-level body never runs (dot-sourcing would execute the whole script).
|
||||
# The COM-backed expansion only fires for inputs containing "~<digit>"; the
|
||||
# pass-through and graceful-fallback paths are assertable on any host (incl.
|
||||
# non-Windows pwsh, where the COM object is simply unavailable).
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path))
|
||||
$installScript = Join-Path $repoRoot "scripts/install.ps1"
|
||||
|
||||
if (-not (Test-Path $installScript)) {
|
||||
throw "Could not locate install.ps1 at $installScript"
|
||||
}
|
||||
|
||||
$failures = 0
|
||||
function Assert-Equal {
|
||||
param([Parameter(Mandatory = $true)] $Expected,
|
||||
[Parameter(Mandatory = $true)] $Actual,
|
||||
[Parameter(Mandatory = $true)] [string]$Label)
|
||||
if ($Expected -ne $Actual) {
|
||||
Write-Host "FAIL: $Label" -ForegroundColor Red
|
||||
Write-Host " expected: $Expected"
|
||||
Write-Host " actual: $Actual"
|
||||
$script:failures++
|
||||
} else {
|
||||
Write-Host "OK: $Label" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
# --- Load ConvertTo-LongPath from install.ps1 without executing the script ---
|
||||
$tokens = $null
|
||||
$errors = $null
|
||||
$ast = [System.Management.Automation.Language.Parser]::ParseFile($installScript, [ref]$tokens, [ref]$errors)
|
||||
$fnAst = $ast.FindAll(
|
||||
{
|
||||
param($node)
|
||||
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
|
||||
$node.Name -eq 'ConvertTo-LongPath'
|
||||
}, $true) | Select-Object -First 1
|
||||
|
||||
if (-not $fnAst) {
|
||||
throw "ConvertTo-LongPath not found in install.ps1 -- did the helper get renamed/removed?"
|
||||
}
|
||||
. ([scriptblock]::Create($fnAst.Extent.Text))
|
||||
|
||||
# --- Tests ---
|
||||
Write-Host ""
|
||||
Write-Host "-- ConvertTo-LongPath --"
|
||||
|
||||
Assert-Equal -Expected "" -Actual (ConvertTo-LongPath "") -Label "empty string returns empty"
|
||||
Assert-Equal -Expected $null -Actual (ConvertTo-LongPath $null) -Label "null returns null"
|
||||
|
||||
# No 8.3 component -> returned verbatim (even with spaces).
|
||||
$longish = "C:\Users\First Last\AppData\Local\Temp"
|
||||
Assert-Equal -Expected $longish -Actual (ConvertTo-LongPath $longish) -Label "long path with spaces is unchanged"
|
||||
|
||||
$noTilde = "/tmp/some/long/path"
|
||||
Assert-Equal -Expected $noTilde -Actual (ConvertTo-LongPath $noTilde) -Label "tilde-free path is unchanged"
|
||||
|
||||
# Looks like an 8.3 name but does not exist -> graceful fallback to the input
|
||||
# (FolderExists/FileExists both false, or COM unavailable on this host).
|
||||
$fakeShort = "C:\Users\FIRST~1.LAS\does\not\exist"
|
||||
Assert-Equal -Expected $fakeShort -Actual (ConvertTo-LongPath $fakeShort) -Label "nonexistent 8.3 path falls back to input"
|
||||
|
||||
# --- Summary ---
|
||||
Write-Host ""
|
||||
if ($failures -gt 0) {
|
||||
Write-Host "FAILED: $failures assertion(s) failed" -ForegroundColor Red
|
||||
exit 1
|
||||
} else {
|
||||
Write-Host "All ConvertTo-LongPath tests passed." -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue