Skip to content

Commit 00a89c1

Browse files
committed
fix(cli/fix): preserve original diagnostic descriptions in permission/schema handlers
Permission handler: - repairPermissions now returns RepairOutcome[] aligned by index - Dir-first ordering preserved via index maps (original issue indices tracked through reorder) - Remove dead collectResults function Schema handler: - Keep original fixIssues from getSchemaIssues instead of building new objects from repair strings - Mark all issues based on overall repair success/failure (schema repair runs independently of detection, so 1:1 mapping isn't possible)
1 parent b2919bc commit 00a89c1

File tree

1 file changed

+67
-82
lines changed

1 file changed

+67
-82
lines changed

src/commands/cli/fix.ts

Lines changed: 67 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -456,53 +456,63 @@ function formatMode(mode: number): string {
456456
* @param issues - Permission issues to repair
457457
* @returns Separate lists of human-readable repair successes and failures
458458
*/
459-
async function repairPermissions(issues: PermissionIssue[]): Promise<{
460-
fixed: string[];
461-
failed: string[];
462-
}> {
463-
const fixed: string[] = [];
464-
const failed: string[] = [];
465-
466-
// Repair directories first so child file chmod calls don't fail with EACCES
467-
const dirIssues = issues.filter((i) => i.kind === "directory");
468-
const fileIssues = issues.filter((i) => i.kind !== "directory");
459+
/**
460+
* Repair permissions, returning outcomes aligned by index with input.
461+
*
462+
* Repairs directories before files to avoid EACCES on child chmod calls.
463+
*/
464+
async function repairPermissions(
465+
issues: PermissionIssue[]
466+
): Promise<RepairOutcome[]> {
467+
const outcomes = new Array<RepairOutcome>(issues.length);
468+
469+
// Build index maps for dirs and files
470+
const dirEntries: Array<{ idx: number; issue: PermissionIssue }> = [];
471+
const fileEntries: Array<{ idx: number; issue: PermissionIssue }> = [];
472+
for (let i = 0; i < issues.length; i++) {
473+
const issue = issues[i] as PermissionIssue;
474+
if (issue.kind === "directory") {
475+
dirEntries.push({ idx: i, issue });
476+
} else {
477+
fileEntries.push({ idx: i, issue });
478+
}
479+
}
469480

470-
await collectResults(dirIssues, fixed, failed);
471-
await collectResults(fileIssues, fixed, failed);
481+
// Repair directories first, then files
482+
await collectPermResults(dirEntries, outcomes);
483+
await collectPermResults(fileEntries, outcomes);
472484

473-
return { fixed, failed };
485+
return outcomes;
474486
}
475487

476488
/**
477-
* Run chmod for each issue in parallel, collecting successes and failures.
478-
*
479-
* @param issues - Permission issues to repair
480-
* @param fixed - Accumulator for successful repair messages
481-
* @param failed - Accumulator for failed repair messages
489+
* Run chmod for each entry in parallel, writing outcomes by original index.
482490
*/
483-
async function collectResults(
484-
issues: PermissionIssue[],
485-
fixed: string[],
486-
failed: string[]
491+
async function collectPermResults(
492+
entries: Array<{ idx: number; issue: PermissionIssue }>,
493+
outcomes: RepairOutcome[]
487494
): Promise<void> {
488495
const results = await Promise.allSettled(
489-
issues.map(async (issue) => {
496+
entries.map(async ({ issue }) => {
490497
await chmod(issue.path, issue.expectedMode);
491498
return `${issue.kind} ${issue.path}: ${formatMode(issue.currentMode)} -> ${formatMode(issue.expectedMode)}`;
492499
})
493500
);
494501

495502
for (let i = 0; i < results.length; i++) {
496503
const result = results[i] as PromiseSettledResult<string>;
504+
const entry = entries[i] as { idx: number; issue: PermissionIssue };
497505
if (result.status === "fulfilled") {
498-
fixed.push(result.value);
506+
outcomes[entry.idx] = { success: true, message: result.value };
499507
} else {
500-
const issue = issues[i] as PermissionIssue;
501508
const reason =
502509
result.reason instanceof Error
503510
? result.reason.message
504-
: "permission denied";
505-
failed.push(`${issue.kind} ${issue.path}: ${reason}`);
511+
: "unknown error";
512+
outcomes[entry.idx] = {
513+
success: false,
514+
message: `${entry.issue.kind} ${entry.issue.path}: ${reason}`,
515+
};
506516
}
507517
}
508518
}
@@ -533,38 +543,26 @@ async function handlePermissionIssues(
533543
return { issues: fixIssues, repairFailed: false };
534544
}
535545

536-
const { fixed, failed } = await repairPermissions(permIssues);
537-
538-
// Map repair results back to issues
539-
const repairedIssues: FixIssue[] = [];
540-
541-
for (const f of fixed) {
542-
repairedIssues.push({
543-
category: "permission",
544-
description: f,
545-
repaired: true,
546-
repairMessage: f,
547-
});
548-
}
546+
const outcomes = await repairPermissions(permIssues);
549547

550-
if (failed.length > 0) {
551-
for (const f of failed) {
552-
repairedIssues.push({
553-
category: "permission",
554-
description: f,
555-
repaired: false,
556-
repairMessage: f,
557-
});
548+
// Mark each issue with its repair outcome (aligned by index)
549+
let anyFailed = false;
550+
for (let i = 0; i < fixIssues.length; i++) {
551+
const issue = fixIssues[i] as FixIssue;
552+
const outcome = outcomes[i] as RepairOutcome;
553+
issue.repaired = outcome.success;
554+
issue.repairMessage = outcome.message;
555+
if (!outcome.success) {
556+
anyFailed = true;
558557
}
559558
}
560559

561560
return {
562-
issues: repairedIssues.length > 0 ? repairedIssues : fixIssues,
563-
instructions:
564-
failed.length > 0
565-
? `You may need to fix permissions manually:\n chmod 700 "${getConfigDir()}"\n chmod 600 "${dbPath}"`
566-
: undefined,
567-
repairFailed: failed.length > 0,
561+
issues: fixIssues,
562+
instructions: anyFailed
563+
? `You may need to fix permissions manually:\n chmod 700 "${getConfigDir()}"\n chmod 600 "${dbPath}"`
564+
: undefined,
565+
repairFailed: anyFailed,
568566
};
569567
}
570568

@@ -593,37 +591,24 @@ function handleSchemaIssues(dbPath: string, dryRun: boolean): HandlerResult {
593591
}
594592

595593
const { fixed, failed } = repairSchema(db);
596-
597-
// Map repair results back to issues
598-
const repairedIssues: FixIssue[] = [];
599-
600-
for (const f of fixed) {
601-
repairedIssues.push({
602-
category: "schema",
603-
description: f,
604-
repaired: true,
605-
repairMessage: f,
606-
});
607-
}
608-
609-
if (failed.length > 0) {
610-
for (const f of failed) {
611-
repairedIssues.push({
612-
category: "schema",
613-
description: f,
614-
repaired: false,
615-
repairMessage: f,
616-
});
617-
}
594+
const anyFailed = failed.length > 0;
595+
596+
// Mark original issues with repair outcomes.
597+
// Schema repair runs independently of detection — mark all issues based
598+
// on overall success/failure. Attach repair messages for diagnostics.
599+
for (const issue of fixIssues) {
600+
issue.repaired = !anyFailed;
601+
issue.repairMessage = anyFailed
602+
? failed.join("; ")
603+
: fixed.join("; ") || "Schema repaired";
618604
}
619605

620606
return {
621-
issues: repairedIssues.length > 0 ? repairedIssues : fixIssues,
622-
instructions:
623-
failed.length > 0
624-
? `Try deleting the database and restarting: rm "${dbPath}"`
625-
: undefined,
626-
repairFailed: failed.length > 0,
607+
issues: fixIssues,
608+
instructions: anyFailed
609+
? `Try deleting the database and restarting: rm "${dbPath}"`
610+
: undefined,
611+
repairFailed: anyFailed,
627612
};
628613
}
629614

0 commit comments

Comments
 (0)