-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathupdate.ps1
More file actions
243 lines (218 loc) · 9.72 KB
/
update.ps1
File metadata and controls
243 lines (218 loc) · 9.72 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# PortOS Update Script for Windows PowerShell
$ErrorActionPreference = "Stop"
$RootDir = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location $RootDir
New-Item -ItemType Directory -Force -Path "$RootDir\data" | Out-Null
# Log file for external command output — keeps noisy git/npm/node output
# off the parent pipe so broken-pipe errors don't abort the update
$UpdateLog = Join-Path $RootDir "data\update.log"
"" | Set-Content -Path $UpdateLog
# Safe write helper — suppresses broken-pipe IOExceptions when the parent
# Node process dies mid-update (mirrors the bash SIGPIPE trap)
function Write-SafeHost {
param(
[string]$Text,
[string]$ForegroundColor = ""
)
try {
if ($ForegroundColor) {
Write-Host $Text -ForegroundColor $ForegroundColor
} else {
Write-Host $Text
}
} catch {
if ($_.Exception -is [System.IO.IOException] -or
$_.Exception.InnerException -is [System.IO.IOException] -or
$_.Exception.ToString() -like "*The pipe has been ended*") {
return
}
throw
}
}
# Safe stdout helper for machine-readable output consumed by the parent process.
# Uses [Console]::Out.WriteLine so STEP markers reach stdout even when Write-Host
# is redirected to the information stream.
function Write-SafeStdout {
param([string]$Text)
try {
[Console]::Out.WriteLine($Text)
} catch {
if ($_.Exception -is [System.IO.IOException] -or
$_.Exception.InnerException -is [System.IO.IOException] -or
$_.Exception.ToString() -like "*The pipe has been ended*") {
return
}
throw
}
}
# Step output helper (parsed by updateExecutor for UI progress)
function Step {
param([string]$Name, [string]$Status, [string]$Message)
Write-SafeStdout "STEP:${Name}:${Status}:${Message}"
}
# Run an external command, routing stdout/stderr to the log file so
# broken-pipe errors from the parent Node process don't abort the update
function Invoke-Logged {
param([Parameter(ValueFromRemainingArguments)]$CmdArgs)
$cmd = $CmdArgs[0]
$args = @()
if ($CmdArgs.Count -gt 1) { $args = $CmdArgs[1..($CmdArgs.Count - 1)] }
& $cmd @args >> $UpdateLog 2>&1
}
Write-SafeHost "===================================" -ForegroundColor Cyan
Write-SafeHost " PortOS Update" -ForegroundColor Cyan
Write-SafeHost "===================================" -ForegroundColor Cyan
Write-SafeHost ""
# Resilient npm install — retries once after cleaning node_modules on failure
function Safe-Install {
param([string]$Dir = ".", [string]$Label = "root")
Write-SafeHost "📦 Installing deps ($Label)..." -ForegroundColor Yellow
Push-Location $Dir
Invoke-Logged npm install
if ($LASTEXITCODE -eq 0) { Pop-Location; return }
Write-SafeHost "⚠️ npm install failed for $Label — cleaning node_modules and retrying..." -ForegroundColor Yellow
Pop-Location
if (Test-Path "$Dir/node_modules") {
Remove-Item -Recurse -Force "$Dir/node_modules" -ErrorAction SilentlyContinue
}
Push-Location $Dir
Invoke-Logged npm install
if ($LASTEXITCODE -eq 0) { Pop-Location; return }
Pop-Location
Write-SafeHost "❌ npm install failed for $Label after retry" -ForegroundColor Red
exit 1
}
# Pull latest — always switch to main (detached HEAD or feature branch both
# need to land on main before pulling, or the version won't advance)
Step "git-pull" "running" "Pulling latest changes..."
$headRef = git symbolic-ref -q HEAD 2>$null
$currentBranch = if ($headRef) { $headRef -replace "refs/heads/", "" } else { "" }
if ($currentBranch -ne "main") {
Write-SafeHost "⚠️ On branch '$currentBranch' — switching to main for update" -ForegroundColor Yellow
Invoke-Logged git checkout main
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
Invoke-Logged git pull --rebase --autostash
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Step "git-pull" "done" "Latest changes pulled"
Write-SafeHost ""
# Update submodules (slash-do and any others)
Step "submodules" "running" "Updating submodules..."
Invoke-Logged git submodule update --init --recursive
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Step "submodules" "done" "Submodules updated"
Write-SafeHost ""
# Kill PM2 daemon entirely — pm2 stop/restart only signal app processes but
# leave the daemon alive. If the daemon was originally launched from a different
# project, it can cache a stale ProcessContainerFork.js path and crash future
# fork() calls with MODULE_NOT_FOUND. Killing the daemon mirrors update.sh and
# forces a fresh launch from this checkout on restart.
Step "pm2-stop" "running" "Stopping PortOS apps..."
Invoke-Logged node ./node_modules/pm2/bin/pm2 kill
if ($LASTEXITCODE -ne 0) {
Write-SafeHost "⚠️ PM2 daemon was not running or could not be killed; continuing update" -ForegroundColor Yellow
}
Step "pm2-stop" "done" "Apps stopped"
Write-SafeHost ""
# Update dependencies with retry logic
Step "npm-install" "running" "Installing all dependencies..."
Safe-Install -Dir "." -Label "root"
Safe-Install -Dir "client" -Label "client"
Safe-Install -Dir "server" -Label "server"
Safe-Install -Dir "autofixer" -Label "autofixer"
# Run trusted install scripts skipped by ignore-scripts=true in .npmrc
Write-SafeHost "🔧 Rebuilding esbuild & node-pty..." -ForegroundColor Yellow
Invoke-Logged node client/node_modules/esbuild/install.js
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Invoke-Logged node server/node_modules/esbuild/install.js
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Invoke-Logged npm rebuild node-pty --prefix server
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-SafeHost ""
# Verify critical dependencies exist
if (-not (Test-Path "client/node_modules/vite/bin/vite.js")) {
Write-SafeHost "❌ Critical dependency missing: client/node_modules/vite" -ForegroundColor Red
Write-SafeHost " Try running: npm run install:all"
exit 1
}
Step "npm-install" "done" "Dependencies installed"
# Run data/db/browser setup. Don't call `npm run setup` — that re-runs the
# installs we just did above. The three scripts here are the data-side half
# of `npm run setup` and are idempotent.
Step "setup" "running" "Running setup..."
Invoke-Logged node scripts/setup-data.js
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Invoke-Logged node scripts/setup-db.js
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Invoke-Logged node scripts/setup-browser.js
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Invoke-Logged node scripts/setup-ghostty.js
Step "setup" "done" "Setup complete"
Write-SafeHost ""
# Run data migrations
Step "migrations" "running" "Running data migrations..."
$migrationsScript = Join-Path $RootDir "scripts\run-migrations.js"
if (Test-Path $migrationsScript) {
Invoke-Logged node $migrationsScript
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
Step "migrations" "done" "Migrations complete"
# Check for slash-do (optional, used by the PR Reviewer schedule task)
$slashDoFound = Get-Command slash-do -ErrorAction SilentlyContinue
if (-not $slashDoFound) {
Write-SafeHost "slash-do is not installed. It is used by the PR Reviewer schedule task." -ForegroundColor Yellow
if ([Environment]::UserInteractive) {
$reply = Read-Host "Install slash-do now? [y/N]"
if ($reply -match "^[Yy]$") {
Write-SafeHost "Installing slash-do..." -ForegroundColor Yellow
Invoke-Logged npm install -g slash-do@latest
if ($LASTEXITCODE -ne 0) {
Write-SafeHost "⚠️ Failed to install slash-do. Continuing without it." -ForegroundColor Red
}
} else {
Write-SafeHost "Skipping slash-do install. You can install later with: npm install -g slash-do@latest"
}
} else {
Write-SafeHost "Skipping slash-do prompt (non-interactive). Install later with: npm install -g slash-do@latest"
}
Write-SafeHost ""
}
# Build UI assets for production serving
Step "build" "running" "Building client..."
Invoke-Logged npm run build
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Step "build" "done" "Client built"
Write-SafeHost ""
# Write completion marker atomically before restart so server reads it on boot
$Tag = (Get-Content package.json -Raw | ConvertFrom-Json).version
if (-not $Tag) {
Write-SafeHost "❌ Failed to determine package version from package.json" -ForegroundColor Red
exit 1
}
$completedAt = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$markerObj = @{ version = $Tag; completedAt = $completedAt }
$marker = $markerObj | ConvertTo-Json -Compress
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText("$RootDir\data\update-complete.json.tmp", $marker, $utf8NoBom)
Move-Item -Force "$RootDir\data\update-complete.json.tmp" "$RootDir\data\update-complete.json"
# Restart PM2 apps — remove marker if restart fails so it isn't misread on boot
Step "restart" "running" "Restarting PortOS..."
Invoke-Logged npm run pm2:restart
if ($LASTEXITCODE -ne 0) {
if (Test-Path "$RootDir\data\update-complete.json") {
Remove-Item -Force "$RootDir\data\update-complete.json"
}
exit $LASTEXITCODE
}
Step "restart" "done" "PortOS restarted"
Write-SafeHost ""
# Open the dashboard in the PortOS-managed browser. Fail-soft — explicitly
# reset $LASTEXITCODE to 0 after the call so a non-zero exit from the auto-
# open script doesn't propagate as the script's own exit code (the update
# is already complete by this point).
Invoke-Logged node scripts/open-ui-in-browser.js
$global:LASTEXITCODE = 0
Write-SafeHost "===================================" -ForegroundColor Green
Write-SafeHost " ✅ Update Complete!" -ForegroundColor Green
Write-SafeHost "===================================" -ForegroundColor Green
Write-SafeHost ""