easy-devops 0.1.0 → 0.1.1

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.
@@ -222,23 +222,95 @@ async function stopNginx(nginxDir) {
222
222
  // ─── installNginx ─────────────────────────────────────────────────────────────
223
223
 
224
224
  async function installNginx() {
225
- const spinner = ora('Installing nginx…').start();
226
-
227
- const cmd = isWindows
228
- ? 'winget install -e --id Nginx.Nginx --accept-package-agreements --accept-source-agreements'
229
- : 'sudo apt-get install -y nginx';
230
-
231
- const result = await run(cmd, { timeout: 120000 });
232
-
233
- if (result.success) {
234
- spinner.succeed('nginx installed successfully');
235
- return { success: true, message: 'nginx installed successfully', output: result.stdout };
236
- } else {
225
+ if (!isWindows) {
226
+ const spinner = ora('Installing nginx…').start();
227
+ const result = await run('sudo apt-get install -y nginx', { timeout: 120000 });
228
+ if (result.success) {
229
+ spinner.succeed('nginx installed successfully');
230
+ return { success: true, message: 'nginx installed successfully', output: result.stdout };
231
+ }
237
232
  spinner.fail('Installation failed');
238
233
  console.log(chalk.red(result.stderr || result.stdout));
239
234
  console.log(chalk.gray('\n Manual instructions: https://nginx.org/en/docs/install.html\n'));
240
235
  return { success: false, message: 'Installation failed', output: result.stderr || result.stdout };
241
236
  }
237
+
238
+ // ── Windows ──────────────────────────────────────────────────────────────────
239
+ const spinner = ora('Checking for winget…').start();
240
+
241
+ // Try winget first (available on Windows 10/11 desktop; not on Windows Server by default)
242
+ const wingetCheck = await run('where.exe winget 2>$null');
243
+ const hasWinget = wingetCheck.success && wingetCheck.stdout.trim().length > 0;
244
+
245
+ if (hasWinget) {
246
+ spinner.text = 'Installing nginx via winget…';
247
+ const result = await run(
248
+ 'winget install -e --id Nginx.Nginx --accept-package-agreements --accept-source-agreements',
249
+ { timeout: 120000 },
250
+ );
251
+ if (result.success) {
252
+ spinner.succeed('nginx installed successfully');
253
+ return { success: true, message: 'nginx installed successfully', output: result.stdout };
254
+ }
255
+ spinner.fail('winget install failed');
256
+ console.log(chalk.red(result.stderr || result.stdout));
257
+ console.log(chalk.gray('\n Manual instructions: https://nginx.org/en/docs/install.html\n'));
258
+ return { success: false, message: 'Installation failed', output: result.stderr || result.stdout };
259
+ }
260
+
261
+ // ── Fallback: direct download from nginx.org ─────────────────────────────────
262
+ spinner.text = 'Fetching latest nginx version…';
263
+
264
+ // Try to resolve the current stable version; fall back to a known-good release
265
+ const FALLBACK_VERSION = '1.26.3';
266
+ let nginxVersion = FALLBACK_VERSION;
267
+
268
+ const fetchVersionResult = await run(
269
+ `try { $p=(Invoke-WebRequest -Uri 'https://nginx.org/en/download.html' -UseBasicParsing -TimeoutSec 15).Content; if($p -match 'nginx-(\\d+\\.\\d+\\.\\d+)\\.zip'){$Matches[1]}else{''} } catch { '' }`,
270
+ { timeout: 20000 },
271
+ );
272
+ const fetched = (fetchVersionResult.stdout || '').trim();
273
+ if (/^\d+\.\d+\.\d+$/.test(fetched)) nginxVersion = fetched;
274
+
275
+ const { nginxDir } = loadConfig();
276
+ const zipUrl = `https://nginx.org/download/nginx-${nginxVersion}.zip`;
277
+
278
+ spinner.text = `Downloading nginx ${nginxVersion}…`;
279
+
280
+ const downloadResult = await run(
281
+ `$ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '${zipUrl}' -OutFile "$env:TEMP\\nginx-${nginxVersion}.zip" -UseBasicParsing -TimeoutSec 120`,
282
+ { timeout: 130000 },
283
+ );
284
+
285
+ if (!downloadResult.success) {
286
+ spinner.fail('Download failed');
287
+ console.log(chalk.red(downloadResult.stderr || downloadResult.stdout));
288
+ console.log(chalk.gray(`\n Download nginx manually from: https://nginx.org/en/download.html\n`));
289
+ return { success: false, message: 'Download failed', output: downloadResult.stderr || downloadResult.stdout };
290
+ }
291
+
292
+ spinner.text = 'Extracting nginx…';
293
+
294
+ const extractResult = await run(
295
+ `$ProgressPreference='SilentlyContinue'; $tmp="$env:TEMP\\nginx-extract-${nginxVersion}"; if(Test-Path $tmp){Remove-Item -Recurse -Force $tmp}; Expand-Archive -Path "$env:TEMP\\nginx-${nginxVersion}.zip" -DestinationPath $tmp -Force; $src=Join-Path $tmp 'nginx-${nginxVersion}'; if(-not(Test-Path '${nginxDir}')){New-Item -ItemType Directory -Force '${nginxDir}'|Out-Null}; Copy-Item -Path "$src\\*" -Destination '${nginxDir}' -Recurse -Force; Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue; Remove-Item -Force "$env:TEMP\\nginx-${nginxVersion}.zip" -ErrorAction SilentlyContinue`,
296
+ { timeout: 60000 },
297
+ );
298
+
299
+ if (!extractResult.success) {
300
+ spinner.fail('Extraction failed');
301
+ console.log(chalk.red(extractResult.stderr || extractResult.stdout));
302
+ return { success: false, message: 'Extraction failed', output: extractResult.stderr || extractResult.stdout };
303
+ }
304
+
305
+ // Verify nginx.exe is present
306
+ const verifyResult = await run(`Test-Path '${nginxDir}\\nginx.exe'`);
307
+ if (!verifyResult.success || !verifyResult.stdout.trim().toLowerCase().includes('true')) {
308
+ spinner.fail(`nginx.exe not found in ${nginxDir} after extraction`);
309
+ return { success: false, message: 'nginx.exe not found after extraction' };
310
+ }
311
+
312
+ spinner.succeed(`nginx ${nginxVersion} installed to ${nginxDir}`);
313
+ return { success: true, message: `nginx ${nginxVersion} installed successfully`, output: '' };
242
314
  }
243
315
 
244
316
  // ─── showNginxManager ─────────────────────────────────────────────────────────
package/install.ps1 CHANGED
@@ -63,8 +63,54 @@ function Write-Err { param([string]$msg) Write-Host " ERROR $msg" -Foreg
63
63
  function Write-Info { param([string]$msg) Write-Host " $msg" -ForegroundColor Gray }
64
64
 
65
65
  function Refresh-Path {
66
- $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
67
- [System.Environment]::GetEnvironmentVariable('Path', 'User')
66
+ # Read NVM vars from User scope, fall back to Machine scope
67
+ # (nvm-windows writes to Machine on system-wide installs)
68
+ $nvmHome = [System.Environment]::GetEnvironmentVariable('NVM_HOME', 'User')
69
+ if (-not $nvmHome) { $nvmHome = [System.Environment]::GetEnvironmentVariable('NVM_HOME', 'Machine') }
70
+ $nvmSymlink = [System.Environment]::GetEnvironmentVariable('NVM_SYMLINK', 'User')
71
+ if (-not $nvmSymlink) { $nvmSymlink = [System.Environment]::GetEnvironmentVariable('NVM_SYMLINK', 'Machine') }
72
+
73
+ if ($nvmHome) { $env:NVM_HOME = $nvmHome }
74
+ if ($nvmSymlink) { $env:NVM_SYMLINK = $nvmSymlink }
75
+
76
+ $raw = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' +
77
+ [System.Environment]::GetEnvironmentVariable('Path', 'User')
78
+
79
+ # Expand literal %NVM_HOME% / %NVM_SYMLINK% tokens nvm-windows may have written
80
+ if ($nvmHome) { $raw = $raw -ireplace [regex]::Escape('%NVM_HOME%'), $nvmHome }
81
+ if ($nvmSymlink) { $raw = $raw -ireplace [regex]::Escape('%NVM_SYMLINK%'), $nvmSymlink }
82
+
83
+ $env:Path = $raw
84
+
85
+ # Ensure NVM_SYMLINK is in PATH even if the registry entry was already absolute
86
+ if ($nvmSymlink -and (Test-Path $nvmSymlink -ErrorAction SilentlyContinue)) {
87
+ if ($env:Path -notlike "*$([regex]::Escape($nvmSymlink))*") {
88
+ $env:Path = "$nvmSymlink;$env:Path"
89
+ }
90
+ }
91
+ }
92
+
93
+ # Find nvm.exe: checks PATH first, then known install locations
94
+ function Find-NvmExe {
95
+ try {
96
+ $f = (& where.exe nvm 2>$null)
97
+ if ($LASTEXITCODE -eq 0 -and $f) { return 'nvm' }
98
+ } catch {}
99
+
100
+ $candidates = @()
101
+ if ($env:NVM_HOME) { $candidates += Join-Path $env:NVM_HOME 'nvm.exe' }
102
+ if ($env:APPDATA) { $candidates += Join-Path $env:APPDATA 'nvm\nvm.exe' }
103
+ if ($env:ProgramData) { $candidates += Join-Path $env:ProgramData 'nvm\nvm.exe' }
104
+ $candidates += 'C:\ProgramData\nvm\nvm.exe'
105
+
106
+ foreach ($c in $candidates) {
107
+ if (Test-Path $c -ErrorAction SilentlyContinue) {
108
+ $dir = Split-Path $c -Parent
109
+ if ($env:Path -notlike "*$dir*") { $env:Path = "$dir;$env:Path" }
110
+ return $c
111
+ }
112
+ }
113
+ return $null
68
114
  }
69
115
 
70
116
  function Get-NodeMajor {
@@ -204,18 +250,29 @@ function Select-NodeVersion {
204
250
  }
205
251
 
206
252
  # ─── 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
253
+ # source = running from a git-cloned project directory -> npm install + npm link
254
+ # update = easy-devops already on PATH -> skip install steps
255
+ # npm = downloaded installer standalone -> npm install -g easy-devops
256
+
257
+ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
258
+ $packageJson = Join-Path $scriptDir 'package.json'
209
259
 
210
- $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
211
- $isSourceMode = $true
212
- $existingCmd = $null
260
+ $isSourceMode = $false
261
+ $isAlreadyInstalled = $false
262
+ $existingCmd = $null
213
263
 
264
+ # Source mode: script dir has this project's package.json
265
+ if (Test-Path $packageJson) {
266
+ try {
267
+ $pkg = Get-Content $packageJson -Raw | ConvertFrom-Json
268
+ if ($pkg.name -eq 'easy-devops') { $isSourceMode = $true }
269
+ } catch {}
270
+ }
271
+
272
+ # Already installed: easy-devops on PATH
214
273
  try {
215
274
  $existingCmd = (& where.exe easy-devops 2>$null)
216
- if ($LASTEXITCODE -eq 0 -and $existingCmd) {
217
- $isSourceMode = $false
218
- }
275
+ if ($LASTEXITCODE -eq 0 -and $existingCmd) { $isAlreadyInstalled = $true }
219
276
  } catch {}
220
277
 
221
278
  # ─── Banner ───────────────────────────────────────────────────────────────────
@@ -225,13 +282,14 @@ Write-Host " ==========================================" -ForegroundColor Cyan
225
282
  Write-Host " Easy DevOps -- Windows Installer" -ForegroundColor Cyan
226
283
  Write-Host " ==========================================" -ForegroundColor Cyan
227
284
 
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
285
+ Write-Host ""
286
+ if ($isAlreadyInstalled) {
287
+ Write-Host " Mode: update (easy-devops already installed at $existingCmd)" -ForegroundColor DarkGray
288
+ Write-Host " Node.js will still be managed; npm steps skipped." -ForegroundColor DarkGray
289
+ } elseif ($isSourceMode) {
290
+ Write-Host " Mode: source (project directory -- npm install + npm link)" -ForegroundColor DarkGray
232
291
  } else {
233
- Write-Host ""
234
- Write-Host " Mode: source (installing from project directory)" -ForegroundColor DarkGray
292
+ Write-Host " Mode: npm (will run: npm install -g easy-devops)" -ForegroundColor DarkGray
235
293
  }
236
294
 
237
295
  # ─── Step 1: Detect system ───────────────────────────────────────────────────
@@ -307,7 +365,7 @@ if ($KeepNode) {
307
365
  Write-Warn "Using fallback version $NODE_FALLBACK."
308
366
  $script:ltsVersions = @([PSCustomObject]@{ version = "v$NODE_FALLBACK.0.0"; lts = "LTS" })
309
367
  }
310
- Add-Result "Node.js release list" $true "@($script:ltsVersions).Count LTS versions"
368
+ Add-Result "Node.js release list" $true "$(@($script:ltsVersions).Count) LTS versions fetched"
311
369
  }
312
370
 
313
371
  # ─── Step 3: Node.js version selection ───────────────────────────────────────
@@ -404,15 +462,35 @@ if ($NODE_ACTION -eq "keep") {
404
462
  } else {
405
463
  Write-Step "Installing nvm-windows"
406
464
 
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
465
+ # Check if nvm-windows is already present (check PATH + known locations)
466
+ Refresh-Path
467
+ $nvmExeCheck = Find-NvmExe
468
+ if ($nvmExeCheck) {
469
+ try {
470
+ $nvmVersion = (& $nvmExeCheck version 2>$null).Trim()
471
+ if ($nvmVersion) {
472
+ Write-OK "nvm-windows $nvmVersion already installed"
473
+ Add-Result "nvm-windows" $true $nvmVersion
474
+ $nvmReady = $true
475
+ }
476
+ } catch {}
477
+ }
478
+
479
+ if (-not $nvmReady) {
480
+ # After Refresh-Path, try locating nvm at known paths
481
+ Refresh-Path
482
+ $nvmExePath = Find-NvmExe
483
+ if ($nvmExePath) {
484
+ try {
485
+ $nvmVersion = (& $nvmExePath version 2>$null).Trim()
486
+ if ($nvmVersion) {
487
+ Write-OK "nvm-windows $nvmVersion already installed (found after PATH refresh)"
488
+ Add-Result "nvm-windows" $true $nvmVersion
489
+ $nvmReady = $true
490
+ }
491
+ } catch {}
414
492
  }
415
- } catch {}
493
+ }
416
494
 
417
495
  if (-not $nvmReady) {
418
496
  $skipNvm = $false
@@ -480,53 +558,60 @@ if ($NODE_ACTION -eq "keep") {
480
558
  } else {
481
559
  Write-Step "Installing Node.js via nvm"
482
560
 
483
- if ($nvmReady -and -not $nodeOK) {
484
- # No compatible Node.js: install chosen version
561
+ if ($nvmReady -and (-not $nodeOK -or $NODE_ACTION -eq "upgrade" -or $NODE_ACTION -eq "switch")) {
485
562
  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"
563
+ $nvmExe = Find-NvmExe
564
+ if (-not $nvmExe) {
565
+ Write-Warn "nvm not found -- open a new terminal and run: nvm install $NODE_TARGET"
566
+ Add-Result "Node.js install" $false "nvm not found"
567
+ } else {
568
+ # IMPORTANT: do NOT pipe nvm output (2>&1 | ...).
569
+ # nvm.exe checks if its stdout is a real console; piping makes it think
570
+ # it is not in a terminal and it shows a GUI dialog instead of running.
571
+ $nvmRunOK = $true
572
+ try {
573
+ & $nvmExe install $NODE_TARGET
574
+ & $nvmExe use $NODE_TARGET
575
+ } catch {
576
+ Write-Warn "nvm error: $_"
577
+ Add-Result "Node.js install" $false "nvm error -- see above"
578
+ $nvmRunOK = $false
501
579
  }
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
580
+
581
+ if ($nvmRunOK) {
582
+ # PATH in this session may still be stale after nvm use.
583
+ # Read NVM_SYMLINK directly from registry and call node.exe by full path.
584
+ Refresh-Path
585
+ $raw = ""
586
+ try { $raw = (& node --version 2>&1).Trim() } catch {}
587
+
588
+ if (-not ($raw -match '^v')) {
589
+ $nvmSym = [System.Environment]::GetEnvironmentVariable('NVM_SYMLINK', 'Machine')
590
+ if (-not $nvmSym) { $nvmSym = [System.Environment]::GetEnvironmentVariable('NVM_SYMLINK', 'User') }
591
+ if ($nvmSym) {
592
+ $nodeExe = Join-Path $nvmSym 'node.exe'
593
+ if (Test-Path $nodeExe -ErrorAction SilentlyContinue) {
594
+ # Add to PATH so npm also works in the rest of this script
595
+ if ($env:Path -notlike "*$([regex]::Escape($nvmSym))*") {
596
+ $env:Path = "$nvmSym;$env:Path"
597
+ }
598
+ try { $raw = (& $nodeExe --version 2>&1).Trim() } catch {}
599
+ }
600
+ }
601
+ }
602
+
603
+ if ($raw -match '^v') {
604
+ $nodeVersion = $raw
605
+ Write-OK "Node.js $nodeVersion installed and active"
606
+ Add-Result "Node.js install" $true $nodeVersion
607
+ $nodeOK = $true
608
+ } else {
609
+ Write-Warn "Node.js installed but PATH not yet updated in this session"
610
+ Write-Warn "Open a new terminal and run: nvm use $NODE_TARGET"
611
+ Add-Result "Node.js install" $false "PATH refresh needed -- open a new terminal"
612
+ $nodeOK = $false
613
+ }
525
614
  }
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
615
  }
531
616
  } elseif (-not $nvmReady -and $nodeOK) {
532
617
  # nvm not available but Node >= 18 already present: skip
@@ -539,82 +624,106 @@ if ($NODE_ACTION -eq "keep") {
539
624
  }
540
625
  }
541
626
 
542
- # ─── Source-mode steps ────────────────────────────────────────────────────────
627
+ # ─── Steps 6 + 7: Install Easy DevOps & register CLI ─────────────────────────
543
628
 
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)"
629
+ if ($isAlreadyInstalled) {
630
+ # ── Already installed: skip both steps ──────────────────────────────────────
631
+ Write-Step "Installing Easy DevOps"
632
+ Write-OK "Skipped (easy-devops already installed at $existingCmd)"
633
+ Add-Result "npm install" $true "Skipped (already installed)"
549
634
 
550
635
  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"
636
+ Write-OK "Skipped (already registered)"
637
+ Add-Result "CLI registered" $true "Skipped (already installed)"
558
638
 
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
- }
639
+ } elseif ($isSourceMode) {
640
+ # ── Source mode: npm install in project dir + npm link ───────────────────────
641
+ Write-Step "Installing Easy DevOps dependencies"
565
642
 
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"
643
+ if (-not $nodeOK) {
644
+ Write-Warn "Skipping -- Node.js is not ready. Open a new terminal and re-run install.ps1."
645
+ Add-Result "npm install" $false "Skipped -- Node.js not ready"
646
+ } else {
647
+ try {
648
+ Push-Location $scriptDir
649
+ Write-Info "Running npm install..."
650
+ & npm install
651
+ if ($LASTEXITCODE -ne 0) {
652
+ Write-Err "npm install failed (exit code $LASTEXITCODE)"
653
+ Add-Result "npm install" $false "Exit code $LASTEXITCODE"
654
+ exit 1
655
+ }
656
+ Write-OK "All dependencies installed"
657
+ Add-Result "npm install" $true ""
658
+ } catch {
659
+ Write-Err "npm install error: $_"
660
+ Add-Result "npm install" $false "$_"
577
661
  exit 1
662
+ } finally {
663
+ Pop-Location
578
664
  }
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
665
  }
588
- }
589
666
 
590
- # ─── Step 7: npm link ────────────────────────────────────────────────────────
667
+ Write-Step "Registering global command"
591
668
 
592
- Write-Step "Registering global command"
669
+ if (-not $nodeOK) {
670
+ Write-Warn "Skipping -- Node.js is not ready"
671
+ Add-Result "CLI registered" $false "Skipped -- Node.js not ready"
672
+ } else {
673
+ try {
674
+ Push-Location $scriptDir
675
+ & npm link
676
+ if ($LASTEXITCODE -ne 0) {
677
+ Write-Warn "npm link failed -- CLI won't be globally available"
678
+ Write-Warn "You can still run: node cli/index.js"
679
+ Add-Result "CLI registered" $false "Exit code $LASTEXITCODE"
680
+ } else {
681
+ Write-OK "easy-devops command linked globally"
682
+ Add-Result "CLI registered" $true ""
683
+ }
684
+ } catch {
685
+ Write-Warn "npm link failed: $_ -- run: node cli/index.js"
686
+ Add-Result "CLI registered" $false "$_"
687
+ } finally {
688
+ Pop-Location
689
+ }
690
+ }
593
691
 
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
692
  } 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 ""
693
+ # ── npm global mode: npm install -g easy-devops ──────────────────────────────
694
+ Write-Step "Installing Easy DevOps"
695
+
696
+ if (-not $nodeOK) {
697
+ Write-Warn "Skipping -- Node.js is not ready."
698
+ Write-Warn "Open a new terminal and run: npm install -g easy-devops"
699
+ Add-Result "npm install" $false "Skipped -- Node.js not ready"
700
+ } else {
701
+ try {
702
+ Write-Info "Running npm install -g easy-devops..."
703
+ & npm install -g easy-devops
704
+ if ($LASTEXITCODE -ne 0) {
705
+ Write-Err "npm install -g easy-devops failed (exit code $LASTEXITCODE)"
706
+ Add-Result "npm install" $false "Exit code $LASTEXITCODE"
707
+ exit 1
708
+ }
709
+ Write-OK "easy-devops installed globally"
710
+ Add-Result "npm install" $true "npm install -g easy-devops"
711
+ } catch {
712
+ Write-Err "npm install -g error: $_"
713
+ Add-Result "npm install" $false "$_"
714
+ exit 1
608
715
  }
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
716
  }
615
- }
616
717
 
617
- } # end source-mode block
718
+ Write-Step "Registering global command"
719
+ if ($nodeOK) {
720
+ Write-OK "Registered via npm install -g"
721
+ Add-Result "CLI registered" $true "npm install -g"
722
+ } else {
723
+ Write-Warn "Skipped -- Node.js not ready"
724
+ Add-Result "CLI registered" $false "Skipped -- Node.js not ready"
725
+ }
726
+ }
618
727
 
