From c469a05ce58b0f269b9750dc6e9a857abcff7ccf Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 7 May 2026 18:00:59 -0700 Subject: [PATCH] fix(install.ps1): validate existing repo via git itself + clean up broken stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit teknium1 hit "fatal: not in a git directory" on re-install when the previous install left a $InstallDir\.git stub that Test-Path matched but git didn't recognize (three "fatal: not a git repository" lines, then the script exited before touching anything). Two bugs: 1. Test-Path "$InstallDir\.git" was a weak gate — it matches .git whether it's a directory, file, symlink, submodule gitfile, OR a broken stub from a failed previous Remove-Item. Replaced with a real repo probe: Push-Location + git rev-parse --is-inside-work-tree + $LASTEXITCODE check. If git itself can't see a repo, we treat the directory as not-a-repo and fall through to fresh clone. 2. The original update path ignored $LASTEXITCODE. fetch/checkout/pull all emitted fatals but the script kept going. Now each command checks $LASTEXITCODE and throws with an explicit message. Also: when the directory exists but isn't a valid repo, the new code wipes it (Remove-Item -ErrorAction Stop) and falls through to fresh clone, instead of dying with the old "Directory exists but is not a git repository" error. If the wipe itself fails (file locked, hermes still running), we throw with a user-readable "close any programs using files in " hint. Refactored the function to use a $didUpdate flag instead of my earlier draft's early `return` — that was skipping the submodule init block at the bottom of the function. Both the update and fresh-clone paths now fall through to the submodule init step, which is correct (git pull doesn't auto-update submodules). PowerShell structural check: 21 functions defined, braces balanced. --- scripts/install.ps1 | 74 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index b04efd954b..dfffb9f82f 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -605,21 +605,61 @@ function Install-SystemPackages { function Install-Repository { Write-Info "Installing to $InstallDir..." - + + $didUpdate = $false + if (Test-Path $InstallDir) { + # Test-Path "$InstallDir\.git" returns True when .git is a file OR a + # directory OR a symlink OR a submodule-style gitfile — and also when + # it's a broken stub left over from a failed previous install (e.g. + # a partial Remove-Item that couldn't delete a locked index.lock). + # Validate the repo properly by asking git itself. If git can't see + # it as a real repo, fall through to the clone-or-zip path. + $repoValid = $false if (Test-Path "$InstallDir\.git") { + Push-Location $InstallDir + try { + $null = git -c windows.appendAtomically=false rev-parse --is-inside-work-tree 2>$null + if ($LASTEXITCODE -eq 0) { + $repoValid = $true + } + } catch {} + Pop-Location + } + + if ($repoValid) { Write-Info "Existing installation found, updating..." Push-Location $InstallDir - git -c windows.appendAtomically=false fetch origin - git -c windows.appendAtomically=false checkout $Branch - git -c windows.appendAtomically=false pull origin $Branch - Pop-Location + try { + git -c windows.appendAtomically=false fetch origin + if ($LASTEXITCODE -ne 0) { throw "git fetch failed (exit $LASTEXITCODE)" } + git -c windows.appendAtomically=false checkout $Branch + if ($LASTEXITCODE -ne 0) { throw "git checkout $Branch failed (exit $LASTEXITCODE)" } + git -c windows.appendAtomically=false pull origin $Branch + if ($LASTEXITCODE -ne 0) { throw "git pull failed (exit $LASTEXITCODE)" } + } finally { + Pop-Location + } + $didUpdate = $true } else { - Write-Err "Directory exists but is not a git repository: $InstallDir" - Write-Info "Remove it or choose a different directory with -InstallDir" - throw "Directory exists but is not a git repository: $InstallDir" + # Directory exists but isn't a usable git repo. Wipe it and + # fall through to a fresh clone. A leftover ``.git`` stub from + # a partial uninstall used to lock the installer into the + # "update" branch forever, emitting three ``fatal: not a git + # repository`` errors and failing with "not in a git directory". + Write-Warn "Existing directory at $InstallDir is not a valid git repo — replacing it." + try { + Remove-Item -Recurse -Force $InstallDir -ErrorAction Stop + } catch { + Write-Err "Could not remove $InstallDir : $_" + Write-Info "Close any programs that might be using files in $InstallDir (editors," + Write-Info "terminals, running hermes processes) and try again." + throw + } } - } else { + } + + if (-not $didUpdate) { $cloneSuccess = $false # Fix Windows git "copy-fd: write returned: Invalid argument" error. @@ -640,7 +680,7 @@ function Install-Repository { if ($LASTEXITCODE -eq 0) { $cloneSuccess = $true } } catch { } $env:GIT_SSH_COMMAND = $null - + if (-not $cloneSuccess) { if (Test-Path $InstallDir) { Remove-Item -Recurse -Force $InstallDir -ErrorAction SilentlyContinue } Write-Info "SSH failed, trying HTTPS..." @@ -658,18 +698,18 @@ function Install-Repository { $zipUrl = "https://github.com/NousResearch/hermes-agent/archive/refs/heads/$Branch.zip" $zipPath = "$env:TEMP\hermes-agent-$Branch.zip" $extractPath = "$env:TEMP\hermes-agent-extract" - + Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath -UseBasicParsing if (Test-Path $extractPath) { Remove-Item -Recurse -Force $extractPath } Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force - + # GitHub ZIPs extract to repo-branch/ subdirectory $extractedDir = Get-ChildItem $extractPath -Directory | Select-Object -First 1 if ($extractedDir) { New-Item -ItemType Directory -Force -Path (Split-Path $InstallDir) -ErrorAction SilentlyContinue | Out-Null Move-Item $extractedDir.FullName $InstallDir -Force Write-Success "Downloaded and extracted" - + # Initialize git repo so updates work later Push-Location $InstallDir git -c windows.appendAtomically=false init 2>$null @@ -677,10 +717,10 @@ function Install-Repository { git remote add origin $RepoUrlHttps 2>$null Pop-Location Write-Success "Git repo initialized for future updates" - + $cloneSuccess = $true } - + # Cleanup temp files Remove-Item -Force $zipPath -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $extractPath -ErrorAction SilentlyContinue @@ -693,7 +733,7 @@ function Install-Repository { throw "Failed to download repository (tried git clone SSH, HTTPS, and ZIP)" } } - + # Set per-repo config (harmless if it fails) Push-Location $InstallDir git -c windows.appendAtomically=false config windows.appendAtomically false 2>$null @@ -707,7 +747,7 @@ function Install-Repository { Write-Success "Submodules ready" } Pop-Location - + Write-Success "Repository ready" }