paratix 0.2.0 → 0.4.0
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 +60 -358
- package/dist/{chunk-ULJMW23T.js → chunk-C45YPXCX.js} +35 -2
- package/dist/chunk-C45YPXCX.js.map +1 -0
- package/dist/{chunk-DUIGEB2J.js → chunk-EGP3QRLV.js} +5 -2
- package/dist/chunk-EGP3QRLV.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/index.d.ts +16 -1
- package/dist/index.js +182 -98
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/index.js +1 -1
- package/llm-guide.md +39 -4
- package/package.json +1 -1
- package/dist/chunk-DUIGEB2J.js.map +0 -1
- package/dist/chunk-ULJMW23T.js.map +0 -1
|
@@ -140,7 +140,10 @@ function getCurrentOutputDepth() {
|
|
|
140
140
|
return Math.max(recipeOutputDepth, 0);
|
|
141
141
|
}
|
|
142
142
|
function getModuleIndent() {
|
|
143
|
-
|
|
143
|
+
if (recipeOutputDepth < 0) {
|
|
144
|
+
return OUTPUT_INDENT_UNIT;
|
|
145
|
+
}
|
|
146
|
+
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2);
|
|
144
147
|
}
|
|
145
148
|
function getRecipeHeaderIndent() {
|
|
146
149
|
return recipeOutputDepth < 0 ? "" : OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1);
|
|
@@ -436,4 +439,4 @@ export {
|
|
|
436
439
|
printSummary,
|
|
437
440
|
runSignalModules
|
|
438
441
|
};
|
|
439
|
-
//# sourceMappingURL=chunk-
|
|
442
|
+
//# sourceMappingURL=chunk-EGP3QRLV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/output.ts","../src/outputFormatting.ts","../src/signalOrchestration.ts"],"sourcesContent":["/* eslint-disable max-lines -- CLI output rendering is intentionally kept together */\nimport pc from \"picocolors\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\nimport { fitAnimatedModuleLine, formatDisplayModule } from \"./outputFormatting.js\"\nimport { CommandError } from \"./sshHelpers.js\"\n\nconst MODULE_NAME_WIDTH = 56\nconst MIN_MODULE_NAME_WIDTH = 12\nconst OUTPUT_INDENT_UNIT = \" \"\nconst SPINNER_FRAME_INTERVAL_MS = 80\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst STATUS_ICONS: Record<DisplayStatus, string> = {\n changed: pc.yellow(\"\\u21ba\"),\n failed: pc.red(\"\\u2717\"),\n ok: pc.green(\"\\u2713\"),\n skipped: pc.dim(\"\\u2298\"),\n waiting: pc.cyan(\"\\u23f8\"),\n}\n\nconst CLI_HEADER_LINES = [\n \" _ _ \",\n \" | | (_) \",\n \" _ __ __ _ _ __ __ _| |_ ___ __\",\n \" | '_ \\\\ / _` | '__/ _` | __| \\\\ \\\\/ /\",\n \" | |_) | (_| | | | (_| | |_| |> < \",\n \" | .__/ \\\\__,_|_| \\\\__,_|\\\\__|_/_/\\\\_\\\\\",\n \" | | \",\n \" |_| \",\n]\n\ntype ActiveSpinner = {\n detail?: string\n frameIndex: number\n interval: NodeJS.Timeout\n}\n\nlet activeSpinner: ActiveSpinner | null = null\nlet recipeOutputDepth = -1\n\nexport function renderCliHeader(version: string): string {\n const versionText = pc.dim(`v${version}`)\n return `${pc.cyan(CLI_HEADER_LINES.join(\"\\n\"))}${versionText}\\n`\n}\n\nexport function printCliHeader(version: string): void {\n console.log(renderCliHeader(version))\n}\n\nfunction supportsAnimatedModuleOutput(): boolean {\n return (\n process.stdout.isTTY &&\n typeof process.stdout.clearLine === \"function\" &&\n typeof process.stdout.cursorTo === \"function\"\n )\n}\n\nfunction getModuleIcon(status: DisplayStatus, waitingFrame?: string): string {\n return status === \"waiting\" ? pc.cyan(waitingFrame ?? \"|\") : STATUS_ICONS[status]\n}\n\nfunction getModuleStatusText(status: DisplayStatus): string {\n switch (status) {\n case \"changed\": {\n return pc.yellow(status)\n }\n case \"failed\": {\n return pc.red(status)\n }\n case \"ok\": {\n return pc.green(status)\n }\n case \"skipped\": {\n return pc.dim(status)\n }\n case \"waiting\": {\n return pc.cyan(\"running\")\n }\n }\n}\n\nfunction getCurrentOutputDepth(): number {\n return Math.max(recipeOutputDepth, 0)\n}\n\nfunction getModuleIndent(): string {\n if (recipeOutputDepth < 0) {\n return OUTPUT_INDENT_UNIT\n }\n\n return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2)\n}\n\nfunction getRecipeHeaderIndent(): string {\n return recipeOutputDepth < 0 ? \"\" : OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1)\n}\n\nfunction getErrorIndent(): string {\n return `${getModuleIndent()}│ `\n}\n\nfunction getContinuationIndent(): string {\n return `${getModuleIndent()} `\n}\n\nexport async function withRecipeOutputScope<T>(\n scopedOperation: () => Promise<T> | T\n): Promise<T> {\n recipeOutputDepth += 1\n try {\n return await scopedOperation()\n } finally {\n recipeOutputDepth -= 1\n }\n}\n\nfunction renderModuleLine(parameters: {\n detail?: string\n name: string\n status: DisplayStatus\n waitingFrame?: string\n}): string {\n const { detail, name, status, waitingFrame } = parameters\n const indent = getModuleIndent()\n const icon = getModuleIcon(status, waitingFrame)\n const statusText = getModuleStatusText(status)\n const detailSuffix = detail == null ? \"\" : ` ${pc.dim(detail)}`\n const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - indent.length, MIN_MODULE_NAME_WIDTH)\n return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`\n}\n\nfunction writeAnimatedModuleLine(line: string): void {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n process.stdout.write(fitAnimatedModuleLine(line, process.stdout.columns))\n}\n\nfunction stopAnimatedModuleLine(clearCurrentLine = false): void {\n if (activeSpinner == null) return\n\n clearInterval(activeSpinner.interval)\n activeSpinner = null\n\n if (clearCurrentLine && supportsAnimatedModuleOutput()) {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n }\n}\n\nexport function startModuleSpinner(name: string, detail?: string): void {\n if (!supportsAnimatedModuleOutput()) return\n\n stopAnimatedModuleLine()\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail,\n name,\n status: \"waiting\",\n terminalColumns: process.stdout.columns,\n })\n\n const spinner: ActiveSpinner = {\n detail: displayModule.detail,\n frameIndex: 0,\n interval: setInterval(() => {\n spinner.frameIndex = (spinner.frameIndex + 1) % SPINNER_FRAMES.length\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: spinner.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[spinner.frameIndex],\n })\n )\n }, SPINNER_FRAME_INTERVAL_MS),\n }\n\n activeSpinner = spinner\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: displayModule.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[0],\n })\n )\n}\n\nexport function resetLiveOutputForTests(): void {\n stopAnimatedModuleLine()\n recipeOutputDepth = -1\n}\n\n/**\n * Print a bold, colored header line marking the start of a recipe run.\n * @param name - The recipe or server name to display.\n */\nexport function printRecipeHeader(name: string): void {\n stopAnimatedModuleLine(true)\n const header = pc.bold(pc.blue(`[${name}]`))\n console.log(`${getRecipeHeaderIndent()}${header}`)\n}\n\nexport function printRunContext(parameters: {\n dryRun: boolean\n host: string\n name: string\n ports: number[]\n}): void {\n const mode = parameters.dryRun ? pc.yellow(\"dry-run\") : pc.green(\"apply\")\n const ports = parameters.ports.join(\", \")\n console.log(\n pc.dim(`Run ${parameters.name} · host ${parameters.host} · ports ${ports} · mode ${mode}`)\n )\n}\n\n/**\n * Print a single module result row with a status icon, name, and colored status label.\n *\n * @param name - The module name shown in the left column.\n * @param status - One of the known status strings (`ok`, `changed`, `skipped`, `failed`).\n * @param detail - Optional short detail appended in dim text after the status.\n */\nexport function printModuleResult(name: string, status: DisplayStatus, detail?: string): void {\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail,\n name,\n status,\n terminalColumns: process.stdout.columns,\n })\n const line = renderModuleLine({\n detail: displayModule.detail,\n name: displayModule.name,\n status,\n })\n if (supportsAnimatedModuleOutput() && activeSpinner != null) {\n stopAnimatedModuleLine()\n writeAnimatedModuleLine(line)\n process.stdout.write(\"\\n\")\n for (const detailLine of displayModule.detailLines) {\n process.stdout.write(`${getContinuationIndent()}${pc.dim(detailLine)}\\n`)\n }\n return\n }\n\n console.log(line)\n for (const detailLine of displayModule.detailLines) {\n console.log(`${getContinuationIndent()}${pc.dim(detailLine)}`)\n }\n}\n\n/**\n * Print captured stderr and stdout from a failed command in a red bordered block.\n * Outputs nothing if both streams are empty.\n *\n * @param stdout - Captured standard output of the failed command.\n * @param stderr - Captured standard error of the failed command.\n */\nexport function printCommandError(stdout: string, stderr: string): void {\n const lines: string[] = []\n if (stderr.trim()) {\n lines.push(...stderr.trim().split(\"\\n\"))\n }\n if (stdout.trim()) {\n lines.push(...stdout.trim().split(\"\\n\"))\n }\n if (lines.length > 0) {\n console.error(pc.red(`${getErrorIndent()}Error output:`))\n for (const line of lines) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\n/**\n * Print the full (untruncated) stdout and stderr of a failed command.\n * Used in verbose mode to show the complete output that was truncated in the error message.\n *\n * @param stdout - Full standard output of the failed command.\n * @param stderr - Full standard error of the failed command.\n */\nexport function printVerboseCommandError(stdout: string, stderr: string): void {\n if (stderr.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stderr:`))\n for (const line of stderr.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n if (stdout.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stdout:`))\n for (const line of stdout.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\nfunction printVerboseErrorBlock(label: string, content: string): void {\n if (!content.trim()) {\n return\n }\n\n console.error(pc.red(`${getErrorIndent()}${label}`))\n for (const line of content.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n}\n\nfunction getErrorCause(error: Error): unknown {\n return (error as { cause?: unknown } & Error).cause\n}\n\nfunction printVerboseErrorCause(cause: unknown, depth: number): void {\n const label = `Cause ${depth}:`\n if (cause instanceof Error) {\n const stack = cause.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(cause)\n printVerboseErrorBlock(label, stackOrMessage)\n const nestedCause = getErrorCause(cause)\n if (nestedCause !== undefined) {\n printVerboseErrorCause(nestedCause, depth + 1)\n }\n return\n }\n\n printVerboseErrorBlock(label, String(cause))\n}\n\nfunction printVerboseGenericError(error: Error): void {\n const stack = error.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(error)\n printVerboseErrorBlock(\"Full stack:\", stackOrMessage)\n const cause = getErrorCause(error)\n if (cause !== undefined) {\n printVerboseErrorCause(cause, 1)\n }\n}\n\n/**\n * Print the error message of a failed command and, when verbose mode is active\n * and the error is a {@link CommandError}, the full untruncated output.\n *\n * @param error - The caught error value.\n * @param verbose - Whether to show full stdout/stderr.\n */\nexport function printCommandFailure(error: unknown, verbose: boolean): void {\n if (verbose && error instanceof CommandError) {\n // Print only the exit-code line, skip the truncated output and hint\n const summaryLine = error.message.split(\"\\n\")[0]\n printCommandError(\"\", summaryLine)\n printVerboseCommandError(error.fullStdout, error.fullStderr)\n return\n }\n\n printCommandError(\"\", String(error))\n if (verbose && error instanceof Error) {\n printVerboseGenericError(error)\n }\n}\n\n/**\n * Print a run summary line with counts for each status category.\n *\n * @param stats - Aggregated counts from the completed run.\n * @param stats.changed - Number of modules that changed state.\n * @param stats.ok - Number of modules already in desired state.\n * @param stats.skipped - Number of modules skipped.\n * @param stats.failed - Number of modules that failed.\n * @param stats.signals - Number of signals triggered.\n */\nexport function printSummary(stats: {\n changed: number\n failed: number\n ok: number\n signals: number\n skipped: number\n}): void {\n const parts = [\n pc.yellow(`${stats.changed} changed`),\n pc.green(`${stats.ok} ok`),\n pc.dim(`${stats.skipped} skipped`),\n stats.failed > 0 ? pc.red(`${stats.failed} failed`) : `${stats.failed} failed`,\n pc.cyan(`${stats.signals} signals triggered`),\n ]\n console.log(`\\n${parts.join(pc.dim(\" \\u00b7 \"))}`)\n}\n","import { stripVTControlCharacters } from \"node:util\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst PACKAGE_MODULE_SUFFIX_LENGTH = 2\nconst PACKAGE_COLUMN_GAP_WIDTH = 2\nconst DEFAULT_TERMINAL_COLUMNS = 100\nconst MIN_PACKAGE_COLUMNS_WIDTH = 24\nconst MIN_PACKAGE_COLUMN_WIDTH = 18\nconst MIN_ANIMATED_LINE_COLUMNS = 8\n\nexport type DisplayModule = {\n detail?: string\n detailLines: string[]\n name: string\n}\n\nfunction splitPackageModuleName(name: string): { packages: string[]; summaryName: string } | null {\n for (const prefix of [\"package.installed: \", \"package.absent: \"]) {\n if (!name.startsWith(prefix)) continue\n\n const packages = name\n .slice(prefix.length)\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean)\n if (packages.length === 0) return null\n\n return {\n packages,\n summaryName: prefix.slice(0, -PACKAGE_MODULE_SUFFIX_LENGTH),\n }\n }\n\n return null\n}\n\nfunction getPackageColumns(parameters: {\n continuationIndentWidth: number\n packages: string[]\n terminalColumns?: number\n}): string[] {\n if (parameters.packages.length <= 1) return parameters.packages\n\n const availableWidth = Math.max(\n (parameters.terminalColumns ?? DEFAULT_TERMINAL_COLUMNS) - parameters.continuationIndentWidth,\n MIN_PACKAGE_COLUMNS_WIDTH\n )\n const widestPackage = Math.max(...parameters.packages.map((entry) => entry.length))\n const columnWidth = Math.max(widestPackage + PACKAGE_COLUMN_GAP_WIDTH, MIN_PACKAGE_COLUMN_WIDTH)\n const columnCount = Math.max(Math.floor(availableWidth / columnWidth), 1)\n const rowCount = Math.ceil(parameters.packages.length / columnCount)\n\n return Array.from({ length: rowCount }, (_row, rowIndex) =>\n Array.from({ length: columnCount }, (_column, columnIndex) => {\n const packageIndex = rowIndex + columnIndex * rowCount\n const packageName = parameters.packages.at(packageIndex)\n if (packageName == null) return \"\"\n const isLastVisibleColumn =\n columnIndex === columnCount - 1 || packageIndex + rowCount >= parameters.packages.length\n return isLastVisibleColumn ? packageName : packageName.padEnd(columnWidth)\n })\n .filter(Boolean)\n .join(\"\")\n )\n}\n\nfunction getPackageSummaryDetail(packageCount: number, detail?: string): string {\n const packageLabel = packageCount === 1 ? \"1 package\" : `${packageCount} packages`\n return detail == null ? packageLabel : `${packageLabel} · ${detail}`\n}\n\nexport function fitAnimatedModuleLine(line: string, columns?: number): string {\n if (columns === undefined || columns < MIN_ANIMATED_LINE_COLUMNS) return line\n\n const plainLine = stripVTControlCharacters(line)\n if (plainLine.length < columns) return line\n\n return `${plainLine.slice(0, columns - 1)}\\u2026`\n}\n\nexport function formatDisplayModule(parameters: {\n continuationIndentWidth: number\n detail?: string\n name: string\n status: DisplayStatus\n terminalColumns?: number\n}): DisplayModule {\n const packageModule = splitPackageModuleName(parameters.name)\n if (packageModule == null) {\n return {\n detail: parameters.detail,\n detailLines: [],\n name: parameters.name,\n }\n }\n\n return {\n detail: getPackageSummaryDetail(packageModule.packages.length, parameters.detail),\n detailLines:\n parameters.status === \"waiting\"\n ? []\n : getPackageColumns({\n continuationIndentWidth: parameters.continuationIndentWidth,\n packages: packageModule.packages,\n terminalColumns: parameters.terminalColumns,\n }),\n name: packageModule.summaryName,\n }\n}\n","import type {\n Environment,\n Module,\n ModuleStatus,\n OrchestrationStep,\n SshConnection,\n} from \"./types.js\"\n\nimport { assertValidModuleMetaEntries, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport { printCommandFailure, printModuleResult, startModuleSpinner } from \"./output.js\"\n\nexport type SignalHooks = {\n onSignalFinished?: (status: ModuleStatus) => void\n onSignalStarted?: () => void\n}\n\nexport type SignalRunStatus = \"changed\" | \"failed\"\n\ntype SignalRunParameters = {\n environment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}\n\nfunction handleSignalResult(parameters: {\n hooks?: SignalHooks\n result: Awaited<ReturnType<Module[\"apply\"]>>\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { hooks, result, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, result.status)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n hooks?.onSignalFinished?.(result.status)\n return result.status === \"failed\" ? \"failed\" : \"changed\"\n}\n\nfunction handleSignalFailure(parameters: {\n error: unknown\n hooks?: SignalHooks\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { error, hooks, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, \"failed\")\n printCommandFailure(error, verbose)\n hooks?.onSignalFinished?.(\"failed\")\n return \"failed\"\n}\n\nasync function applySignalMeta(parameters: {\n currentEnvironment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n result: Awaited<ReturnType<Module[\"apply\"]>>\n}): Promise<Environment> {\n assertValidModuleMetaEntries(parameters.result.meta)\n const nextEnvironment = await mergeEnvironmentFromMeta(\n parameters.currentEnvironment,\n parameters.result.meta\n )\n await parameters.onSignalStep?.({\n env: nextEnvironment,\n meta: parameters.result.meta,\n status: parameters.result.status,\n })\n return nextEnvironment\n}\n\nasync function runOneSignal(parameters: {\n currentEnvironment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n signal: Module\n ssh: null | SshConnection\n verbose: boolean\n}): Promise<{ nextEnvironment: Environment; status: SignalRunStatus }> {\n const connection = parameters.signal.local === true ? null : parameters.ssh\n startModuleSpinner(`signal: ${parameters.signal.name}`)\n const result = await parameters.signal.apply(connection, parameters.currentEnvironment)\n const nextEnvironment = await applySignalMeta({\n currentEnvironment: parameters.currentEnvironment,\n onSignalStep: parameters.onSignalStep,\n result,\n })\n return {\n nextEnvironment,\n status: handleSignalResult({\n hooks: parameters.hooks,\n result,\n signalName: parameters.signal.name,\n verbose: parameters.verbose,\n }),\n }\n}\n\nexport async function runSignalModules(parameters: SignalRunParameters): Promise<SignalRunStatus> {\n const getShutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let currentEnvironment = parameters.environment\n let status: SignalRunStatus = \"changed\"\n\n for (const signal of parameters.signals) {\n if (getShutdownSignal() != null) break\n parameters.hooks?.onSignalStarted?.()\n try {\n // eslint-disable-next-line no-await-in-loop\n const signalStep = await runOneSignal({\n currentEnvironment,\n hooks: parameters.hooks,\n onSignalStep: parameters.onSignalStep,\n signal,\n ssh: parameters.ssh,\n verbose,\n })\n currentEnvironment = signalStep.nextEnvironment\n if (signalStep.status === \"failed\") status = \"failed\"\n } catch (error) {\n status = handleSignalFailure({\n error,\n hooks: parameters.hooks,\n signalName: signal.name,\n verbose,\n })\n }\n }\n\n return status\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
runSignalModules,
|
|
9
9
|
startModuleSpinner,
|
|
10
10
|
withRecipeOutputScope
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EGP3QRLV.js";
|
|
12
12
|
import {
|
|
13
13
|
SshConnectionImpl,
|
|
14
14
|
assertValidModuleMetaEntries,
|
|
@@ -695,7 +695,7 @@ async function loadServerDefinitionFromFile(file, options) {
|
|
|
695
695
|
return definition;
|
|
696
696
|
}
|
|
697
697
|
var program = new Command();
|
|
698
|
-
program.name("paratix").description("Idempotent VPS setup tool in TypeScript").version("0.
|
|
698
|
+
program.name("paratix").description("Idempotent VPS setup tool in TypeScript").version("0.4.0");
|
|
699
699
|
program.command("apply <file>").description("Apply a server definition").option(
|
|
700
700
|
"--dry-run",
|
|
701
701
|
"Only check, do not apply. Some modules validate prospective config but cannot verify runtime restarts.",
|
|
@@ -707,7 +707,7 @@ program.command("apply <file>").description("Apply a server definition").option(
|
|
|
707
707
|
DEFAULT_RECONNECT_TIMEOUT_SECONDS
|
|
708
708
|
).option("--verbose", "Show full stack traces on error", false).action(async (file, options) => {
|
|
709
709
|
try {
|
|
710
|
-
printCliHeader("0.
|
|
710
|
+
printCliHeader("0.4.0");
|
|
711
711
|
const environmentOverrides = applyCliEnvironmentOverrides(
|
|
712
712
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>
|
|
713
713
|
options.env,
|
package/dist/index.d.ts
CHANGED
|
@@ -80,7 +80,22 @@ declare const signals: {
|
|
|
80
80
|
* @example
|
|
81
81
|
* when(env => env["DEPLOY_ENV"] === "production", service.enabled("fail2ban"))
|
|
82
82
|
*/
|
|
83
|
-
declare function
|
|
83
|
+
declare function baseWhen(condition: (environment: Environment) => boolean, ...modules: Module[]): Module;
|
|
84
|
+
type WhenFunction = {
|
|
85
|
+
commandExists: (commandName: string, ...modules: Module[]) => Module;
|
|
86
|
+
commandMissing: (commandName: string, ...modules: Module[]) => Module;
|
|
87
|
+
fileExists: (path: string, ...modules: Module[]) => Module;
|
|
88
|
+
fileMissing: (path: string, ...modules: Module[]) => Module;
|
|
89
|
+
packageAbsent: (packageName: string, ...modules: Module[]) => Module;
|
|
90
|
+
packageInstalled: (packageName: string, ...modules: Module[]) => Module;
|
|
91
|
+
pathExists: (path: string, ...modules: Module[]) => Module;
|
|
92
|
+
pathMissing: (path: string, ...modules: Module[]) => Module;
|
|
93
|
+
socketExists: (path: string, ...modules: Module[]) => Module;
|
|
94
|
+
socketMissing: (path: string, ...modules: Module[]) => Module;
|
|
95
|
+
symlinkExists: (path: string, ...modules: Module[]) => Module;
|
|
96
|
+
symlinkMissing: (path: string, ...modules: Module[]) => Module;
|
|
97
|
+
} & typeof baseWhen;
|
|
98
|
+
declare const when: WhenFunction;
|
|
84
99
|
|
|
85
100
|
type BooleanEnvironmentMetaEntry = {
|
|
86
101
|
valueType: "boolean";
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
runSignalModules,
|
|
6
6
|
startModuleSpinner,
|
|
7
7
|
withRecipeOutputScope
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EGP3QRLV.js";
|
|
9
9
|
import {
|
|
10
10
|
NEEDS_APPLY,
|
|
11
11
|
apt,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
command,
|
|
14
14
|
compose,
|
|
15
15
|
cron,
|
|
16
|
+
detectPackageManager,
|
|
16
17
|
download,
|
|
17
18
|
failed,
|
|
18
19
|
failedCommand,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
git,
|
|
21
22
|
group,
|
|
22
23
|
hostname,
|
|
24
|
+
isPackageInstalled,
|
|
23
25
|
mount,
|
|
24
26
|
net,
|
|
25
27
|
op,
|
|
@@ -35,7 +37,7 @@ import {
|
|
|
35
37
|
systemd,
|
|
36
38
|
ufw,
|
|
37
39
|
user
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-C45YPXCX.js";
|
|
39
41
|
import {
|
|
40
42
|
CommandError,
|
|
41
43
|
assertValidModuleMetaEntries,
|
|
@@ -60,6 +62,165 @@ import {
|
|
|
60
62
|
validateSshConfig
|
|
61
63
|
} from "./chunk-G3BMCQKU.js";
|
|
62
64
|
|
|
65
|
+
// src/conditionalModules.ts
|
|
66
|
+
function createConditionalApplyState(environment) {
|
|
67
|
+
return { environment: { ...environment }, meta: [], status: "ok" };
|
|
68
|
+
}
|
|
69
|
+
function markConditionalApplyChanged(state) {
|
|
70
|
+
return { ...state, status: "changed" };
|
|
71
|
+
}
|
|
72
|
+
async function executeConditionalApply(parameters) {
|
|
73
|
+
const { dryRun, environment, module, ssh: ssh2 } = parameters;
|
|
74
|
+
if (dryRun && module._applyDryRun != null) {
|
|
75
|
+
return module._applyDryRun(ssh2, environment);
|
|
76
|
+
}
|
|
77
|
+
return module.apply(ssh2, environment);
|
|
78
|
+
}
|
|
79
|
+
function shouldExecuteConditionalApply(module, dryRun) {
|
|
80
|
+
if (!dryRun) return true;
|
|
81
|
+
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
82
|
+
}
|
|
83
|
+
async function mergeConditionalApplyState(state, result) {
|
|
84
|
+
const environment = await mergeEnvironmentFromMeta(state.environment, result.meta);
|
|
85
|
+
return {
|
|
86
|
+
environment,
|
|
87
|
+
flushSignals: result._flushSignals === true ? true : state.flushSignals,
|
|
88
|
+
meta: result.meta == null ? state.meta : [...state.meta, ...result.meta],
|
|
89
|
+
status: result.status === "changed" ? "changed" : state.status,
|
|
90
|
+
stopRun: result._stopRun === true ? true : state.stopRun
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function applyConditionalModules(parameters) {
|
|
94
|
+
const { dryRun = false, modules, ssh: ssh2 } = parameters;
|
|
95
|
+
let state = createConditionalApplyState(parameters.environment);
|
|
96
|
+
for (const currentModule of modules) {
|
|
97
|
+
const checkResult = await currentModule.check(ssh2, state.environment);
|
|
98
|
+
if (checkResult === "ok") continue;
|
|
99
|
+
if (!shouldExecuteConditionalApply(currentModule, dryRun)) {
|
|
100
|
+
state = markConditionalApplyChanged(state);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const result = await executeConditionalApply({
|
|
104
|
+
dryRun,
|
|
105
|
+
environment: state.environment,
|
|
106
|
+
module: currentModule,
|
|
107
|
+
ssh: ssh2
|
|
108
|
+
});
|
|
109
|
+
if (result.status === "failed") return result;
|
|
110
|
+
state = await mergeConditionalApplyState(state, result);
|
|
111
|
+
if (state.stopRun === true) break;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
_flushSignals: state.flushSignals,
|
|
115
|
+
_stopRun: state.stopRun,
|
|
116
|
+
meta: state.meta.length === 0 ? void 0 : state.meta,
|
|
117
|
+
status: state.status
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function shouldExecuteConditionalDryRun(module) {
|
|
121
|
+
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
122
|
+
}
|
|
123
|
+
function whenNeedsDryRunApply(modules) {
|
|
124
|
+
return modules.some((module) => shouldExecuteConditionalDryRun(module));
|
|
125
|
+
}
|
|
126
|
+
async function checkConditionalModules(modules, ssh2, environment) {
|
|
127
|
+
const currentEnvironment = { ...environment };
|
|
128
|
+
for (const currentModule of modules) {
|
|
129
|
+
const result = await currentModule.check(ssh2, currentEnvironment);
|
|
130
|
+
if (result === NEEDS_APPLY) {
|
|
131
|
+
return NEEDS_APPLY;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return "ok";
|
|
135
|
+
}
|
|
136
|
+
function createWhenDryRunApply(condition, modules, needsDryRunApply) {
|
|
137
|
+
if (!needsDryRunApply) return void 0;
|
|
138
|
+
return async (ssh2, environment) => {
|
|
139
|
+
if (!await condition(ssh2, environment)) {
|
|
140
|
+
return { status: "skipped" };
|
|
141
|
+
}
|
|
142
|
+
return applyConditionalModules({ dryRun: true, environment, modules, ssh: ssh2 });
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function createConditionalModule(parameters) {
|
|
146
|
+
const needsDryRunApply = whenNeedsDryRunApply(parameters.modules);
|
|
147
|
+
const applyDryRun = createWhenDryRunApply(
|
|
148
|
+
parameters.condition,
|
|
149
|
+
parameters.modules,
|
|
150
|
+
needsDryRunApply
|
|
151
|
+
);
|
|
152
|
+
return {
|
|
153
|
+
...parameters.modules.some((module) => module._dryRunBlocker === true) ? { _dryRunBlocker: true } : {},
|
|
154
|
+
...parameters.modules.some((module) => module._dryRunMetaProducer === true) ? { _dryRunMetaProducer: true } : {},
|
|
155
|
+
...applyDryRun == null ? {} : { _applyDryRun: applyDryRun },
|
|
156
|
+
async apply(ssh2, environment) {
|
|
157
|
+
if (!await parameters.condition(ssh2, environment)) {
|
|
158
|
+
return { status: "skipped" };
|
|
159
|
+
}
|
|
160
|
+
return applyConditionalModules({ environment, modules: parameters.modules, ssh: ssh2 });
|
|
161
|
+
},
|
|
162
|
+
async check(ssh2, environment) {
|
|
163
|
+
if (!await parameters.condition(ssh2, environment)) {
|
|
164
|
+
return "ok";
|
|
165
|
+
}
|
|
166
|
+
return checkConditionalModules(parameters.modules, ssh2, environment);
|
|
167
|
+
},
|
|
168
|
+
name: parameters.name
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function filesystemTypeName(testFlag) {
|
|
172
|
+
switch (testFlag) {
|
|
173
|
+
case "-d": {
|
|
174
|
+
return "path";
|
|
175
|
+
}
|
|
176
|
+
case "-f": {
|
|
177
|
+
return "file";
|
|
178
|
+
}
|
|
179
|
+
case "-L": {
|
|
180
|
+
return "symlink";
|
|
181
|
+
}
|
|
182
|
+
case "-S": {
|
|
183
|
+
return "socket";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function createFilesystemGuard(parameters) {
|
|
188
|
+
const typeName = filesystemTypeName(parameters.testFlag);
|
|
189
|
+
return createConditionalModule({
|
|
190
|
+
condition: async (ssh2) => {
|
|
191
|
+
if (ssh2 == null) return false;
|
|
192
|
+
const exists = await ssh2.test(`test ${parameters.testFlag} ${shellQuote(parameters.path)}`);
|
|
193
|
+
return parameters.invert ? !exists : exists;
|
|
194
|
+
},
|
|
195
|
+
modules: parameters.modules,
|
|
196
|
+
name: `when.${typeName}${parameters.invert ? "Missing" : "Exists"}: ${parameters.path}`
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function createCommandGuard(commandName, invert, modules) {
|
|
200
|
+
return createConditionalModule({
|
|
201
|
+
condition: async (ssh2) => {
|
|
202
|
+
if (ssh2 == null) return false;
|
|
203
|
+
const exists = await ssh2.test(`command -v ${shellQuote(commandName)} >/dev/null 2>&1`);
|
|
204
|
+
return invert ? !exists : exists;
|
|
205
|
+
},
|
|
206
|
+
modules,
|
|
207
|
+
name: `when.command${invert ? "Missing" : "Exists"}: ${commandName}`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function createPackageGuard(packageName, invert, modules) {
|
|
211
|
+
return createConditionalModule({
|
|
212
|
+
condition: async (ssh2) => {
|
|
213
|
+
if (ssh2 == null) return false;
|
|
214
|
+
const pm = await detectPackageManager(ssh2);
|
|
215
|
+
if (pm == null) return false;
|
|
216
|
+
const installed = await isPackageInstalled(ssh2, pm, packageName);
|
|
217
|
+
return invert ? !installed : installed;
|
|
218
|
+
},
|
|
219
|
+
modules,
|
|
220
|
+
name: `when.package${invert ? "Absent" : "Installed"}: ${packageName}`
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
63
224
|
// src/builtins.ts
|
|
64
225
|
function assert(condition, message) {
|
|
65
226
|
return {
|
|
@@ -192,104 +353,27 @@ var signals = {
|
|
|
192
353
|
};
|
|
193
354
|
}
|
|
194
355
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const checkResult = await currentModule.check(ssh2, state.environment);
|
|
200
|
-
if (checkResult === "ok") continue;
|
|
201
|
-
if (!shouldExecuteConditionalApply(currentModule, dryRun)) {
|
|
202
|
-
state = markConditionalApplyChanged(state);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
const result = await executeConditionalApply({
|
|
206
|
-
dryRun,
|
|
207
|
-
environment: state.environment,
|
|
208
|
-
module: currentModule,
|
|
209
|
-
ssh: ssh2
|
|
210
|
-
});
|
|
211
|
-
if (result.status === "failed") return result;
|
|
212
|
-
state = await mergeConditionalApplyState(state, result);
|
|
213
|
-
if (state.stopRun === true) break;
|
|
214
|
-
}
|
|
215
|
-
return {
|
|
216
|
-
_flushSignals: state.flushSignals,
|
|
217
|
-
_stopRun: state.stopRun,
|
|
218
|
-
meta: state.meta.length === 0 ? void 0 : state.meta,
|
|
219
|
-
status: state.status
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
function shouldExecuteConditionalApply(module, dryRun) {
|
|
223
|
-
if (!dryRun) return true;
|
|
224
|
-
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
225
|
-
}
|
|
226
|
-
function createConditionalApplyState(environment) {
|
|
227
|
-
return { environment: { ...environment }, meta: [], status: "ok" };
|
|
228
|
-
}
|
|
229
|
-
function markConditionalApplyChanged(state) {
|
|
230
|
-
return { ...state, status: "changed" };
|
|
231
|
-
}
|
|
232
|
-
async function executeConditionalApply(parameters) {
|
|
233
|
-
const { dryRun, environment, module, ssh: ssh2 } = parameters;
|
|
234
|
-
if (dryRun && module._applyDryRun != null) {
|
|
235
|
-
return module._applyDryRun(ssh2, environment);
|
|
236
|
-
}
|
|
237
|
-
return module.apply(ssh2, environment);
|
|
238
|
-
}
|
|
239
|
-
function whenNeedsDryRunApply(modules) {
|
|
240
|
-
return modules.some((module) => shouldExecuteConditionalDryRun(module));
|
|
241
|
-
}
|
|
242
|
-
function shouldExecuteConditionalDryRun(module) {
|
|
243
|
-
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
244
|
-
}
|
|
245
|
-
async function mergeConditionalApplyState(state, result) {
|
|
246
|
-
const environment = await mergeEnvironmentFromMeta(state.environment, result.meta);
|
|
247
|
-
return {
|
|
248
|
-
environment,
|
|
249
|
-
flushSignals: result._flushSignals === true ? true : state.flushSignals,
|
|
250
|
-
meta: result.meta == null ? state.meta : [...state.meta, ...result.meta],
|
|
251
|
-
status: result.status === "changed" ? "changed" : state.status,
|
|
252
|
-
stopRun: result._stopRun === true ? true : state.stopRun
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function createWhenDryRunApply(condition, modules, needsDryRunApply) {
|
|
256
|
-
if (!needsDryRunApply) return void 0;
|
|
257
|
-
return async (ssh2, environment) => {
|
|
258
|
-
if (!condition(environment)) {
|
|
259
|
-
return { status: "skipped" };
|
|
260
|
-
}
|
|
261
|
-
return applyConditionalModules({ dryRun: true, environment, modules, ssh: ssh2 });
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
function when(condition, ...modules) {
|
|
265
|
-
const needsDryRunApply = whenNeedsDryRunApply(modules);
|
|
266
|
-
const applyDryRun = createWhenDryRunApply(condition, modules, needsDryRunApply);
|
|
267
|
-
return {
|
|
268
|
-
...modules.some((module) => module._dryRunBlocker === true) ? { _dryRunBlocker: true } : {},
|
|
269
|
-
...modules.some((module) => module._dryRunMetaProducer === true) ? { _dryRunMetaProducer: true } : {},
|
|
270
|
-
...applyDryRun == null ? {} : { _applyDryRun: applyDryRun },
|
|
271
|
-
async apply(ssh2, environment) {
|
|
272
|
-
if (!condition(environment)) {
|
|
273
|
-
return { status: "skipped" };
|
|
274
|
-
}
|
|
275
|
-
return applyConditionalModules({ environment, modules, ssh: ssh2 });
|
|
276
|
-
},
|
|
277
|
-
async check(ssh2, environment) {
|
|
278
|
-
if (!condition(environment)) {
|
|
279
|
-
return "ok";
|
|
280
|
-
}
|
|
281
|
-
const currentEnvironment = { ...environment };
|
|
282
|
-
for (const currentModule of modules) {
|
|
283
|
-
const result = await currentModule.check(ssh2, currentEnvironment);
|
|
284
|
-
if (result === NEEDS_APPLY) {
|
|
285
|
-
return NEEDS_APPLY;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return "ok";
|
|
289
|
-
},
|
|
356
|
+
function baseWhen(condition, ...modules) {
|
|
357
|
+
return createConditionalModule({
|
|
358
|
+
condition: (_ssh, environment) => condition(environment),
|
|
359
|
+
modules,
|
|
290
360
|
name: `when: conditional (${modules.length} modules)`
|
|
291
|
-
};
|
|
361
|
+
});
|
|
292
362
|
}
|
|
363
|
+
var when = Object.assign(baseWhen, {
|
|
364
|
+
commandExists: (commandName, ...modules) => createCommandGuard(commandName, false, modules),
|
|
365
|
+
commandMissing: (commandName, ...modules) => createCommandGuard(commandName, true, modules),
|
|
366
|
+
fileExists: (path, ...modules) => createFilesystemGuard({ invert: false, modules, path, testFlag: "-f" }),
|
|
367
|
+
fileMissing: (path, ...modules) => createFilesystemGuard({ invert: true, modules, path, testFlag: "-f" }),
|
|
368
|
+
packageAbsent: (packageName, ...modules) => createPackageGuard(packageName, true, modules),
|
|
369
|
+
packageInstalled: (packageName, ...modules) => createPackageGuard(packageName, false, modules),
|
|
370
|
+
pathExists: (path, ...modules) => createFilesystemGuard({ invert: false, modules, path, testFlag: "-d" }),
|
|
371
|
+
pathMissing: (path, ...modules) => createFilesystemGuard({ invert: true, modules, path, testFlag: "-d" }),
|
|
372
|
+
socketExists: (path, ...modules) => createFilesystemGuard({ invert: false, modules, path, testFlag: "-S" }),
|
|
373
|
+
socketMissing: (path, ...modules) => createFilesystemGuard({ invert: true, modules, path, testFlag: "-S" }),
|
|
374
|
+
symlinkExists: (path, ...modules) => createFilesystemGuard({ invert: false, modules, path, testFlag: "-L" }),
|
|
375
|
+
symlinkMissing: (path, ...modules) => createFilesystemGuard({ invert: true, modules, path, testFlag: "-L" })
|
|
376
|
+
});
|
|
293
377
|
|
|
294
378
|
// src/recipe.ts
|
|
295
379
|
var INTERRUPTED_BEFORE_APPLY = /* @__PURE__ */ Symbol("recipe-interrupted-before-apply");
|