619
728
  # ─── Summary (mirrors install.sh summary block) ───────────────────────────────
620
729
 
package/install.sh CHANGED
@@ -23,11 +23,35 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
23
  EASYDEVOPS_DIR="${EASYDEVOPS_DIR:-$SCRIPT_DIR}"
24
24
 
25
25
  # ---------------------------------------------------------------------------
26
- # Source lib modules
26
+ # Source lib modules (auto-download if running standalone)
27
27
  # ---------------------------------------------------------------------------
28
28
  LIB_DIR="$SCRIPT_DIR/lib/installer"
29
+ _LIB_BASE="https://raw.githubusercontent.com/omar00050/Easy-DevOps/main/lib/installer"
30
+ _LIB_MODULES="progress.sh detect.sh node-versions.sh picker.sh nvm-bootstrap.sh"
31
+
32
+ if [ ! -f "$LIB_DIR/progress.sh" ]; then
33
+ printf 'Lib modules not found locally — downloading from GitHub...\n'
34
+ mkdir -p "$LIB_DIR"
35
+ _dl_ok=true
36
+ for _module in $_LIB_MODULES; do
37
+ if command -v curl >/dev/null 2>&1; then
38
+ curl -fsSL "$_LIB_BASE/$_module" -o "$LIB_DIR/$_module" 2>/dev/null || _dl_ok=false
39
+ elif command -v wget >/dev/null 2>&1; then
40
+ wget -q "$_LIB_BASE/$_module" -O "$LIB_DIR/$_module" 2>/dev/null || _dl_ok=false
41
+ else
42
+ printf 'Error: curl or wget is required to download installer modules.\n' >&2
43
+ exit 1
44
+ fi
45
+ done
46
+ if [ "$_dl_ok" = "false" ]; then
47
+ printf 'Error: Failed to download one or more installer modules from GitHub.\n' >&2
48
+ printf 'Check your internet connection and try again.\n' >&2
49
+ exit 1
50
+ fi
51
+ printf 'Modules downloaded.\n\n'
52
+ fi
29
53
 
