@docker-doctor/cli 0.5.0 → 0.5.2

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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as runDockerfileRules, c as createComposeLocator, d as discoverProject, f as version, i as runComposeRules, l as parseCompose, n as calculateScore, o as allRules, r as loadConfig, s as findRule, t as toJsonReport, u as parseDockerfile } from "./src-CpGd43y0.mjs";
2
+ import { a as runDockerfileRules, c as createComposeLocator, d as discoverProject, f as version, i as runComposeRules, l as parseCompose, n as calculateScore, o as allRules, r as loadConfig, s as findRule, t as toJsonReport, u as parseDockerfile } from "./src-BOGzkp17.mjs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
@@ -574,9 +574,17 @@ const writeDiagnosticsDirectory = async (diagnostics, report, rootDir) => {
574
574
  recursive: true
575
575
  });
576
576
  await fs.mkdir(dir, { recursive: true });
577
+ const sanitizedReport = {
578
+ ...report,
579
+ diagnostics: report.diagnostics.map((d) => ({
580
+ ...d,
581
+ file: sanitizePath(d.file),
582
+ message: sanitizeMessage(d.message)
583
+ }))
584
+ };
577
585
  await fs.writeFile(path.join(dir, "diagnostics.json"), JSON.stringify({
578
586
  note: TRUST_BOUNDARY_NOTE,
579
- ...report
587
+ ...sanitizedReport
580
588
  }, null, 2), "utf-8");
581
589
  const writes = [];
