easy-devops 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/cli/index.js +91 -0
- package/cli/managers/domain-manager.js +451 -0
- package/cli/managers/nginx-manager.js +329 -0
- package/cli/managers/node-manager.js +275 -0
- package/cli/managers/ssl-manager.js +397 -0
- package/cli/menus/.gitkeep +0 -0
- package/cli/menus/dashboard.js +223 -0
- package/cli/menus/domains.js +5 -0
- package/cli/menus/nginx.js +5 -0
- package/cli/menus/nodejs.js +5 -0
- package/cli/menus/settings.js +83 -0
- package/cli/menus/ssl.js +5 -0
- package/core/config.js +37 -0
- package/core/db.js +30 -0
- package/core/detector.js +257 -0
- package/core/nginx-conf-generator.js +309 -0
- package/core/shell.js +151 -0
- package/dashboard/lib/.gitkeep +0 -0
- package/dashboard/lib/cert-reader.js +59 -0
- package/dashboard/lib/domains-db.js +51 -0
- package/dashboard/lib/nginx-conf-generator.js +16 -0
- package/dashboard/lib/nginx-service.js +282 -0
- package/dashboard/public/js/app.js +486 -0
- package/dashboard/routes/.gitkeep +0 -0
- package/dashboard/routes/auth.js +30 -0
- package/dashboard/routes/domains.js +300 -0
- package/dashboard/routes/nginx.js +151 -0
- package/dashboard/routes/settings.js +78 -0
- package/dashboard/routes/ssl.js +105 -0
- package/dashboard/server.js +79 -0
- package/dashboard/views/index.ejs +327 -0
- package/dashboard/views/partials/domain-form.ejs +229 -0
- package/dashboard/views/partials/domains-panel.ejs +66 -0
- package/dashboard/views/partials/login.ejs +50 -0
- package/dashboard/views/partials/nginx-panel.ejs +90 -0
- package/dashboard/views/partials/overview.ejs +67 -0
- package/dashboard/views/partials/settings-panel.ejs +37 -0
- package/dashboard/views/partials/sidebar.ejs +45 -0
- package/dashboard/views/partials/ssl-panel.ejs +53 -0
- package/data/.gitkeep +0 -0
- package/install.bat +41 -0
- package/install.ps1 +653 -0
- package/install.sh +452 -0
- package/lib/installer/.gitkeep +0 -0
- package/lib/installer/detect.sh +88 -0
- package/lib/installer/node-versions.sh +109 -0
- package/lib/installer/nvm-bootstrap.sh +77 -0
- package/lib/installer/picker.sh +163 -0
- package/lib/installer/progress.sh +25 -0
- package/package.json +67 -0
package/install.ps1
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Easy DevOps Bootstrap Installer for Windows
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Installs Node.js (via nvm-windows), project dependencies, and registers
|
|
7
|
+
the easy-devops CLI command globally.
|
|
8
|
+
|
|
9
|
+
.PARAMETER Help
|
|
10
|
+
Print this help message and exit.
|
|
11
|
+
|
|
12
|
+
.PARAMETER Version
|
|
13
|
+
Skip the version picker and use the specified Node.js major version.
|
|
14
|
+
Example: -Version 20
|
|
15
|
+
|
|
16
|
+
.PARAMETER KeepNode
|
|
17
|
+
Skip Node.js management entirely. Proceeds directly to dependency
|
|
18
|
+
installation using whatever Node.js is currently active.
|
|
19
|
+
|
|
20
|
+
.EXAMPLE
|
|
21
|
+
.\install.ps1 # Interactive install
|
|
22
|
+
.\install.ps1 -Version 20 # Install Node.js 20.x (latest patch via nvm)
|
|
23
|
+
.\install.ps1 -KeepNode # Skip Node.js management
|
|
24
|
+
#>
|
|
25
|
+
param(
|
|
26
|
+
[switch]$Help,
|
|
27
|
+
[string]$Version = "",
|
|
28
|
+
[switch]$KeepNode
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
Set-StrictMode -Version Latest
|
|
32
|
+
$ErrorActionPreference = 'Stop'
|
|
33
|
+
|
|
34
|
+
$REQUIRED_NODE_MAJOR = 18
|
|
35
|
+
$NVM_WINDOWS_VERSION = '1.1.12'
|
|
36
|
+
$NODE_FALLBACK = '20'
|
|
37
|
+
|
|
38
|
+
# ─── Summary tracking (mirrors install.sh add_result) ────────────────────────
|
|
39
|
+
|
|
40
|
+
$stepResults = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
41
|
+
|
|
42
|
+
function Add-Result {
|
|
43
|
+
param([string]$Name, [bool]$OK, [string]$Detail = '')
|
|
44
|
+
$script:stepResults.Add([PSCustomObject]@{ Name = $Name; OK = $OK; Detail = $Detail })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ─── Output helpers ───────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
$script:currentStep = 0
|
|
50
|
+
$script:totalSteps = 7 # always 7; package-mode steps shown as skipped
|
|
51
|
+
|
|
52
|
+
function Write-Step {
|
|
53
|
+
param([string]$msg)
|
|
54
|
+
$script:currentStep++
|
|
55
|
+
Write-Host ""
|
|
56
|
+
Write-Host " [$script:currentStep/$script:totalSteps] $msg" -ForegroundColor Cyan
|
|
57
|
+
Write-Host " $('-' * 50)" -ForegroundColor DarkGray
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function Write-OK { param([string]$msg) Write-Host " OK $msg" -ForegroundColor Green }
|
|
61
|
+
function Write-Warn { param([string]$msg) Write-Host " WARN $msg" -ForegroundColor Yellow }
|
|
62
|
+
function Write-Err { param([string]$msg) Write-Host " ERROR $msg" -ForegroundColor Red }
|
|
63
|
+
function Write-Info { param([string]$msg) Write-Host " $msg" -ForegroundColor Gray }
|
|
64
|
+
|
|
65
|
+
function Refresh-Path {
|
|
66
|
+
$env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
|
|
67
|
+
[System.Environment]::GetEnvironmentVariable('Path', 'User')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Get-NodeMajor {
|
|
71
|
+
param([string]$version)
|
|
72
|
+
try { return [int](($version -replace '^v','').Split('.')[0]) } catch { return 0 }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# ─── Help output ──────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function Print-Help {
|
|
78
|
+
Write-Host ""
|
|
79
|
+
Write-Host "Easy DevOps Bootstrap Installer" -ForegroundColor Cyan
|
|
80
|
+
Write-Host ""
|
|
81
|
+
Write-Host "Usage:"
|
|
82
|
+
Write-Host " .\install.ps1 [OPTIONS]"
|
|
83
|
+
Write-Host ""
|
|
84
|
+
Write-Host "Options:"
|
|
85
|
+
Write-Host " -Help Print this help and exit"
|
|
86
|
+
Write-Host " -Version <ver> Skip the version picker; install specified Node.js major"
|
|
87
|
+
Write-Host " Example: -Version 20"
|
|
88
|
+
Write-Host " -KeepNode Skip Node.js management; use current Node.js on PATH"
|
|
89
|
+
Write-Host ""
|
|
90
|
+
Write-Host "Exit codes:"
|
|
91
|
+
Write-Host " 0 Installation completed successfully"
|
|
92
|
+
Write-Host " 1 Unrecoverable error"
|
|
93
|
+
Write-Host " 2 User cancelled"
|
|
94
|
+
Write-Host ""
|
|
95
|
+
Write-Host "Examples:"
|
|
96
|
+
Write-Host " .\install.ps1 # Interactive install"
|
|
97
|
+
Write-Host " .\install.ps1 -Version 20 # Install Node.js 20.x"
|
|
98
|
+
Write-Host " .\install.ps1 -KeepNode # Skip Node.js management"
|
|
99
|
+
Write-Host ""
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if ($Help) {
|
|
103
|
+
Print-Help
|
|
104
|
+
exit 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# ─── BITS/WebClient download with progress ────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
function Download-File {
|
|
110
|
+
param([string]$Url, [string]$Dest, [string]$Label)
|
|
111
|
+
|
|
112
|
+
$bitsOK = $false
|
|
113
|
+
try { Import-Module BitsTransfer -ErrorAction Stop; $bitsOK = $true } catch {}
|
|
114
|
+
|
|
115
|
+
if ($bitsOK) {
|
|
116
|
+
try {
|
|
117
|
+
Write-Info "Downloading $Label..."
|
|
118
|
+
Start-BitsTransfer `
|
|
119
|
+
-Source $Url `
|
|
120
|
+
-Destination $Dest `
|
|
121
|
+
-DisplayName "Easy DevOps Installer" `
|
|
122
|
+
-Description "Downloading $Label" `
|
|
123
|
+
-ErrorAction Stop
|
|
124
|
+
return
|
|
125
|
+
} catch {
|
|
126
|
+
Write-Info "BITS unavailable, falling back to WebClient..."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Write-Info "Downloading $Label..."
|
|
131
|
+
$wc = New-Object System.Net.WebClient
|
|
132
|
+
$wc.Headers.Add('User-Agent', 'EasyDevOps-Installer/1.0')
|
|
133
|
+
try { $wc.DownloadFile($Url, $Dest) }
|
|
134
|
+
finally { $wc.Dispose() }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# ─── Fetch Node.js LTS versions from nodejs.org ───────────────────────────────
|
|
138
|
+
|
|
139
|
+
$script:ltsVersions = $null
|
|
140
|
+
|
|
141
|
+
function Fetch-NodeVersions {
|
|
142
|
+
Write-Info "Fetching available Node.js LTS versions from nodejs.org..."
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
$releases = Invoke-RestMethod -Uri "https://nodejs.org/dist/index.json" -TimeoutSec 20
|
|
146
|
+
|
|
147
|
+
$script:ltsVersions = $releases |
|
|
148
|
+
Where-Object { $_.lts -and $_.lts -ne $false } |
|
|
149
|
+
Group-Object { ($_.version -replace '^v(\d+)\..*', '$1') } |
|
|
150
|
+
ForEach-Object {
|
|
151
|
+
$_.Group | Sort-Object { [System.Version]($_.version -replace '^v','') } -Descending |
|
|
152
|
+
Select-Object -First 1
|
|
153
|
+
} |
|
|
154
|
+
Sort-Object { [int]($_.version -replace '^v(\d+)\..*','$1') } -Descending |
|
|
155
|
+
Select-Object -First 6
|
|
156
|
+
|
|
157
|
+
if (-not $script:ltsVersions -or @($script:ltsVersions).Count -eq 0) {
|
|
158
|
+
Write-Warn "No LTS versions found in response."
|
|
159
|
+
return $false
|
|
160
|
+
}
|
|
161
|
+
return $true
|
|
162
|
+
} catch {
|
|
163
|
+
Write-Warn "Could not fetch version list: $_"
|
|
164
|
+
return $false
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# ─── Interactive version picker ───────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
function Select-NodeVersion {
|
|
171
|
+
param([string]$CurrentVersion = "")
|
|
172
|
+
|
|
173
|
+
$list = @($script:ltsVersions)
|
|
174
|
+
|
|
175
|
+
Write-Host ""
|
|
176
|
+
Write-Host " Available Node.js LTS versions:" -ForegroundColor Cyan
|
|
177
|
+
Write-Host ""
|
|
178
|
+
|
|
179
|
+
for ($i = 0; $i -lt $list.Count; $i++) {
|
|
180
|
+
$v = $list[$i]
|
|
181
|
+
$ltsName = if ($v.lts -is [string] -and $v.lts) { " ($($v.lts))" } else { "" }
|
|
182
|
+
$current = if ($CurrentVersion -and ($v.version -replace '^v(\d+)\..*','$1') -eq ($CurrentVersion -replace '^v(\d+)\..*','$1')) { " [current]" } else { "" }
|
|
183
|
+
$def = if ($i -eq 0) { " <- default" } else { "" }
|
|
184
|
+
Write-Host " [$($i+1)] $($v.version)$ltsName$current$def" -ForegroundColor White
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Write-Host ""
|
|
188
|
+
|
|
189
|
+
$majorVersion = $NODE_FALLBACK
|
|
190
|
+
while ($true) {
|
|
191
|
+
$raw = Read-Host " Choose a version [1-$($list.Count), press Enter for default]"
|
|
192
|
+
if ([string]::IsNullOrWhiteSpace($raw)) { $raw = "1" }
|
|
193
|
+
|
|
194
|
+
if ($raw -match '^\d+$') {
|
|
195
|
+
$idx = [int]$raw - 1
|
|
196
|
+
if ($idx -ge 0 -and $idx -lt $list.Count) {
|
|
197
|
+
$majorVersion = ($list[$idx].version -replace '^v(\d+)\..*', '$1')
|
|
198
|
+
Write-OK "Selected $($list[$idx].version)"
|
|
199
|
+
return $majorVersion
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
Write-Warn "Please enter a number between 1 and $($list.Count)"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# ─── Detect install mode ──────────────────────────────────────────────────────
|
|
207
|
+
# Source mode = running from a git-cloned directory -> need npm install + link
|
|
208
|
+
# Package mode = easy-devops already globally installed via npm
|
|
209
|
+
|
|
210
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
211
|
+
$isSourceMode = $true
|
|
212
|
+
$existingCmd = $null
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
$existingCmd = (& where.exe easy-devops 2>$null)
|
|
216
|
+
if ($LASTEXITCODE -eq 0 -and $existingCmd) {
|
|
217
|
+
$isSourceMode = $false
|
|
218
|
+
}
|
|
219
|
+
} catch {}
|
|
220
|
+
|
|
221
|
+
# ─── Banner ───────────────────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
Write-Host ""
|
|
224
|
+
Write-Host " ==========================================" -ForegroundColor Cyan
|
|
225
|
+
Write-Host " Easy DevOps -- Windows Installer" -ForegroundColor Cyan
|
|
226
|
+
Write-Host " ==========================================" -ForegroundColor Cyan
|
|
227
|
+
|
|
228
|
+
if (-not $isSourceMode) {
|
|
229
|
+
Write-Host ""
|
|
230
|
+
Write-Host " Mode: package (easy-devops already installed at $existingCmd)" -ForegroundColor DarkGray
|
|
231
|
+
Write-Host " Skipping npm install / npm link steps." -ForegroundColor DarkGray
|
|
232
|
+
} else {
|
|
233
|
+
Write-Host ""
|
|
234
|
+
Write-Host " Mode: source (installing from project directory)" -ForegroundColor DarkGray
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
# ─── Step 1: Detect system ───────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
Write-Step "Detecting system"
|
|
240
|
+
|
|
241
|
+
$osVer = [System.Environment]::OSVersion.Version
|
|
242
|
+
if ($osVer.Major -lt 10) {
|
|
243
|
+
Write-Err "Windows 10 or later is required (found $($osVer.Major).$($osVer.Minor))"
|
|
244
|
+
exit 1
|
|
245
|
+
}
|
|
246
|
+
Write-OK "Windows $($osVer.Major).$($osVer.Minor)"
|
|
247
|
+
Add-Result "System detection" $true "Windows $($osVer.Major).$($osVer.Minor)"
|
|
248
|
+
|
|
249
|
+
if ($PSVersionTable.PSVersion.Major -lt 5) {
|
|
250
|
+
Write-Err "PowerShell 5.1+ is required (found $($PSVersionTable.PSVersion))"
|
|
251
|
+
exit 1
|
|
252
|
+
}
|
|
253
|
+
Write-OK "PowerShell $($PSVersionTable.PSVersion)"
|
|
254
|
+
|
|
255
|
+
if (-not $KeepNode) {
|
|
256
|
+
try {
|
|
257
|
+
$null = Invoke-WebRequest -Uri "https://nodejs.org" -UseBasicParsing -TimeoutSec 10
|
|
258
|
+
Write-OK "Internet connectivity confirmed"
|
|
259
|
+
} catch {
|
|
260
|
+
Write-Err "No internet connection. Please check your network and try again."
|
|
261
|
+
exit 1
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
# Detect existing Node.js
|
|
266
|
+
$nodeOK = $false
|
|
267
|
+
$nodeVersion = $null
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
$raw = (& node --version 2>$null).Trim()
|
|
271
|
+
if ($LASTEXITCODE -eq 0 -and $raw -match '^v') {
|
|
272
|
+
$nodeVersion = $raw
|
|
273
|
+
$major = Get-NodeMajor $nodeVersion
|
|
274
|
+
if ($major -ge $REQUIRED_NODE_MAJOR) {
|
|
275
|
+
Write-OK "Node.js $nodeVersion -- compatible"
|
|
276
|
+
$nodeOK = $true
|
|
277
|
+
} else {
|
|
278
|
+
Write-Warn "Node.js $nodeVersion found but v$REQUIRED_NODE_MAJOR+ is required"
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
Write-Info "Node.js not found on PATH"
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
Write-Info "Node.js not found on PATH"
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# ─── Step 2: Fetch Node.js release list ──────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
$NODE_ACTION = "" # keep | upgrade | switch
|
|
290
|
+
$NODE_TARGET = "" # major version string
|
|
291
|
+
|
|
292
|
+
if ($KeepNode) {
|
|
293
|
+
Write-Step "Fetching Node.js release list"
|
|
294
|
+
Write-OK "Skipped (--KeepNode)"
|
|
295
|
+
Add-Result "Node.js release list" $true "Skipped (-KeepNode)"
|
|
296
|
+
$NODE_ACTION = "keep"
|
|
297
|
+
} elseif ($Version -ne "") {
|
|
298
|
+
Write-Step "Fetching Node.js release list"
|
|
299
|
+
Write-OK "Skipped (-Version $Version specified)"
|
|
300
|
+
Add-Result "Node.js release list" $true "Skipped (-Version $Version)"
|
|
301
|
+
$NODE_ACTION = "switch"
|
|
302
|
+
$NODE_TARGET = $Version
|
|
303
|
+
} else {
|
|
304
|
+
Write-Step "Fetching Node.js release list"
|
|
305
|
+
$fetchOK = Fetch-NodeVersions
|
|
306
|
+
if (-not $fetchOK) {
|
|
307
|
+
Write-Warn "Using fallback version $NODE_FALLBACK."
|
|
308
|
+
$script:ltsVersions = @([PSCustomObject]@{ version = "v$NODE_FALLBACK.0.0"; lts = "LTS" })
|
|
309
|
+
}
|
|
310
|
+
Add-Result "Node.js release list" $true "@($script:ltsVersions).Count LTS versions"
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
# ─── Step 3: Node.js version selection ───────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
if ($NODE_ACTION -eq "keep") {
|
|
316
|
+
Write-Step "Node.js version selection"
|
|
317
|
+
Write-OK "Skipped (-KeepNode)"
|
|
318
|
+
Add-Result "Node.js selection" $true "Skipped (-KeepNode)"
|
|
319
|
+
} elseif ($Version -ne "") {
|
|
320
|
+
Write-Step "Node.js version selection"
|
|
321
|
+
Write-OK "Skipped (using -Version $Version)"
|
|
322
|
+
Add-Result "Node.js selection" $true "$Version"
|
|
323
|
+
} else {
|
|
324
|
+
Write-Step "Node.js version selection"
|
|
325
|
+
|
|
326
|
+
if ($nodeOK) {
|
|
327
|
+
# Node >= 18 already installed: 3-option menu (mirrors install.sh)
|
|
328
|
+
Write-Host ""
|
|
329
|
+
Write-Host " Node.js $nodeVersion is already installed." -ForegroundColor White
|
|
330
|
+
Write-Host " What would you like to do?" -ForegroundColor White
|
|
331
|
+
Write-Host ""
|
|
332
|
+
Write-Host " [1] Keep current version ($nodeVersion)" -ForegroundColor White
|
|
333
|
+
Write-Host " [2] Upgrade to latest LTS automatically" -ForegroundColor White
|
|
334
|
+
Write-Host " [3] Switch to a different version (picker)" -ForegroundColor White
|
|
335
|
+
Write-Host ""
|
|
336
|
+
|
|
337
|
+
while ($true) {
|
|
338
|
+
$raw = Read-Host " Enter 1, 2, or 3 (q to quit)"
|
|
339
|
+
switch ($raw.Trim()) {
|
|
340
|
+
"1" {
|
|
341
|
+
$NODE_ACTION = "keep"
|
|
342
|
+
$NODE_TARGET = ""
|
|
343
|
+
break
|
|
344
|
+
}
|
|
345
|
+
"2" {
|
|
346
|
+
$NODE_ACTION = "upgrade"
|
|
347
|
+
# Pick the newest LTS from the fetched list
|
|
348
|
+
$list = @($script:ltsVersions)
|
|
349
|
+
if ($list.Count -gt 0) {
|
|
350
|
+
$NODE_TARGET = ($list[0].version -replace '^v(\d+)\..*','$1')
|
|
351
|
+
} else {
|
|
352
|
+
$NODE_TARGET = $NODE_FALLBACK
|
|
353
|
+
}
|
|
354
|
+
Write-OK "Upgrading to latest LTS (major $NODE_TARGET)"
|
|
355
|
+
break
|
|
356
|
+
}
|
|
357
|
+
"3" {
|
|
358
|
+
$NODE_ACTION = "switch"
|
|
359
|
+
$NODE_TARGET = Select-NodeVersion -CurrentVersion $nodeVersion
|
|
360
|
+
break
|
|
361
|
+
}
|
|
362
|
+
{ $_ -eq "q" -or $_ -eq "Q" } {
|
|
363
|
+
Write-Host ""
|
|
364
|
+
Write-Host " Installation cancelled by user." -ForegroundColor Yellow
|
|
365
|
+
exit 2
|
|
366
|
+
}
|
|
367
|
+
default {
|
|
368
|
+
Write-Warn "Invalid choice. Please enter 1, 2, or 3."
|
|
369
|
+
continue
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
break
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if ($NODE_ACTION -eq "keep") {
|
|
376
|
+
Write-OK "Keeping Node.js $nodeVersion"
|
|
377
|
+
Add-Result "Node.js selection" $true "Keep $nodeVersion"
|
|
378
|
+
} else {
|
|
379
|
+
Add-Result "Node.js selection" $true "$NODE_ACTION -> $NODE_TARGET"
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
# Node not installed or below 18: go straight to picker
|
|
383
|
+
if ($script:ltsVersions) {
|
|
384
|
+
$NODE_TARGET = Select-NodeVersion
|
|
385
|
+
} else {
|
|
386
|
+
$NODE_TARGET = $NODE_FALLBACK
|
|
387
|
+
Write-OK "Using default Node.js $NODE_TARGET"
|
|
388
|
+
}
|
|
389
|
+
$NODE_ACTION = "switch"
|
|
390
|
+
Add-Result "Node.js selection" $true "$NODE_TARGET"
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
# ─── Step 4: Install nvm-windows ─────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
$nvmReady = $false
|
|
397
|
+
$nvmVersion = $null
|
|
398
|
+
|
|
399
|
+
if ($NODE_ACTION -eq "keep") {
|
|
400
|
+
Write-Step "Installing nvm-windows"
|
|
401
|
+
Write-OK "Skipped (keeping current Node.js)"
|
|
402
|
+
Add-Result "nvm-windows" $true "Skipped (keep)"
|
|
403
|
+
$nvmReady = $true
|
|
404
|
+
} else {
|
|
405
|
+
Write-Step "Installing nvm-windows"
|
|
406
|
+
|
|
407
|
+
# Check if nvm-windows is already present
|
|
408
|
+
try {
|
|
409
|
+
$nvmVersion = (& nvm version 2>$null).Trim()
|
|
410
|
+
if ($LASTEXITCODE -eq 0 -and $nvmVersion) {
|
|
411
|
+
Write-OK "nvm-windows $nvmVersion already installed"
|
|
412
|
+
Add-Result "nvm-windows" $true $nvmVersion
|
|
413
|
+
$nvmReady = $true
|
|
414
|
+
}
|
|
415
|
+
} catch {}
|
|
416
|
+
|
|
417
|
+
if (-not $nvmReady) {
|
|
418
|
+
$skipNvm = $false
|
|
419
|
+
|
|
420
|
+
if ($nodeOK) {
|
|
421
|
+
Write-Host ""
|
|
422
|
+
Write-Host " nvm-windows is not installed." -ForegroundColor Yellow
|
|
423
|
+
Write-Host " Required for 'nvm install $NODE_TARGET'. Install now?" -ForegroundColor Gray
|
|
424
|
+
Write-Host ""
|
|
425
|
+
$answer = Read-Host " Install nvm-windows? [Y/n]"
|
|
426
|
+
if ($answer -match '^[Nn]') {
|
|
427
|
+
Write-Info "Skipping nvm-windows."
|
|
428
|
+
Add-Result "nvm-windows" $true "Skipped (optional)"
|
|
429
|
+
$skipNvm = $true
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (-not $skipNvm) {
|
|
434
|
+
$installer = "$env:TEMP\nvm-setup.exe"
|
|
435
|
+
$nvmUrl = "https://github.com/coreybutler/nvm-windows/releases/download/$NVM_WINDOWS_VERSION/nvm-setup.exe"
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
Download-File -Url $nvmUrl -Dest $installer -Label "nvm-windows $NVM_WINDOWS_VERSION"
|
|
439
|
+
Write-OK "Downloaded"
|
|
440
|
+
} catch {
|
|
441
|
+
Write-Err "Download failed: $_"
|
|
442
|
+
Write-Info "Manual: https://github.com/coreybutler/nvm-windows/releases"
|
|
443
|
+
Add-Result "nvm-windows" $false "Download failed"
|
|
444
|
+
if (-not $nodeOK) { exit 1 }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (Test-Path $installer) {
|
|
448
|
+
try {
|
|
449
|
+
Write-Info "Running installer silently..."
|
|
450
|
+
$proc = Start-Process -FilePath $installer -ArgumentList '/S' -Wait -PassThru
|
|
451
|
+
if ($proc.ExitCode -ne 0) {
|
|
452
|
+
Write-Err "Installer exited with code $($proc.ExitCode)"
|
|
453
|
+
Add-Result "nvm-windows" $false "Exit code $($proc.ExitCode)"
|
|
454
|
+
if (-not $nodeOK) { exit 1 }
|
|
455
|
+
} else {
|
|
456
|
+
Write-OK "nvm-windows $NVM_WINDOWS_VERSION installed"
|
|
457
|
+
Add-Result "nvm-windows" $true $NVM_WINDOWS_VERSION
|
|
458
|
+
$nvmReady = $true
|
|
459
|
+
}
|
|
460
|
+
} catch {
|
|
461
|
+
Write-Err "Installer failed: $_"
|
|
462
|
+
Add-Result "nvm-windows" $false "$_"
|
|
463
|
+
if (-not $nodeOK) { exit 1 }
|
|
464
|
+
} finally {
|
|
465
|
+
try { Remove-Item $installer -Force -ErrorAction SilentlyContinue } catch {}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
Refresh-Path
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
# ─── Step 5: Install Node.js via nvm ─────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
if ($NODE_ACTION -eq "keep") {
|
|
477
|
+
Write-Step "Installing Node.js via nvm"
|
|
478
|
+
Write-OK "Skipped (keeping current Node.js)"
|
|
479
|
+
Add-Result "Node.js install" $true "Skipped (keep)"
|
|
480
|
+
} else {
|
|
481
|
+
Write-Step "Installing Node.js via nvm"
|
|
482
|
+
|
|
483
|
+
if ($nvmReady -and -not $nodeOK) {
|
|
484
|
+
# No compatible Node.js: install chosen version
|
|
485
|
+
Write-Info "Installing Node.js $NODE_TARGET via nvm..."
|
|
486
|
+
try {
|
|
487
|
+
& nvm install $NODE_TARGET 2>&1 | ForEach-Object { Write-Info " $_" }
|
|
488
|
+
& nvm use $NODE_TARGET 2>&1 | ForEach-Object { Write-Info " $_" }
|
|
489
|
+
Refresh-Path
|
|
490
|
+
|
|
491
|
+
$raw = (& node --version 2>$null).Trim()
|
|
492
|
+
if ($raw -match '^v') {
|
|
493
|
+
$nodeVersion = $raw
|
|
494
|
+
Write-OK "Node.js $nodeVersion installed and active"
|
|
495
|
+
Add-Result "Node.js install" $true $nodeVersion
|
|
496
|
+
$nodeOK = $true
|
|
497
|
+
} else {
|
|
498
|
+
Write-Warn "nvm install ran but node is not yet on PATH"
|
|
499
|
+
Write-Warn "Open a NEW terminal, then run: nvm use $NODE_TARGET"
|
|
500
|
+
Add-Result "Node.js install" $false "PATH refresh needed -- open a new terminal"
|
|
501
|
+
}
|
|
502
|
+
} catch {
|
|
503
|
+
Write-Warn "nvm node install error: $_"
|
|
504
|
+
Write-Warn "Open a new terminal and run: nvm install $NODE_TARGET"
|
|
505
|
+
Add-Result "Node.js install" $false "Manual step needed"
|
|
506
|
+
}
|
|
507
|
+
} elseif ($nvmReady -and $nodeOK -and ($NODE_ACTION -eq "upgrade" -or $NODE_ACTION -eq "switch")) {
|
|
508
|
+
# Upgrade or switch: install the selected version
|
|
509
|
+
Write-Info "Installing Node.js $NODE_TARGET via nvm..."
|
|
510
|
+
try {
|
|
511
|
+
& nvm install $NODE_TARGET 2>&1 | ForEach-Object { Write-Info " $_" }
|
|
512
|
+
& nvm use $NODE_TARGET 2>&1 | ForEach-Object { Write-Info " $_" }
|
|
513
|
+
Refresh-Path
|
|
514
|
+
|
|
515
|
+
$raw = (& node --version 2>$null).Trim()
|
|
516
|
+
if ($raw -match '^v') {
|
|
517
|
+
$nodeVersion = $raw
|
|
518
|
+
Write-OK "Node.js $nodeVersion active"
|
|
519
|
+
Add-Result "Node.js install" $true $nodeVersion
|
|
520
|
+
$nodeOK = $true
|
|
521
|
+
} else {
|
|
522
|
+
Write-Warn "node not yet on PATH -- open a new terminal and run: nvm use $NODE_TARGET"
|
|
523
|
+
Add-Result "Node.js install" $false "PATH refresh needed"
|
|
524
|
+
$nodeOK = $false
|
|
525
|
+
}
|
|
526
|
+
} catch {
|
|
527
|
+
Write-Warn "nvm error: $_ -- open a new terminal and run: nvm use $NODE_TARGET"
|
|
528
|
+
Add-Result "Node.js install" $false "Manual step needed"
|
|
529
|
+
$nodeOK = $false
|
|
530
|
+
}
|
|
531
|
+
} elseif (-not $nvmReady -and $nodeOK) {
|
|
532
|
+
# nvm not available but Node >= 18 already present: skip
|
|
533
|
+
Write-OK "nvm not available; using existing Node.js $nodeVersion"
|
|
534
|
+
Add-Result "Node.js install" $true "Using existing $nodeVersion"
|
|
535
|
+
} else {
|
|
536
|
+
Write-Warn "No Node.js installed and nvm not available."
|
|
537
|
+
Write-Info "Install Node.js manually from: https://nodejs.org"
|
|
538
|
+
Add-Result "Node.js install" $false "Manual install required"
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
# ─── Source-mode steps ────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
if (-not $isSourceMode) {
|
|
545
|
+
# Package mode: steps 6+7 skipped
|
|
546
|
+
Write-Step "Installing Easy DevOps dependencies"
|
|
547
|
+
Write-OK "Skipped (package mode -- easy-devops already installed)"
|
|
548
|
+
Add-Result "npm install" $true "Skipped (package mode)"
|
|
549
|
+
|
|
550
|
+
Write-Step "Registering global command"
|
|
551
|
+
Write-OK "Skipped (package mode)"
|
|
552
|
+
Add-Result "CLI registered" $true "Skipped (package mode)"
|
|
553
|
+
} else {
|
|
554
|
+
|
|
555
|
+
# ─── Step 6: npm install ─────────────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
Write-Step "Installing Easy DevOps dependencies"
|
|
558
|
+
|
|
559
|
+
$packageJson = Join-Path $scriptDir "package.json"
|
|
560
|
+
if (-not (Test-Path $packageJson)) {
|
|
561
|
+
Write-Err "package.json not found at: $packageJson"
|
|
562
|
+
Write-Err "Run this installer from the Easy DevOps project root."
|
|
563
|
+
exit 1
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (-not $nodeOK) {
|
|
567
|
+
Write-Warn "Skipping -- Node.js is not ready. Re-run install.ps1 after setting up Node.js."
|
|
568
|
+
Add-Result "npm install" $false "Skipped -- Node.js not ready"
|
|
569
|
+
} else {
|
|
570
|
+
try {
|
|
571
|
+
Push-Location $scriptDir
|
|
572
|
+
Write-Info "Running npm install..."
|
|
573
|
+
& npm install
|
|
574
|
+
if ($LASTEXITCODE -ne 0) {
|
|
575
|
+
Write-Err "npm install failed (exit code $LASTEXITCODE)"
|
|
576
|
+
Add-Result "npm install" $false "Exit code $LASTEXITCODE"
|
|
577
|
+
exit 1
|
|
578
|
+
}
|
|
579
|
+
Write-OK "All dependencies installed"
|
|
580
|
+
Add-Result "npm install" $true ""
|
|
581
|
+
} catch {
|
|
582
|
+
Write-Err "npm install error: $_"
|
|
583
|
+
Add-Result "npm install" $false "$_"
|
|
584
|
+
exit 1
|
|
585
|
+
} finally {
|
|
586
|
+
Pop-Location
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
# ─── Step 7: npm link ────────────────────────────────────────────────────────
|
|
591
|
+
|
|
592
|
+
Write-Step "Registering global command"
|
|
593
|
+
|
|
594
|
+
if (-not $nodeOK) {
|
|
595
|
+
Write-Warn "Skipping -- Node.js is not ready"
|
|
596
|
+
Add-Result "CLI registered" $false "Skipped -- Node.js not ready"
|
|
597
|
+
} else {
|
|
598
|
+
try {
|
|
599
|
+
Push-Location $scriptDir
|
|
600
|
+
& npm link
|
|
601
|
+
if ($LASTEXITCODE -ne 0) {
|
|
602
|
+
Write-Warn "npm link failed -- CLI won't be globally available"
|
|
603
|
+
Write-Warn "You can still run: node cli/index.js"
|
|
604
|
+
Add-Result "CLI registered" $false "Exit code $LASTEXITCODE"
|
|
605
|
+
} else {
|
|
606
|
+
Write-OK "easy-devops command linked globally"
|
|
607
|
+
Add-Result "CLI registered" $true ""
|
|
608
|
+
}
|
|
609
|
+
} catch {
|
|
610
|
+
Write-Warn "npm link failed: $_ -- run: node cli/index.js"
|
|
611
|
+
Add-Result "CLI registered" $false "$_"
|
|
612
|
+
} finally {
|
|
613
|
+
Pop-Location
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
} # end source-mode block
|
|
618
|
+
|
|
619
|
+
# ─── Summary (mirrors install.sh summary block) ───────────────────────────────
|
|
620
|
+
|
|
621
|
+
Write-Host ""
|
|
622
|
+
Write-Host " ==========================================" -ForegroundColor Cyan
|
|
623
|
+
Write-Host " Installation Summary" -ForegroundColor Cyan
|
|
624
|
+
Write-Host " ==========================================" -ForegroundColor Cyan
|
|
625
|
+
Write-Host ""
|
|
626
|
+
|
|
627
|
+
$allOK = $true
|
|
628
|
+
foreach ($r in $stepResults) {
|
|
629
|
+
if ($r.OK) {
|
|
630
|
+
$icon = " OK "
|
|
631
|
+
$color = 'Green'
|
|
632
|
+
} else {
|
|
633
|
+
$icon = "FAIL"
|
|
634
|
+
$color = 'Yellow'
|
|
635
|
+
$allOK = $false
|
|
636
|
+
}
|
|
637
|
+
$detail = if ($r.Detail) { " ($($r.Detail))" } else { "" }
|
|
638
|
+
Write-Host " [$icon] $($r.Name)$detail" -ForegroundColor $color
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
Write-Host ""
|
|
642
|
+
if ($allOK) {
|
|
643
|
+
Write-Host " All steps completed successfully!" -ForegroundColor Green
|
|
644
|
+
Write-Host ""
|
|
645
|
+
Write-Host " Run the CLI:" -ForegroundColor White
|
|
646
|
+
Write-Host " easy-devops" -ForegroundColor Cyan
|
|
647
|
+
} else {
|
|
648
|
+
Write-Host " Some steps need attention -- see warnings above." -ForegroundColor Yellow
|
|
649
|
+
Write-Host ""
|
|
650
|
+
Write-Host " Fallback:" -ForegroundColor White
|
|
651
|
+
Write-Host " node cli/index.js" -ForegroundColor Cyan
|
|
652
|
+
}
|
|
653
|
+
Write-Host ""
|