fix(update): continue after package doctor warnings (#91586)

* fix(update): continue after package doctor warnings

* fix(update): type advisory step rendering

* fix(update): preserve advisory doctor step state

* fix(update): share advisory doctor state

* fix(update): keep timed-out doctor failures blocking

* fix(update): require explicit doctor advisory result

* fix(update): reject malformed doctor advisory results

* fix(update): bound doctor advisory diagnostics

* fix(update): keep doctor advisory restart-neutral

* fix(update): protect doctor advisory IPC

* fix(update): scope doctor advisories to converging updater

* fix(update): scope doctor advisories to deferred repairs

* fix(update): secure doctor advisory IPC

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Jason (Json)
2026-06-13 22:03:57 -06:00
committed by GitHub
parent 889bc52ba5
commit 7259cb5c77
16 changed files with 779 additions and 33 deletions
+33 -8
View File
@@ -5,6 +5,7 @@ import { theme } from "../../../packages/terminal-core/src/theme.js";
import { formatDurationPrecise } from "../../infra/format-time/format-duration.ts";
import type {
UpdateRunResult,
UpdateStepAdvisory,
UpdateStepInfo,
UpdateStepProgress,
} from "../../infra/update-runner.js";
@@ -37,10 +38,14 @@ const STEP_LABELS: Record<string, string> = {
"global install": "Installing global package",
};
function getStepLabel(step: UpdateStepInfo): string {
function getStepLabel(step: Pick<UpdateStepInfo, "name">): string {
return STEP_LABELS[step.name] ?? step.name;
}
function isAdvisoryStep(step: { advisory?: UpdateStepAdvisory }): boolean {
return step.advisory !== undefined;
}
/** Convert updater failure reasons and stderr tails into operator-facing recovery hints. */
export function inferUpdateFailureHints(result: UpdateRunResult): string[] {
if (result.status !== "error") {
@@ -138,12 +143,19 @@ export function createUpdateProgress(enabled: boolean): ProgressController {
const label = getStepLabel(step);
const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
const icon = step.exitCode === 0 ? theme.success("\u2713") : theme.error("\u2717");
const icon = formatStepStatus(step);
currentSpinner.stop(`${icon} ${label} ${duration}`);
currentSpinner = null;
if (step.exitCode !== 0 && step.stderrTail) {
if (isAdvisoryStep(step) && step.stderrTail) {
const lines = step.stderrTail.split("\n").slice(-10);
for (const line of lines) {
if (line.trim()) {
defaultRuntime.log(` ${theme.warn(line)}`);
}
}
} else if (step.exitCode !== 0 && step.stderrTail) {
const lines = step.stderrTail.split("\n").slice(-10);
for (const line of lines) {
if (line.trim()) {
@@ -165,11 +177,17 @@ export function createUpdateProgress(enabled: boolean): ProgressController {
};
}
function formatStepStatus(exitCode: number | null): string {
if (exitCode === 0) {
function formatStepStatus(step: {
exitCode: number | null;
advisory?: UpdateStepAdvisory;
}): string {
if (isAdvisoryStep(step)) {
return theme.warn("!");
}
if (step.exitCode === 0) {
return theme.success("\u2713");
}
if (exitCode === null) {
if (step.exitCode === null) {
return theme.warn("?");
}
return theme.error("\u2717");
@@ -213,11 +231,18 @@ export function printResult(result: UpdateRunResult, opts: PrintResultOptions):
defaultRuntime.log("");
defaultRuntime.log(theme.heading("Steps:"));
for (const step of result.steps) {
const status = formatStepStatus(step.exitCode);
const status = formatStepStatus(step);
const duration = theme.muted(`(${formatDurationPrecise(step.durationMs)})`);
defaultRuntime.log(` ${status} ${step.name} ${duration}`);
if (step.exitCode !== 0 && step.stderrTail) {
if (isAdvisoryStep(step) && step.stderrTail) {
const lines = step.stderrTail.split("\n").slice(0, 5);
for (const line of lines) {
if (line.trim()) {
defaultRuntime.log(` ${theme.warn(line)}`);
}
}
} else if (step.exitCode !== 0 && step.stderrTail) {
const lines = step.stderrTail.split("\n").slice(0, 5);
for (const line of lines) {
if (line.trim()) {