braeburn 1.3.1 → 1.4.1
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/README.md +3 -1
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +187 -24
- package/dist/commands/setup.d.ts +9 -1
- package/dist/commands/setup.js +66 -55
- package/dist/commands/update.d.ts +2 -2
- package/dist/commands/update.js +22 -101
- package/dist/index.js +22 -17
- package/dist/runner.js +2 -2
- package/dist/steps/catalog.d.ts +2 -0
- package/dist/steps/catalog.js +13 -0
- package/dist/steps/cleanup.d.ts +1 -1
- package/dist/steps/cleanup.js +1 -1
- package/dist/steps/dotnet.d.ts +1 -1
- package/dist/steps/dotnet.js +1 -1
- package/dist/steps/homebrew.d.ts +1 -1
- package/dist/steps/homebrew.js +1 -1
- package/dist/steps/index.d.ts +2 -25
- package/dist/steps/index.js +1 -23
- package/dist/steps/macos.d.ts +1 -1
- package/dist/steps/mas.d.ts +1 -1
- package/dist/steps/mas.js +1 -1
- package/dist/steps/npm.d.ts +1 -1
- package/dist/steps/npm.js +1 -1
- package/dist/steps/nvm.d.ts +1 -1
- package/dist/steps/nvm.js +1 -1
- package/dist/steps/ohmyzsh.d.ts +1 -1
- package/dist/steps/ohmyzsh.js +1 -1
- package/dist/steps/pip.d.ts +1 -1
- package/dist/steps/pip.js +1 -1
- package/dist/steps/pyenv.d.ts +1 -1
- package/dist/steps/pyenv.js +1 -1
- package/dist/steps/runtime.d.ts +7 -0
- package/dist/steps/runtime.js +23 -0
- package/dist/steps/types.d.ts +21 -0
- package/dist/steps/types.js +1 -0
- package/dist/ui/currentStep.d.ts +2 -3
- package/dist/ui/header.d.ts +3 -4
- package/dist/ui/screen.js +1 -5
- package/dist/ui/state.d.ts +1 -30
- package/dist/ui/state.js +1 -14
- package/dist/ui/terminal.d.ts +1 -0
- package/dist/ui/terminal.js +14 -3
- package/dist/ui/versionReport.d.ts +0 -1
- package/dist/ui/versionReport.js +0 -16
- package/dist/update/displayStep.d.ts +4 -0
- package/dist/update/displayStep.js +11 -0
- package/dist/update/engine.d.ts +23 -0
- package/dist/update/engine.js +126 -0
- package/dist/update/state.d.ts +38 -0
- package/dist/update/state.js +15 -0
- package/dist/update/versionCollector.d.ts +2 -0
- package/dist/update/versionCollector.js +16 -0
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { pyenvStep, nvmStep, homebrewStep, masStep, ohmyzshStep, npmStep, pipStep, dotnetStep, macosStep, cleanupStep, } from "./index.js";
|
|
2
|
+
export const ALL_STEPS = [
|
|
3
|
+
pyenvStep,
|
|
4
|
+
nvmStep,
|
|
5
|
+
homebrewStep,
|
|
6
|
+
masStep,
|
|
7
|
+
ohmyzshStep,
|
|
8
|
+
npmStep,
|
|
9
|
+
pipStep,
|
|
10
|
+
dotnetStep,
|
|
11
|
+
macosStep,
|
|
12
|
+
cleanupStep,
|
|
13
|
+
];
|
package/dist/steps/cleanup.d.ts
CHANGED
package/dist/steps/cleanup.js
CHANGED
package/dist/steps/dotnet.d.ts
CHANGED
package/dist/steps/dotnet.js
CHANGED
package/dist/steps/homebrew.d.ts
CHANGED
package/dist/steps/homebrew.js
CHANGED
package/dist/steps/index.d.ts
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type StepRunContext = {
|
|
4
|
-
onOutputLine: OutputLineCallback;
|
|
5
|
-
logWriter: StepLogWriter;
|
|
6
|
-
runStep: (shellCommand: string) => Promise<void>;
|
|
7
|
-
captureOutput: (options: {
|
|
8
|
-
shellCommand: string;
|
|
9
|
-
}) => Promise<string>;
|
|
10
|
-
};
|
|
11
|
-
export type StepStage = "runtime" | "tools";
|
|
12
|
-
export type Step = {
|
|
13
|
-
id: string;
|
|
14
|
-
name: string;
|
|
15
|
-
description: string;
|
|
16
|
-
stage: StepStage;
|
|
17
|
-
warning?: string;
|
|
18
|
-
brewPackageToInstall?: string;
|
|
19
|
-
checkIsAvailable: () => Promise<boolean>;
|
|
20
|
-
run: (context: StepRunContext) => Promise<void>;
|
|
21
|
-
};
|
|
1
|
+
export type { StepRunContext, StepStage, Step, } from "./types.js";
|
|
2
|
+
export { checkCommandExists, checkPathExists, runStep, createDefaultStepRunContext, } from "./runtime.js";
|
|
22
3
|
export { default as homebrewStep } from "./homebrew.js";
|
|
23
4
|
export { default as masStep } from "./mas.js";
|
|
24
5
|
export { default as ohmyzshStep } from "./ohmyzsh.js";
|
|
@@ -29,7 +10,3 @@ export { default as nvmStep } from "./nvm.js";
|
|
|
29
10
|
export { default as dotnetStep } from "./dotnet.js";
|
|
30
11
|
export { default as macosStep } from "./macos.js";
|
|
31
12
|
export { default as cleanupStep } from "./cleanup.js";
|
|
32
|
-
export declare function checkCommandExists(command: string): Promise<boolean>;
|
|
33
|
-
export declare function checkPathExists(filePath: string): Promise<boolean>;
|
|
34
|
-
export declare function runStep(shellCommand: string, context: StepRunContext): Promise<void>;
|
|
35
|
-
export declare function createDefaultStepRunContext(onOutputLine: OutputLineCallback, logWriter: StepLogWriter): StepRunContext;
|
package/dist/steps/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { checkCommandExists, checkPathExists, runStep, createDefaultStepRunContext, } from "./runtime.js";
|
|
2
2
|
export { default as homebrewStep } from "./homebrew.js";
|
|
3
3
|
export { default as masStep } from "./mas.js";
|
|
4
4
|
export { default as ohmyzshStep } from "./ohmyzsh.js";
|
|
@@ -9,25 +9,3 @@ export { default as nvmStep } from "./nvm.js";
|
|
|
9
9
|
export { default as dotnetStep } from "./dotnet.js";
|
|
10
10
|
export { default as macosStep } from "./macos.js";
|
|
11
11
|
export { default as cleanupStep } from "./cleanup.js";
|
|
12
|
-
export async function checkCommandExists(command) {
|
|
13
|
-
return doesShellCommandSucceed({ shellCommand: `command -v ${command}` });
|
|
14
|
-
}
|
|
15
|
-
export async function checkPathExists(filePath) {
|
|
16
|
-
return doesShellCommandSucceed({ shellCommand: `test -e "${filePath}"` });
|
|
17
|
-
}
|
|
18
|
-
export async function runStep(shellCommand, context) {
|
|
19
|
-
await runShellCommand({
|
|
20
|
-
shellCommand,
|
|
21
|
-
onOutputLine: context.onOutputLine,
|
|
22
|
-
logWriter: context.logWriter,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
export function createDefaultStepRunContext(onOutputLine, logWriter) {
|
|
26
|
-
const context = {
|
|
27
|
-
onOutputLine,
|
|
28
|
-
logWriter,
|
|
29
|
-
runStep: (shellCommand) => runStep(shellCommand, context),
|
|
30
|
-
captureOutput: captureShellCommandOutput,
|
|
31
|
-
};
|
|
32
|
-
return context;
|
|
33
|
-
}
|
package/dist/steps/macos.d.ts
CHANGED
package/dist/steps/mas.d.ts
CHANGED
package/dist/steps/mas.js
CHANGED
package/dist/steps/npm.d.ts
CHANGED
package/dist/steps/npm.js
CHANGED
package/dist/steps/nvm.d.ts
CHANGED
package/dist/steps/nvm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { checkPathExists } from "./
|
|
3
|
+
import { checkPathExists } from "./runtime.js";
|
|
4
4
|
const NVM_DIRECTORY = join(homedir(), ".nvm");
|
|
5
5
|
// nvm is a shell function sourced from nvm.sh — it cannot be invoked as a
|
|
6
6
|
// standalone binary, so we source it explicitly inside each bash invocation.
|
package/dist/steps/ohmyzsh.d.ts
CHANGED
package/dist/steps/ohmyzsh.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { checkPathExists } from "./
|
|
3
|
+
import { checkPathExists } from "./runtime.js";
|
|
4
4
|
const OH_MY_ZSH_UPGRADE_SCRIPT_PATH = join(homedir(), ".oh-my-zsh", "tools", "upgrade.sh");
|
|
5
5
|
const ohmyzshStep = {
|
|
6
6
|
id: "ohmyzsh",
|
package/dist/steps/pip.d.ts
CHANGED
package/dist/steps/pip.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { checkCommandExists } from "./
|
|
1
|
+
import { checkCommandExists } from "./runtime.js";
|
|
2
2
|
const PIP_UPDATE_ALL_OUTDATED_SHELL_COMMAND = "pip3 list --outdated --format=columns | tail -n +3 | awk '{print $1}' | xargs -n1 pip3 install -U";
|
|
3
3
|
const pipStep = {
|
|
4
4
|
id: "pip",
|
package/dist/steps/pyenv.d.ts
CHANGED
package/dist/steps/pyenv.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { checkCommandExists, } from "./
|
|
1
|
+
import { checkCommandExists, } from "./runtime.js";
|
|
2
2
|
const FIND_LATEST_STABLE_PYTHON_SHELL_COMMAND = "pyenv install -l | grep -E '^\\s+3\\.[0-9]+\\.[0-9]+$' | grep -vE 'dev|a[0-9]|b[0-9]|rc[0-9]' | tail -1 | tr -d ' '";
|
|
3
3
|
const pyenvStep = {
|
|
4
4
|
id: "pyenv",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type OutputLineCallback } from "../runner.js";
|
|
2
|
+
import type { StepLogWriter } from "../logger.js";
|
|
3
|
+
import type { StepRunContext } from "./types.js";
|
|
4
|
+
export declare function checkCommandExists(command: string): Promise<boolean>;
|
|
5
|
+
export declare function checkPathExists(filePath: string): Promise<boolean>;
|
|
6
|
+
export declare function runStep(shellCommand: string, context: StepRunContext): Promise<void>;
|
|
7
|
+
export declare function createDefaultStepRunContext(onOutputLine: OutputLineCallback, logWriter: StepLogWriter): StepRunContext;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { doesShellCommandSucceed, runShellCommand, captureShellCommandOutput, } from "../runner.js";
|
|
2
|
+
export async function checkCommandExists(command) {
|
|
3
|
+
return doesShellCommandSucceed({ shellCommand: `command -v ${command}` });
|
|
4
|
+
}
|
|
5
|
+
export async function checkPathExists(filePath) {
|
|
6
|
+
return doesShellCommandSucceed({ shellCommand: `test -e "${filePath}"` });
|
|
7
|
+
}
|
|
8
|
+
export async function runStep(shellCommand, context) {
|
|
9
|
+
await runShellCommand({
|
|
10
|
+
shellCommand,
|
|
11
|
+
onOutputLine: context.onOutputLine,
|
|
12
|
+
logWriter: context.logWriter,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function createDefaultStepRunContext(onOutputLine, logWriter) {
|
|
16
|
+
const context = {
|
|
17
|
+
onOutputLine,
|
|
18
|
+
logWriter,
|
|
19
|
+
runStep: (shellCommand) => runStep(shellCommand, context),
|
|
20
|
+
captureOutput: captureShellCommandOutput,
|
|
21
|
+
};
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { OutputLineCallback } from "../runner.js";
|
|
2
|
+
import type { StepLogWriter } from "../logger.js";
|
|
3
|
+
export type StepRunContext = {
|
|
4
|
+
onOutputLine: OutputLineCallback;
|
|
5
|
+
logWriter: StepLogWriter;
|
|
6
|
+
runStep: (shellCommand: string) => Promise<void>;
|
|
7
|
+
captureOutput: (options: {
|
|
8
|
+
shellCommand: string;
|
|
9
|
+
}) => Promise<string>;
|
|
10
|
+
};
|
|
11
|
+
export type StepStage = "runtime" | "tools";
|
|
12
|
+
export type Step = {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
stage: StepStage;
|
|
17
|
+
warning?: string;
|
|
18
|
+
brewPackageToInstall?: string;
|
|
19
|
+
checkIsAvailable: () => Promise<boolean>;
|
|
20
|
+
run: (context: StepRunContext) => Promise<void>;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ui/currentStep.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { StepPhase } from "./state.js";
|
|
1
|
+
import type { DisplayStep, StepPhase } from "./state.js";
|
|
3
2
|
type ActiveStepOptions = {
|
|
4
|
-
step:
|
|
3
|
+
step: DisplayStep;
|
|
5
4
|
stepNumber: number;
|
|
6
5
|
totalSteps: number;
|
|
7
6
|
phase: StepPhase;
|
package/dist/ui/header.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { StepPhase, CompletedStepRecord, LogoVisibility } from "./state.js";
|
|
1
|
+
import type { DisplayStep, StepPhase, CompletedStepRecord, LogoVisibility } from "./state.js";
|
|
3
2
|
import type { TerminalDimensions } from "./outputBox.js";
|
|
4
3
|
type LogoLayout = "side-by-side" | "stacked" | "none";
|
|
5
4
|
export declare function determineLogoLayout(logoLines: string[], dimensions?: TerminalDimensions): LogoLayout;
|
|
6
5
|
export declare function stepTrackerIcon(phase: StepPhase): string;
|
|
7
6
|
export declare function isActivePhase(phase: StepPhase): boolean;
|
|
8
|
-
export declare function deriveAllStepPhases(steps:
|
|
7
|
+
export declare function deriveAllStepPhases(steps: DisplayStep[], currentStepIndex: number, currentPhase: StepPhase, completedStepRecords: CompletedStepRecord[]): StepPhase[];
|
|
9
8
|
type BuildHeaderOptions = {
|
|
10
|
-
steps:
|
|
9
|
+
steps: DisplayStep[];
|
|
11
10
|
version: string;
|
|
12
11
|
logoVisibility: LogoVisibility;
|
|
13
12
|
currentStepIndex: number;
|
package/dist/ui/screen.js
CHANGED
|
@@ -4,13 +4,9 @@ import { buildOutputBoxLines } from "./outputBox.js";
|
|
|
4
4
|
import { buildPromptLines } from "./prompt.js";
|
|
5
5
|
import { buildVersionReportLines } from "./versionReport.js";
|
|
6
6
|
export function createScreenRenderer(output = process.stdout) {
|
|
7
|
-
let previousLineCount = 0;
|
|
8
7
|
return (content) => {
|
|
9
|
-
|
|
10
|
-
output.write(`\x1b[${previousLineCount}A\x1b[J`);
|
|
11
|
-
}
|
|
8
|
+
output.write("\x1b[H\x1b[2J");
|
|
12
9
|
output.write(content);
|
|
13
|
-
previousLineCount = (content.match(/\n/g) ?? []).length;
|
|
14
10
|
};
|
|
15
11
|
}
|
|
16
12
|
export function buildScreen(state, terminalDimensions) {
|
package/dist/ui/state.d.ts
CHANGED
|
@@ -1,30 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { CommandOutputLine } from "../runner.js";
|
|
3
|
-
export type StepPhase = "pending" | "checking-availability" | "prompting-to-install" | "installing" | "prompting-to-run" | "running" | "complete" | "failed" | "skipped" | "not-available";
|
|
4
|
-
export type CompletedStepRecord = {
|
|
5
|
-
phase: StepPhase;
|
|
6
|
-
summaryNote?: string;
|
|
7
|
-
};
|
|
8
|
-
export type CurrentPrompt = {
|
|
9
|
-
question: string;
|
|
10
|
-
warning?: string;
|
|
11
|
-
};
|
|
12
|
-
export type ResolvedVersion = {
|
|
13
|
-
label: string;
|
|
14
|
-
value: string;
|
|
15
|
-
};
|
|
16
|
-
export type LogoVisibility = "visible" | "hidden";
|
|
17
|
-
export type RunCompletion = "in-progress" | "finished";
|
|
18
|
-
export type AppState = {
|
|
19
|
-
steps: Step[];
|
|
20
|
-
version: string;
|
|
21
|
-
logoVisibility: LogoVisibility;
|
|
22
|
-
currentStepIndex: number;
|
|
23
|
-
currentPhase: StepPhase;
|
|
24
|
-
completedStepRecords: CompletedStepRecord[];
|
|
25
|
-
currentOutputLines: CommandOutputLine[];
|
|
26
|
-
currentPrompt: CurrentPrompt | undefined;
|
|
27
|
-
runCompletion: RunCompletion;
|
|
28
|
-
versionReport: ResolvedVersion[] | undefined;
|
|
29
|
-
};
|
|
30
|
-
export declare function createInitialAppState(steps: Step[], version: string, logoVisibility: LogoVisibility): AppState;
|
|
1
|
+
export { type StepStage, type DisplayStep, type StepPhase, type CompletedStepRecord, type CurrentPrompt, type ResolvedVersion, type LogoVisibility, type RunCompletion, type UpdateState, type AppState, createInitialUpdateState, createInitialAppState, } from "../update/state.js";
|
package/dist/ui/state.js
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
return {
|
|
3
|
-
steps,
|
|
4
|
-
version,
|
|
5
|
-
logoVisibility,
|
|
6
|
-
currentStepIndex: 0,
|
|
7
|
-
currentPhase: "checking-availability",
|
|
8
|
-
completedStepRecords: [],
|
|
9
|
-
currentOutputLines: [],
|
|
10
|
-
currentPrompt: undefined,
|
|
11
|
-
runCompletion: "in-progress",
|
|
12
|
-
versionReport: undefined,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
1
|
+
export { createInitialUpdateState, createInitialAppState, } from "../update/state.js";
|
package/dist/ui/terminal.d.ts
CHANGED
package/dist/ui/terminal.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
export function hideCursorDuringExecution(options = {}) {
|
|
2
2
|
const output = options.output ?? process.stdout;
|
|
3
|
+
const screenBuffer = options.screenBuffer ?? "main";
|
|
4
|
+
if (screenBuffer === "alternate") {
|
|
5
|
+
output.write("\x1b[?1049h");
|
|
6
|
+
}
|
|
3
7
|
output.write("\x1b[?25l");
|
|
4
|
-
const
|
|
8
|
+
const restoreTerminal = () => {
|
|
9
|
+
output.write("\x1b[?25h");
|
|
10
|
+
if (screenBuffer === "alternate") {
|
|
11
|
+
output.write("\x1b[?1049l");
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const restoreOnExit = () => restoreTerminal();
|
|
5
15
|
const restoreAndExitOnInterrupt = () => {
|
|
6
|
-
|
|
16
|
+
restoreTerminal();
|
|
17
|
+
output.write("\n");
|
|
7
18
|
process.exit(130);
|
|
8
19
|
};
|
|
9
20
|
process.on("exit", restoreOnExit);
|
|
@@ -11,6 +22,6 @@ export function hideCursorDuringExecution(options = {}) {
|
|
|
11
22
|
return () => {
|
|
12
23
|
process.removeListener("exit", restoreOnExit);
|
|
13
24
|
process.removeListener("SIGINT", restoreAndExitOnInterrupt);
|
|
14
|
-
|
|
25
|
+
restoreTerminal();
|
|
15
26
|
};
|
|
16
27
|
}
|
package/dist/ui/versionReport.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { captureShellCommandOutput } from "../runner.js";
|
|
3
|
-
const VERSION_ENTRIES = [
|
|
4
|
-
{ label: "macOS", shellCommand: "sw_vers -productVersion" },
|
|
5
|
-
{ label: "Homebrew", shellCommand: "brew --version | head -n1" },
|
|
6
|
-
{ label: "Node", shellCommand: "node -v 2>/dev/null" },
|
|
7
|
-
{ label: "NPM", shellCommand: "npm -v 2>/dev/null" },
|
|
8
|
-
{ label: "Python", shellCommand: "python3 --version 2>/dev/null" },
|
|
9
|
-
{ label: "pip3", shellCommand: "pip3 --version 2>/dev/null | cut -d' ' -f1-2" },
|
|
10
|
-
{ label: "Zsh", shellCommand: "zsh --version 2>/dev/null" },
|
|
11
|
-
];
|
|
12
|
-
export async function collectVersions() {
|
|
13
|
-
return Promise.all(VERSION_ENTRIES.map(async ({ label, shellCommand }) => {
|
|
14
|
-
const value = await captureShellCommandOutput({ shellCommand }).catch(() => "");
|
|
15
|
-
return { label, value: value || "not installed" };
|
|
16
|
-
}));
|
|
17
|
-
}
|
|
18
2
|
export function buildVersionReportLines(versions) {
|
|
19
3
|
return [
|
|
20
4
|
chalk.dim(" ─── Versions ─────────────────────────"),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type StepLogWriter } from "../logger.js";
|
|
2
|
+
import { runShellCommand } from "../runner.js";
|
|
3
|
+
import { createDefaultStepRunContext, type Step } from "../steps/index.js";
|
|
4
|
+
import { type LogoVisibility, type ResolvedVersion, type UpdateState } from "./state.js";
|
|
5
|
+
export type PromptMode = "interactive" | "auto-accept";
|
|
6
|
+
export type ConfirmationAnswer = "yes" | "no" | "force";
|
|
7
|
+
type UpdateEngineDependencies = {
|
|
8
|
+
createLogWriter: (stepId: string) => Promise<StepLogWriter>;
|
|
9
|
+
runCommand: typeof runShellCommand;
|
|
10
|
+
createStepRunContext: typeof createDefaultStepRunContext;
|
|
11
|
+
};
|
|
12
|
+
type RunUpdateEngineOptions = {
|
|
13
|
+
steps: Step[];
|
|
14
|
+
promptMode: PromptMode;
|
|
15
|
+
version: string;
|
|
16
|
+
logoVisibility: LogoVisibility;
|
|
17
|
+
askForConfirmation: () => Promise<ConfirmationAnswer>;
|
|
18
|
+
collectVersions: () => Promise<ResolvedVersion[]>;
|
|
19
|
+
onStateChanged: (state: UpdateState) => void;
|
|
20
|
+
dependencies?: Partial<UpdateEngineDependencies>;
|
|
21
|
+
};
|
|
22
|
+
export declare function runUpdateEngine(options: RunUpdateEngineOptions): Promise<UpdateState>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createLogWriterForStep } from "../logger.js";
|
|
2
|
+
import { runShellCommand } from "../runner.js";
|
|
3
|
+
import { createDefaultStepRunContext } from "../steps/index.js";
|
|
4
|
+
import { toDisplaySteps } from "./displayStep.js";
|
|
5
|
+
import { createInitialUpdateState, } from "./state.js";
|
|
6
|
+
function resolveDependencies(dependencyOverrides) {
|
|
7
|
+
return {
|
|
8
|
+
createLogWriter: dependencyOverrides?.createLogWriter ?? createLogWriterForStep,
|
|
9
|
+
runCommand: dependencyOverrides?.runCommand ?? runShellCommand,
|
|
10
|
+
createStepRunContext: dependencyOverrides?.createStepRunContext ?? createDefaultStepRunContext,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
async function resolvePrompt(promptMode, askForConfirmation) {
|
|
14
|
+
if (promptMode === "auto-accept") {
|
|
15
|
+
return { promptMode: "auto-accept", decision: "run" };
|
|
16
|
+
}
|
|
17
|
+
const answer = await askForConfirmation();
|
|
18
|
+
if (answer === "no") {
|
|
19
|
+
return { promptMode: "interactive", decision: "skip" };
|
|
20
|
+
}
|
|
21
|
+
if (answer === "force") {
|
|
22
|
+
return { promptMode: "auto-accept", decision: "run" };
|
|
23
|
+
}
|
|
24
|
+
return { promptMode: "interactive", decision: "run" };
|
|
25
|
+
}
|
|
26
|
+
function reportState(state, onStateChanged) {
|
|
27
|
+
onStateChanged(state);
|
|
28
|
+
}
|
|
29
|
+
export async function runUpdateEngine(options) {
|
|
30
|
+
const dependencies = resolveDependencies(options.dependencies);
|
|
31
|
+
const state = createInitialUpdateState(toDisplaySteps(options.steps), options.version, options.logoVisibility);
|
|
32
|
+
let promptMode = options.promptMode;
|
|
33
|
+
reportState(state, options.onStateChanged);
|
|
34
|
+
for (let stepIndex = 0; stepIndex < options.steps.length; stepIndex++) {
|
|
35
|
+
const step = options.steps[stepIndex];
|
|
36
|
+
state.currentStepIndex = stepIndex;
|
|
37
|
+
state.currentPhase = "checking-availability";
|
|
38
|
+
state.currentOutputLines = [];
|
|
39
|
+
state.currentPrompt = undefined;
|
|
40
|
+
reportState(state, options.onStateChanged);
|
|
41
|
+
const availabilityStatus = (await step.checkIsAvailable())
|
|
42
|
+
? "available"
|
|
43
|
+
: "unavailable";
|
|
44
|
+
if (availabilityStatus === "unavailable" && !step.brewPackageToInstall) {
|
|
45
|
+
state.currentPhase = "not-available";
|
|
46
|
+
reportState(state, options.onStateChanged);
|
|
47
|
+
state.completedStepRecords.push({ phase: "not-available", summaryNote: "not installed" });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (availabilityStatus === "unavailable" && step.brewPackageToInstall) {
|
|
51
|
+
state.currentPhase = "prompting-to-install";
|
|
52
|
+
state.currentPrompt = {
|
|
53
|
+
question: `Install ${step.name} via Homebrew? (brew install ${step.brewPackageToInstall})`,
|
|
54
|
+
};
|
|
55
|
+
reportState(state, options.onStateChanged);
|
|
56
|
+
const installPrompt = await resolvePrompt(promptMode, options.askForConfirmation);
|
|
57
|
+
promptMode = installPrompt.promptMode;
|
|
58
|
+
state.currentPrompt = undefined;
|
|
59
|
+
if (installPrompt.decision === "skip") {
|
|
60
|
+
state.currentPhase = "skipped";
|
|
61
|
+
reportState(state, options.onStateChanged);
|
|
62
|
+
state.completedStepRecords.push({ phase: "skipped" });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
state.currentPhase = "installing";
|
|
66
|
+
reportState(state, options.onStateChanged);
|
|
67
|
+
try {
|
|
68
|
+
const installLogWriter = await dependencies.createLogWriter(`${step.id}-install`);
|
|
69
|
+
await dependencies.runCommand({
|
|
70
|
+
shellCommand: `brew install ${step.brewPackageToInstall}`,
|
|
71
|
+
onOutputLine: (line) => {
|
|
72
|
+
state.currentOutputLines.push(line);
|
|
73
|
+
reportState(state, options.onStateChanged);
|
|
74
|
+
},
|
|
75
|
+
logWriter: installLogWriter,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
state.currentPhase = "failed";
|
|
80
|
+
reportState(state, options.onStateChanged);
|
|
81
|
+
state.completedStepRecords.push({ phase: "failed", summaryNote: "install failed" });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
state.currentPhase = "prompting-to-run";
|
|
86
|
+
state.currentPrompt = { question: `Run ${step.name} update?`, warning: step.warning };
|
|
87
|
+
reportState(state, options.onStateChanged);
|
|
88
|
+
const runPrompt = await resolvePrompt(promptMode, options.askForConfirmation);
|
|
89
|
+
promptMode = runPrompt.promptMode;
|
|
90
|
+
state.currentPrompt = undefined;
|
|
91
|
+
if (runPrompt.decision === "skip") {
|
|
92
|
+
state.currentPhase = "skipped";
|
|
93
|
+
reportState(state, options.onStateChanged);
|
|
94
|
+
state.completedStepRecords.push({ phase: "skipped" });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
state.currentPhase = "running";
|
|
98
|
+
state.currentOutputLines = [];
|
|
99
|
+
reportState(state, options.onStateChanged);
|
|
100
|
+
const stepLogWriter = await dependencies.createLogWriter(step.id);
|
|
101
|
+
try {
|
|
102
|
+
await step.run(dependencies.createStepRunContext((line) => {
|
|
103
|
+
state.currentOutputLines.push(line);
|
|
104
|
+
reportState(state, options.onStateChanged);
|
|
105
|
+
}, stepLogWriter));
|
|
106
|
+
state.currentPhase = "complete";
|
|
107
|
+
state.currentOutputLines = [];
|
|
108
|
+
reportState(state, options.onStateChanged);
|
|
109
|
+
state.completedStepRecords.push({ phase: "complete", summaryNote: "updated" });
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
113
|
+
state.currentPhase = "failed";
|
|
114
|
+
state.currentOutputLines = [];
|
|
115
|
+
reportState(state, options.onStateChanged);
|
|
116
|
+
state.completedStepRecords.push({ phase: "failed", summaryNote: errorMessage });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
state.runCompletion = "finished";
|
|
120
|
+
state.currentOutputLines = [];
|
|
121
|
+
state.currentPrompt = undefined;
|
|
122
|
+
reportState(state, options.onStateChanged);
|
|
123
|
+
state.versionReport = await options.collectVersions();
|
|
124
|
+
reportState(state, options.onStateChanged);
|
|
125
|
+
return state;
|
|
126
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CommandOutputLine } from "../runner.js";
|
|
2
|
+
export type StepStage = "runtime" | "tools";
|
|
3
|
+
export type DisplayStep = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
stage: StepStage;
|
|
8
|
+
};
|
|
9
|
+
export type StepPhase = "pending" | "checking-availability" | "prompting-to-install" | "installing" | "prompting-to-run" | "running" | "complete" | "failed" | "skipped" | "not-available";
|
|
10
|
+
export type CompletedStepRecord = {
|
|
11
|
+
phase: StepPhase;
|
|
12
|
+
summaryNote?: string;
|
|
13
|
+
};
|
|
14
|
+
export type CurrentPrompt = {
|
|
15
|
+
question: string;
|
|
16
|
+
warning?: string;
|
|
17
|
+
};
|
|
18
|
+
export type ResolvedVersion = {
|
|
19
|
+
label: string;
|
|
20
|
+
value: string;
|
|
21
|
+
};
|
|
22
|
+
export type LogoVisibility = "visible" | "hidden";
|
|
23
|
+
export type RunCompletion = "in-progress" | "finished";
|
|
24
|
+
export type UpdateState = {
|
|
25
|
+
steps: DisplayStep[];
|
|
26
|
+
version: string;
|
|
27
|
+
logoVisibility: LogoVisibility;
|
|
28
|
+
currentStepIndex: number;
|
|
29
|
+
currentPhase: StepPhase;
|
|
30
|
+
completedStepRecords: CompletedStepRecord[];
|
|
31
|
+
currentOutputLines: CommandOutputLine[];
|
|
32
|
+
currentPrompt: CurrentPrompt | undefined;
|
|
33
|
+
runCompletion: RunCompletion;
|
|
34
|
+
versionReport: ResolvedVersion[] | undefined;
|
|
35
|
+
};
|
|
36
|
+
export type AppState = UpdateState;
|
|
37
|
+
export declare function createInitialUpdateState(steps: DisplayStep[], version: string, logoVisibility: LogoVisibility): UpdateState;
|
|
38
|
+
export declare const createInitialAppState: typeof createInitialUpdateState;
|