582
590
  for (const [rule, ruleDiagnostics] of groupDiagnosticsByRule(diagnostics)) {
@@ -649,7 +657,7 @@ const buildHandoffPayload = (input) => {
649
657
  ];
650
658
  for (const [index, [rule, ruleDiagnostics]] of groups.entries()) {
651
659
  const [first] = ruleDiagnostics;
652
- const category = findRule(rule)?.category ?? "General";
660
+ const category = first.category ?? findRule(rule)?.category ?? "General";
653
661
  const countBadge = ruleDiagnostics.length > 1 ? ` (×${ruleDiagnostics.length})` : "";
654
662
  lines.push(`${index + 1}. ${SEVERITY_LABEL[first.severity]} ${category}: ${sanitizeMessage(first.message)} [${rule}]${countBadge}`, ` Fix: ${first.help}`);
655
663
  const files = [...new Set(ruleDiagnostics.map((d) => d.file))];
@@ -784,7 +792,7 @@ const printDiagnostics = (diagnostics, verbose, fileContents, categoryIssueCount
784
792
  console.log(`\n Run ${chalk.cyan("docker-doctor --verbose")} to list every error and warning`);
785
793
  const ruleCounts = {};
786
794
  for (const d of diagnostics) ruleCounts[d.rule] = (ruleCounts[d.rule] || 0) + 1;
787
- const migrationRules = Object.entries(ruleCounts).filter(([_, count]) => count >= 5);
795
+ const migrationRules = Object.entries(ruleCounts).filter(([, count]) => count >= 5);
788
796
  if (migrationRules.length > 0) {
789
797
  console.log();
790
798
  console.log(` ${chalk.yellow("⚠ Migration-scale change: sample before you sweep")}`);
@@ -911,7 +919,7 @@ const formatTerminal = async (diagnostics, score, label, project, verbose = fals
911
919
  let warningsCount = 0;
912
920
  let errorsCount = 0;
913
921
  for (const d of diagnostics) {
914
- const category = findRule(d.rule)?.category || "Best Practices";
922
+ const category = d.category ?? findRule(d.rule)?.category ?? "Best Practices";
915
923
  categoryIssueCounts[category] = (categoryIssueCounts[category] || 0) + 1;
916
924
  if (d.severity === "warning") warningsCount += 1;
917
925
  else if (d.severity === "error") errorsCount += 1;
@@ -1176,9 +1184,14 @@ const runInteractiveWizard = async (context) => {
1176
1184
  console.log(` Inputs and gating: ${chalk.cyan("https://docker-doctor.vercel.app/docs/guides/github-actions")}`);
1177
1185
  }
1178
1186
  }
1179
- if (context.diagnostics.length === 0) return;
1187
+ if (context.diagnostics.length === 0) return true;
1180
1188
  await runAgentHandoff(context);
1181
- } catch {}
1189
+ return true;
1190
+ } catch (error) {
1191
+ const msg = error instanceof Error ? error.message : String(error);
1192
+ console.error(`\n ${chalk.yellow("⚠")} Post-scan step failed: ${msg}`);
1193
+ return false;
1194
+ }
1182
1195
  };
1183
1196
  const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, projectFilesList, fileContents, options, setStatus) => {
1184
1197
  const diagnostics = [];
@@ -1193,7 +1206,7 @@ const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, p
1193
1206
  try {
1194
1207
  const content = await fs.readFile(fullPath, "utf-8");
1195
1208
  fileContents[df] = content;
1196
- const instructions = parseDockerfile(content);
1209
+ const instructions = parseDockerfile(content, df);
1197
1210
  return runDockerfileRules(instructions, df, projectFilesList, rulesConfig, categoriesConfig);
1198
1211
  } catch (error) {
1199
1212
  const msg = error instanceof Error ? error.message : String(error);
@@ -1234,6 +1247,7 @@ const runRulesEngine = async (rootDir, project, rulesConfig, categoriesConfig, p
1234
1247
  };
1235
1248
  };
1236
1249
  const SCAN_FAILURE_EXIT_CODE = 2;
1250
+ const POST_SCAN_FAILURE_EXIT_CODE = 1;
1237
1251
  const WHITESPACE_RUN = /\s+/gu;
1238
1252
  const flattenMessage = (message) => message.replaceAll(WHITESPACE_RUN, " ").trim();
1239
1253
  const scanExitCode = (scanIncomplete, failing) => {
@@ -1329,12 +1343,14 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
1329
1343
  return;
1330
1344
  }
1331
1345
  await formatTerminal(diagnostics, score, label, project, options.verbose, fileContents);
1332
- if (process.stdout.isTTY && process.stdin.isTTY) await runInteractiveWizard({
1346
+ let wizardOk = true;
1347
+ if (process.stdout.isTTY && process.stdin.isTTY) wizardOk = await runInteractiveWizard({
1333
1348
  diagnostics,
1334
1349
  report: toJsonReport(diagnostics, score, label, project),
1335
1350
  rootDir
1336
1351
  });
1337
- process.exitCode = scanExitCode(scanIncomplete, hasErrors);
1352
+ const exitCode = scanExitCode(scanIncomplete, hasErrors);
1353
+ process.exitCode = wizardOk ? exitCode : Math.max(POST_SCAN_FAILURE_EXIT_CODE, exitCode);
1338
1354
  } finally {
1339
1355
  if (spinnerInterval !== null) {
1340
1356
  clearInterval(spinnerInterval);
@@ -1344,7 +1360,7 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
1344
1360
  } catch (error) {
1345
1361
  const msg = error instanceof Error ? error.message : String(error);
1346
1362
  console.error(`Error: ${msg}`);
1347
- process.exit(1);
1363
+ process.exitCode = 1;
1348
1364
  }
1349
1365
  });
1350
1366
  const CURATED_INSTALL_AGENTS = [
@@ -1378,10 +1394,14 @@ const resolveInstallAgents = async (requested) => {
1378
1394
  program.command("install").description("install the Docker Doctor agent skill for your coding agents").option("-a, --agent <agents...>", "agent id(s) to install for (e.g. claude-code codex cursor)").option("-g, --global", "install into each agent's global skills directory (e.g. ~/.claude/skills) instead of the current project").action(async (options) => {
1379
1395
  if (!getSkillSourceDirectory()) {
1380
1396
  console.error("Bundled skill not found — this looks like a broken installation.");
1381
- process.exit(1);
1397
+ process.exitCode = 1;
1398
+ return;
1382
1399
  }
1383
1400
  const agents = await resolveInstallAgents(options.agent);
1384
- if (agents === null) process.exit(1);
1401
+ if (agents === null) {
1402
+ process.exitCode = 1;
1403
+ return;
1404
+ }
1385
1405
  if (agents.length === 0) {
1386
1406
  console.log("Nothing selected — skipped.");
1387
1407
  return;
@@ -1392,7 +1412,8 @@ program.command("install").description("install the Docker Doctor agent skill fo
1392
1412
  });
1393
1413
  if (!result) {
1394
1414
  console.error("Failed to install the skill.");
1395
- process.exit(1);
1415
+ process.exitCode = 1;
1416
+ return;
1396
1417
  }
1397
1418
  for (const installed of result.installed) console.log(` ${chalk.green("✔")} ${agentDisplayName(installed.agent)} → ${installed.path}`);
1398
1419
  for (const failed of result.failed) console.log(` ${chalk.red("✖")} ${agentDisplayName(failed.agent)}: ${failed.error}`);
@@ -1416,7 +1437,8 @@ rules.command("explain <rule>").description("explain a specific rule in detail")
1416
1437
  const rule = findRule(ruleKey);
1417
1438
  if (!rule) {
1418
1439
  console.error(`Rule '${ruleKey}' not found.`);
1419
- process.exit(1);
1440
+ process.exitCode = 1;
1441
+ return;
1420
1442
  }
1421
1443
  console.log(`\nRule: ${rule.key}`);
1422
1444
  console.log(`Category: ${rule.category}`);