easy-devops 0.1.9 → 0.2.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.
@@ -319,8 +319,21 @@ async function installCertbot() {
319
319
  // Enable all TLS versions (1.0, 1.1, 1.2, 1.3) for maximum compatibility
320
320
  const tlsSetup = `[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13`;
321
321
 
322
+ // Helper to safely run a command and catch EPERM errors
323
+ const safeRun = async (cmd, opts) => {
324
+ try {
325
+ return await run(cmd, opts);
326
+ } catch (err) {
327
+ // Re-throw EPERM so caller can handle Windows Defender blocks
328
+ if (err.code === 'EPERM' || err.message?.includes('EPERM')) {
329
+ throw err;
330
+ }
331
+ return { success: false, stdout: '', stderr: err.message, exitCode: null };
332
+ }
333
+ };
334
+
322
335
  // Method 1: Invoke-WebRequest with all TLS versions
323
- let r = await run(
336
+ let r = await safeRun(
324
337
  `${tlsSetup}; $ProgressPreference='SilentlyContinue'; Invoke-WebRequest -Uri '${safeUrl}' -OutFile '${safeDest}' -UseBasicParsing -TimeoutSec 120`,
325
338
  { timeout: 130000 },
326
339
  );
@@ -328,7 +341,7 @@ async function installCertbot() {
328
341
  if (showErrors && r.stderr) lastDownloadError = `Invoke-WebRequest: ${r.stderr}`;
329
342
 
330
343
  // Method 2: WebClient with TLS
331
- r = await run(
344
+ r = await safeRun(
332
345
  `${tlsSetup}; (New-Object System.Net.WebClient).DownloadFile('${safeUrl}','${safeDest}')`,
333
346
  { timeout: 130000 },
334
347
  );
@@ -336,7 +349,7 @@ async function installCertbot() {
336
349
  if (showErrors && r.stderr && !lastDownloadError) lastDownloadError = `WebClient: ${r.stderr}`;
337
350
 
338
351
  // Method 3: BITS Transfer
339
- r = await run(
352
+ r = await safeRun(
340
353
  `Import-Module BitsTransfer -ErrorAction SilentlyContinue; Start-BitsTransfer -Source '${safeUrl}' -Destination '${safeDest}' -ErrorAction Stop`,
341
354
  { timeout: 130000 },
342
355
  );
@@ -345,7 +358,7 @@ async function installCertbot() {
345
358
 
346
359
  // Method 4: curl.exe (works on Windows 10+)
347
360
  if (hasCurl) {
348
- r = await run(
361
+ r = await safeRun(
349
362
  `curl.exe -L --ssl-no-revoke --max-time 120 -o '${safeDest}' '${safeUrl}'`,
350
363
  { timeout: 130000 },
351
364
  );
@@ -354,7 +367,7 @@ async function installCertbot() {
354
367
  }
355
368
 
356
369
  // Method 5: HttpClient with custom handler
357
- r = await run(
370
+ r = await safeRun(
358
371
  `${tlsSetup}; $handler=[System.Net.Http.HttpClientHandler]::new(); $handler.ServerCertificateCustomValidationCallback={$true}; $handler.AllowAutoRedirect=$true; $hc=[System.Net.Http.HttpClient]::new($handler); $hc.DefaultRequestHeaders.Add('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64)'); $hc.Timeout=[TimeSpan]::FromSeconds(120); $bytes=$hc.GetByteArrayAsync('${safeUrl}').GetAwaiter().GetResult(); [System.IO.File]::WriteAllBytes('${safeDest}',$bytes)`,
359
372
  { timeout: 130000 },
360
373
  );
@@ -362,7 +375,7 @@ async function installCertbot() {
362
375
  if (showErrors && r.stderr && !lastDownloadError) lastDownloadError = `HttpClient: ${r.stderr}`;
363
376
 
364
377
  // Method 6: Certutil (built-in Windows tool)
365
- r = await run(
378
+ r = await safeRun(
366
379
  `certutil.exe -urlcache -split -f "${safeUrl}" "${safeDest}"`,
367
380
  { timeout: 130000 },
368
381
  );
@@ -370,7 +383,7 @@ async function installCertbot() {
370
383
  if (showErrors && r.stderr && !lastDownloadError) lastDownloadError = `certutil: ${r.stderr}`;
371
384
 
372
385
  // Method 7: PowerShell with DisableKeepAlive
373
- r = await run(
386
+ r = await safeRun(
374
387
  `${tlsSetup}; $req=[System.Net.WebRequest]::Create('${safeUrl}'); $req.Method='GET'; $req.KeepAlive=$false; $req.UserAgent='Mozilla/5.0'; $resp=$req.GetResponse(); $stream=$resp.GetResponseStream(); $reader=[System.IO.BinaryReader]::new($stream); $bytes=$reader.ReadBytes($resp.ContentLength); $reader.Close(); [System.IO.File]::WriteAllBytes('${safeDest}',$bytes)`,
375
388
  { timeout: 130000 },
376
389
  );
@@ -479,7 +492,7 @@ async function installCertbot() {
479
492
  const appInstallerDest = '$env:TEMP\\AppInstaller.msixbundle';
480
493
 
481
494
  console.log(chalk.gray('\n Downloading App Installer...'));
482
- const downloaded = await downloadFile(appInstallerUrl, appInstallerDest);
495
+ let downloaded = false; try { downloaded = await downloadFile(appInstallerUrl, appInstallerDest); } catch (err) { if (err.code === 'EPERM' || err.message?.includes('EPERM')) { console.log(chalk.red('\n ? Windows Defender blocked the download.')); console.log(chalk.gray(' Add an exclusion in Windows Defender or download manually.')); console.log(chalk.gray(' Download URL: https://aka.ms/getwinget\n')); } else { console.log(chalk.yellow('\n Download failed: ' + err.message + '\n')); } }
483
496
 
484
497
  if (downloaded) {
485
498
  console.log(chalk.gray(' Running App Installer...'));
@@ -573,7 +586,7 @@ async function installCertbot() {
573
586
  const WINACME_DEST = 'C:\\Program Files\\win-acme';
574
587
 
575
588
  // Test network before attempting downloads
576
- await testNetworkConnectivity();
589
+ try { await testNetworkConnectivity(); } catch (err) { console.log(chalk.yellow('Network test failed: ' + err.message)); }
577
590
 
578
591
  step('Downloading win-acme from GitHub ...');
579
592
  const winAcmeUrls = [
@@ -587,7 +600,7 @@ async function installCertbot() {
587
600
 
588
601
  const zipDest = `$env:TEMP\\win-acme.zip`;
589
602
  lastDownloadError = '';
590
- if (await downloadFile(url, zipDest, true)) {
603
+ let dlOk = false; try { dlOk = await downloadFile(url, zipDest, true); } catch (err) { if (err.code === 'EPERM' || err.message?.includes('EPERM')) { console.log(chalk.red('Windows Defender blocked this download.')); console.log(chalk.gray('Use the manual installer option or add a Windows Defender exclusion.')); } } if (dlOk) {
591
604
  console.log(chalk.gray(' Extracting win-acme ...\n'));
592
605
  await run(`New-Item -ItemType Directory -Force -Path '${WINACME_DEST}'`);
593
606
  await run(`Expand-Archive -Path '${zipDest}' -DestinationPath '${WINACME_DEST}' -Force`);
@@ -619,7 +632,7 @@ async function installCertbot() {
619
632
  step(`Downloading certbot installer from ${hostname} ...`);
620
633
 
621
634
  lastDownloadError = '';
622
- if (await downloadFile(url, INSTALLER_DEST, true)) {
635
+ let dlOk = false; try { dlOk = await downloadFile(url, INSTALLER_DEST, true); } catch (err) { if (err.code === 'EPERM' || err.message?.includes('EPERM')) { console.log(chalk.red('Windows Defender blocked this download.')); console.log(chalk.gray('Use the manual installer option or add a Windows Defender exclusion.')); } } if (dlOk) {
623
636
  console.log(chalk.gray(' Running installer silently ...\n'));
624
637
  const ok = await runNsisInstaller(INSTALLER_DEST);
625
638
  await run(`Remove-Item -Force '${INSTALLER_DEST}' -ErrorAction SilentlyContinue`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-devops",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
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",