30
- for _module in progress.sh detect.sh node-versions.sh picker.sh nvm-bootstrap.sh; do
54
+ for _module in $_LIB_MODULES; do
31
55
  if [ ! -f "$LIB_DIR/$_module" ]; then
32
56
  printf 'Error: Required module not found: %s/%s\n' "$LIB_DIR" "$_module" >&2
33
57
  exit 1
@@ -147,19 +171,35 @@ while [ "$#" -gt 0 ]; do
147
171
  done
148
172
 
149
173
  # ---------------------------------------------------------------------------
150
- # Source mode / package mode detection (mirrors install.ps1)
174
+ # Install mode detection (mirrors install.ps1 3-mode logic)
151
175
  #
152
- # Package mode: easy-devops already on PATH (installed via npm -g)
153
- # -> skip npm install + npm link
154
- # Source mode: running from cloned repo or fresh directory
155
- # -> run all 7 steps
176
+ # source : package.json with name=easy-devops found in script dir
177
+ # -> npm install + npm link
178
+ # update : easy-devops already on PATH
179
+ # -> skip install steps
180
+ # npm : standalone download, not in project dir
181
+ # -> npm install -g easy-devops
156
182
  # ---------------------------------------------------------------------------
157
- PACKAGE_MODE=false
183
+ SOURCE_MODE=false
184
+ ALREADY_INSTALLED=false
158
185
  EXISTING_CMD=""
