Repository navigation
Expand file tree
/
Copy pathinstall.ps1
More file actions
170 lines (160 loc) · 8.61 KB
/
Copy pathinstall.ps1
File metadata and controls
170 lines (160 loc) · 8.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# CodeGraph standalone installer for Windows (PowerShell).
#
# Downloads a self-contained bundle (a vendored Node runtime + the app) from
# GitHub Releases. No Node.js, no build tools required.
#
# irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex
#
# Upgrade with `codegraph upgrade` (or just re-run this -- safe even while agent
# sessions are running CodeGraph). To uninstall: remove
# $env:LOCALAPPDATA\codegraph and drop its \current\bin entry from your user PATH.
#
# Environment:
# CODEGRAPH_VERSION release tag to install (default: latest)
# CODEGRAPH_INSTALL_DIR install location (default: %LOCALAPPDATA%\codegraph)
$ErrorActionPreference = 'Stop'
# >>> Install-CodeGraphFiles -- keep identical to WINDOWS_SWAP_FUNCTION in src/upgrade/index.ts
function Install-CodeGraphFiles([string]$Stage, [string]$Dest) {
# Move an unpacked bundle into $Dest. Windows can't overwrite or delete a
# running node.exe or a loaded .node addon, but it can rename one, so every
# file being replaced (or dropped by the new version) is first renamed aside
# to <name>.old-<token>. Any failure puts every file back, so the install is
# never left half-replaced or without its node.exe.
$ErrorActionPreference = 'Stop'
$stageDir = (Resolve-Path -LiteralPath $Stage).ProviderPath.TrimEnd('\')
foreach ($need in 'node.exe', 'bin\codegraph.cmd') {
if (-not (Test-Path -LiteralPath (Join-Path $stageDir $need))) { throw "The CodeGraph download is incomplete (no $need); nothing was changed." }
}
$token = [guid]::NewGuid().ToString('N').Substring(0, 8)
$asideName = '\.old-[0-9a-f]{8,32}$'
$files = @{}; $dirs = @{}
foreach ($i in @(Get-ChildItem -LiteralPath $stageDir -Recurse -Force)) {
$rel = $i.FullName.Substring($stageDir.Length)
if ($i.PSIsContainer) { $dirs[$rel] = $true } else { $files[$rel] = $true }
}
$undo = New-Object System.Collections.ArrayList
function Move-Logged([string]$From, [string]$To) { [IO.File]::Move($From, $To); [void]$undo.Add(@($From, $To)) }
function Undo-Logged {
$lost = 0
for ($n = $undo.Count - 1; $n -ge 0; $n--) {
$u = $undo[$n]
try { if ($u.Count -eq 2) { [IO.File]::Move($u[1], $u[0]) } else { [IO.Directory]::Delete($u[0]) } } catch { if ($u.Count -eq 2) { $lost++ } }
}
$undo.Clear()
$lost
}
$done = $false; $at = $Dest
try {
if (-not (Test-Path -LiteralPath $Dest)) { [void][IO.Directory]::CreateDirectory($Dest); [void]$undo.Add(@($Dest)) }
$destDir = (Resolve-Path -LiteralPath $Dest).ProviderPath.TrimEnd('\')
foreach ($f in @(Get-ChildItem -LiteralPath $destDir -Recurse -Force -File)) {
if (-not $files.ContainsKey($f.FullName.Substring($destDir.Length)) -and $f.Name -notmatch $asideName) {
$at = $f.FullName; Move-Logged $at "$at.old-$token"
}
}
foreach ($rel in @($dirs.Keys | Sort-Object Length)) {
$at = $destDir + $rel
if (-not [IO.Directory]::Exists($at)) { [void][IO.Directory]::CreateDirectory($at); [void]$undo.Add(@($at)) }
}
foreach ($rel in @($files.Keys)) {
$at = $destDir + $rel
if ([IO.File]::Exists($at)) { Move-Logged $at "$at.old-$token" }
Move-Logged ($stageDir + $rel) $at
}
$done = $true
} catch {
$x = $_.Exception; while ($x.InnerException) { $x = $x.InnerException }
$lost = Undo-Logged
$msg = "Could not replace $at ($($x.Message))."
if ($lost) {
$msg += " $lost file(s) could not be put back, so the install may not start. Close your agent sessions and any running codegraph commands, then reinstall: irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex"
} else {
$msg += " Nothing was changed: the existing install still works. If another program has CodeGraph's files open, close your agent sessions (they run the CodeGraph MCP server) and any running codegraph commands, then try again."
}
$e = New-Object System.Exception($msg); $e.Data['codegraphDamaged'] = [bool]$lost; throw $e
} finally {
# Interrupted (Ctrl+C) without reaching catch: still put everything back.
if (-not $done) { [void](Undo-Logged) }
}
# Delete what this run and earlier ones renamed aside. A file a running
# process still holds can't be deleted yet; the next install retries it.
foreach ($f in @(Get-ChildItem -LiteralPath $destDir -Recurse -Force -File -ErrorAction SilentlyContinue)) {
if ($f.Name -match $asideName) { try { [IO.File]::Delete($f.FullName) } catch {} }
}
foreach ($d in @(Get-ChildItem -LiteralPath $destDir -Recurse -Force -Directory -ErrorAction SilentlyContinue | Sort-Object { $_.FullName.Length } -Descending)) {
if (-not $dirs.ContainsKey($d.FullName.Substring($destDir.Length))) { try { [IO.Directory]::Delete($d.FullName) } catch {} }
}
}
# <<< Install-CodeGraphFiles
$repo = 'colbymchenry/codegraph'
$installDir = if ($env:CODEGRAPH_INSTALL_DIR) { $env:CODEGRAPH_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'codegraph' }
# 1. Detect architecture -> target matching the release archives.
$arch = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'arm64' } else { 'x64' }
$target = "win32-$arch"
# 2. Resolve the version (latest release unless pinned).
$version = $env:CODEGRAPH_VERSION
if (-not $version) {
$version = (Invoke-RestMethod "https://api.github.com/repos/$repo/releases/latest").tag_name
}
if (-not $version) { throw "codegraph: could not resolve latest version; set CODEGRAPH_VERSION." }
# 3. Download the bundle and unpack it next to the stable 'current' dir (same
# volume, so moving it in is renames, not copies), then move it into 'current'.
# 'current' is never deleted wholesale: a CodeGraph process that is still
# running (an agent session's MCP server) holds node.exe and the native kernel,
# which can't be deleted -- Install-CodeGraphFiles renames them aside instead.
$url = "https://github.com/$repo/releases/download/$version/codegraph-$target.zip"
Write-Host "Installing CodeGraph $version ($target)..."
$tmp = Join-Path $env:TEMP ("cg-" + [guid]::NewGuid().ToString())
New-Item -ItemType Directory -Force -Path $tmp | Out-Null
$zip = Join-Path $tmp 'cg.zip'
$dest = Join-Path $installDir 'current'
$stage = Join-Path $installDir ('.staging-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
try {
Invoke-WebRequest -Uri $url -OutFile $zip
New-Item -ItemType Directory -Force -Path $stage | Out-Null
Expand-Archive -Path $zip -DestinationPath $stage -Force
# Archives contain a top-level codegraph-<target>\ dir.
$inner = Join-Path $stage "codegraph-$target"
Install-CodeGraphFiles $(if (Test-Path $inner) { $inner } else { $stage }) $dest
} finally {
Remove-Item -LiteralPath $stage, $tmp -Recurse -Force -ErrorAction SilentlyContinue
}
# 4. Put the launcher dir on the user's PATH.
$binDir = Join-Path $dest 'bin'
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if (($userPath -split ';') -notcontains $binDir) {
[Environment]::SetEnvironmentVariable('Path', "$binDir;$userPath", 'User')
Write-Host "Added $binDir to your PATH (restart your terminal to pick it up)."
}
Write-Host "Installed to $dest"
# 5. Warn if a different codegraph earlier on PATH will shadow this install.
# Most often a stale `npm i -g @colbymchenry/codegraph`, whose launcher keeps
# running its own version-pinned bundle — so `codegraph --version` disagrees
# with what we just installed (issue #1071). Check both the persisted PATH a
# fresh shell sees (Machine + User) and this session's PATH (catches dirs a
# shell profile injects, e.g. conda / npm).
$expected = Join-Path $binDir 'codegraph.cmd'
function Find-FirstCodegraph([string]$pathStr) {
foreach ($dir in ($pathStr -split ';')) {
if (-not $dir) { continue }
foreach ($leaf in @('codegraph.cmd', 'codegraph.exe', 'codegraph.bat', 'codegraph.ps1')) {
$cand = Join-Path $dir $leaf
if (Test-Path -LiteralPath $cand) { return $cand }
}
}
return $null
}
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$freshPath = ((@($machinePath, [Environment]::GetEnvironmentVariable('Path', 'User')) | Where-Object { $_ }) -join ';')
$shadow = $null
foreach ($winner in @((Find-FirstCodegraph $env:Path), (Find-FirstCodegraph $freshPath))) {
if ($winner -and ($winner -ne $expected)) { $shadow = $winner; break }
}
if ($shadow) {
Write-Warning "Another codegraph is earlier on your PATH and will run instead of this install:"
Write-Warning " $shadow"
Write-Warning " (this install: $expected)"
Write-Warning "If 'codegraph --version' shows an unexpected version, remove the other copy"
Write-Warning "(e.g. 'npm rm -g @colbymchenry/codegraph') or put '$binDir' first on your PATH."
}
Write-Host "Run: codegraph --help"