@docker-doctor/cli 0.5.1 → 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.cjs +37 -15
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +37 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +15 -13
- package/dist/index.d.mts +15 -13
- package/dist/index.mjs +1 -1
- package/dist/{src-NqSwkGiS.mjs → src-BOGzkp17.mjs} +119 -36
- package/dist/src-BOGzkp17.mjs.map +1 -0
- package/dist/{src-DYLANvt2.cjs → src-D3jzc3GX.cjs} +119 -36
- package/dist/src-D3jzc3GX.cjs.map +1 -0
- package/package.json +4 -4
- package/skill/docker-doctor/references/explain.md +6 -5
- package/dist/src-DYLANvt2.cjs.map +0 -1
- package/dist/src-NqSwkGiS.mjs.map +0 -1
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-
|
|
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
|
-
...
|
|
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(([
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1397
|
+
process.exitCode = 1;
|
|
1398
|
+
return;
|
|
1382
1399
|
}
|
|
1383
1400
|
const agents = await resolveInstallAgents(options.agent);
|
|
1384
|
-
if (agents === null)
|
|
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.
|
|
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.
|
|
1440
|
+
process.exitCode = 1;
|
|
1441
|
+
return;
|
|
1420
1442
|
}
|
|
1421
1443
|
console.log(`\nRule: ${rule.key}`);
|
|
1422
1444
|
console.log(`Category: ${rule.category}`);
|