159
186
 
187
+ # Source mode: script is running from the project directory
188
+ if [ -f "$SCRIPT_DIR/package.json" ]; then
189
+ if command -v node >/dev/null 2>&1; then
190
+ _pkg_name="$(node -e "try{process.stdout.write(require('$SCRIPT_DIR/package.json').name)}catch(e){}" 2>/dev/null || true)"
191
+ else
192
+ _pkg_name="$(grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' "$SCRIPT_DIR/package.json" 2>/dev/null | grep -o '"[^"]*"$' | tr -d '"' || true)"
193
+ fi
194
+ if [ "$_pkg_name" = "easy-devops" ]; then
195
+ SOURCE_MODE=true
196
+ fi
197
+ fi
198
+
199
+ # Already installed: easy-devops command is on PATH
160
200
  if command -v easy-devops >/dev/null 2>&1; then
161
201
  EXISTING_CMD="$(command -v easy-devops)"
162
- PACKAGE_MODE=true
202
+ ALREADY_INSTALLED=true
163
203
  fi
164
204
 
165
205
  # ---------------------------------------------------------------------------
@@ -171,12 +211,15 @@ printf '║ Easy DevOps -- Bootstrap Installer ║\n'
171
211
  printf '╚══════════════════════════════════════╝\n'
172
212
  printf '\n'
173
213
 
174
- if [ "$PACKAGE_MODE" = "true" ]; then
175
- printf ' Mode: package (easy-devops already installed at %s)\n' "$EXISTING_CMD"
176
- printf ' Skipping npm install / npm link steps.\n'
214
+ if [ "$ALREADY_INSTALLED" = "true" ]; then
215
+ printf ' Mode: update (easy-devops already installed at %s)\n' "$EXISTING_CMD"
216
+ printf ' Node.js will still be managed; npm steps skipped.\n'
217
+ printf '\n'
218
+ elif [ "$SOURCE_MODE" = "true" ]; then
219
+ printf ' Mode: source (project directory -- npm install + npm link)\n'
177
220
  printf '\n'
178
221
  else
179
- printf ' Mode: source (installing from project directory)\n'
222
+ printf ' Mode: npm (will run: npm install -g easy-devops)\n'
180
223
  printf '\n'
181
224
  fi
182
225
 
@@ -369,40 +412,31 @@ else
369
412
  fi
370
413
 
371
414
  # ---------------------------------------------------------------------------
372
- # Step 6: npm install
415
+ # Steps 6 + 7: Install Easy DevOps & register CLI
373
416
  # ---------------------------------------------------------------------------
374
- if [ "$PACKAGE_MODE" = "true" ]; then
375
- step_done "${STEPS[5]} (skipped -- package mode)"
376
- add_result "npm install" "ok" "Skipped (package mode)"
377
- else
417
+
418
+ if [ "$ALREADY_INSTALLED" = "true" ]; then
419
+ # ── Already installed: skip both steps ──────────────────────────────────────
420
+ step_done "${STEPS[5]} (skipped -- easy-devops already installed)"
421
+ add_result "npm install" "ok" "Skipped (already installed)"
422
+ step_done "${STEPS[6]} (skipped -- already registered)"
423
+ add_result "CLI registered" "ok" "Skipped (already installed)"
424
+
425
+ elif [ "$SOURCE_MODE" = "true" ]; then
426
+ # ── Source mode: npm install in project dir + npm link ───────────────────────
378
427
  step_running "${STEPS[5]}"
379
428
  printf 'Running npm install in %s...\n' "$EASYDEVOPS_DIR"
380
- if ! npm install --prefix "$EASYDEVOPS_DIR" 2>&1; then
429
+ if ! (cd "$EASYDEVOPS_DIR" && npm install 2>&1); then
381
430
  die "${STEPS[5]}" "npm install failed" \
382
431
  "cd $EASYDEVOPS_DIR && npm install" \
383
432
  "npm link"
384
433
  fi
385
434
  step_done "${STEPS[5]}"
386
435
  add_result "npm install" "ok" ""
387
- fi
388
436
 
389
- # ---------------------------------------------------------------------------
390
- # Step 7: npm link -- register global command
391
- # ---------------------------------------------------------------------------
392
- if [ "$PACKAGE_MODE" = "true" ]; then
393
- step_done "${STEPS[6]} (skipped -- package mode)"
394
- add_result "CLI registered" "ok" "Skipped (package mode)"
395
- else
396
437
  step_running "${STEPS[6]}"
397
438
  printf 'Registering global command via npm link...\n'
398
- _link_ok=false
399
- if npm link --prefix "$EASYDEVOPS_DIR" 2>&1; then
400
- _link_ok=true
401
- elif (cd "$EASYDEVOPS_DIR" && npm link 2>&1); then
402
- _link_ok=true
403
- fi
404
-
405
- if [ "$_link_ok" = "false" ]; then
439
+ if ! (cd "$EASYDEVOPS_DIR" && npm link 2>&1); then
406
440
  die "${STEPS[6]}" "npm link failed -- could not register global command" \
407
441
  "cd $EASYDEVOPS_DIR && npm link" \
408
442
  "If permission denied, try: sudo npm link"
@@ -410,13 +444,26 @@ else
410
444
 
411
445
  # Verify the command is on PATH
412
446
  if ! command -v easy-devops >/dev/null 2>&1; then
413
- printf 'Warning: easy-devops command not found on PATH yet.\n' >&2
414
- printf 'You may need to open a new terminal or run:\n' >&2
447
+ printf 'Warning: easy-devops not yet on PATH. Open a new terminal or run:\n' >&2
415
448
  printf ' export PATH="$(npm bin -g):$PATH"\n' >&2
416
449
  fi
417
-
418
450
  step_done "${STEPS[6]}"
419
451
  add_result "CLI registered" "ok" ""
452
+
453
+ else
454
+ # ── npm global mode: npm install -g easy-devops ──────────────────────────────
455
+ step_running "${STEPS[5]}"
456
+ printf 'Running npm install -g easy-devops...\n'
457
+ if ! npm install -g easy-devops 2>&1; then
458
+ die "${STEPS[5]}" "npm install -g easy-devops failed" \
459
+ "npm install -g easy-devops" \
460
+ "If permission denied, try: sudo npm install -g easy-devops"
461
+ fi
462
+ step_done "${STEPS[5]}"
463
+ add_result "npm install" "ok" "npm install -g easy-devops"
464
+
465
+ step_done "${STEPS[6]} (registered via npm install -g)"
466
+ add_result "CLI registered" "ok" "npm install -g"
420
467
  fi
421
468
 
422
469
  # ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-devops",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A unified DevOps management tool with CLI and web dashboard for managing Nginx, SSL certificates, and Node.js on Linux and Windows servers.",
5
5
  "keywords": [
6
6
  "devops",