paratix 0.7.0 → 0.9.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/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/dryRunRecipe.ts","../src/runnerHelpers.ts","../src/runner.ts"],"sourcesContent":["import { Command } from \"commander\"\nimport { realpathSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { inspect } from \"node:util\"\nimport pc from \"picocolors\"\n\nimport type { Environment, ServerDefinition } from \"./types.js\"\n\nimport { printCliHeader } from \"./output.js\"\nimport { runPlaybook } from \"./runner.js\"\nimport { collectSshConfigErrors } from \"./serverDefinitionValidation.js\"\n\ndeclare const PACKAGE_DISPLAY_VERSION: string\n\nconst SECONDS_TO_MS = 1000\nconst DEFAULT_RECONNECT_TIMEOUT_SECONDS = 300\nconst ENVIRONMENT_KEY_PATTERN = /^[A-Za-z_]\\w*$/v\nconst FIRST_RUN_ENV_NAME = \"PARATIX_FIRST_RUN\"\n\n/**\n * Type guard that checks whether `value` has the shape of a\n * {@link ServerDefinition} — an object with a non-empty string `name`,\n * a non-empty string `host`, a valid `ssh` config, and a non-empty `run` array.\n *\n * @param value - The value to inspect.\n * @returns `true` when `value` satisfies the structural requirements of `ServerDefinition`.\n */\nexport function isServerDefinitionLike(value: unknown): value is ServerDefinition {\n return collectDefinitionErrors(value).length === 0\n}\n\n/** Descriptor for a property validation check. */\ntype PropertyCheck = {\n /** The key to look up in the object. */\n key: string\n /** The label to use in error messages (defaults to `key`). */\n label?: string\n}\n\n/**\n * Validates that a required string property exists, has the correct type, and\n * is not empty. Pushes a human-readable error into `errors` when any check\n * fails.\n *\n * @param object - The object to inspect.\n * @param check - Property key and optional display label.\n * @param errors - Accumulator for error messages.\n */\nfunction collectStringErrors(\n object: Record<string, unknown>,\n check: PropertyCheck,\n errors: string[]\n): void {\n const name = check.label ?? check.key\n if (!(check.key in object)) {\n errors.push(`Missing property '${name}' (expected string)`)\n } else if (typeof object[check.key] !== \"string\") {\n errors.push(`Invalid property '${name}' (expected string, got ${typeof object[check.key]})`)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by typeof check above\n } else if ((object[check.key] as string).length === 0) {\n errors.push(`Property '${name}' must not be empty`)\n }\n}\n\n/**\n * Validates that a required array property exists, has the correct type, and\n * is not empty. Pushes a human-readable error into `errors` when any check\n * fails.\n *\n * @param object - The object to inspect.\n * @param check - Property key and optional display label.\n * @param errors - Accumulator for error messages.\n */\nfunction collectArrayErrors(\n object: Record<string, unknown>,\n check: PropertyCheck,\n errors: string[]\n): void {\n const name = check.label ?? check.key\n if (!(check.key in object)) {\n errors.push(`Missing property '${name}' (expected array)`)\n } else if (!Array.isArray(object[check.key])) {\n errors.push(`Invalid property '${name}' (expected array, got ${typeof object[check.key]})`)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by Array.isArray check above\n } else if ((object[check.key] as unknown[]).length === 0) {\n errors.push(`Property '${name}' must not be empty`)\n }\n}\n\nfunction collectSshErrors(value: Record<string, unknown>, errors: string[]): void {\n if (!(\"ssh\" in value)) {\n errors.push(\"Missing property 'ssh' (expected object)\")\n return\n }\n errors.push(...collectSshConfigErrors(value.ssh))\n}\n\n/**\n * Collects human-readable error messages for every property of `value` that\n * does not conform to the `ServerDefinition` shape.\n *\n * @param value - The value to validate.\n * @returns An array of error strings, empty when `value` is structurally valid.\n */\nexport function collectDefinitionErrors(value: unknown): string[] {\n const errors: string[] = []\n if (typeof value !== \"object\" || value === null) {\n errors.push(\"Export is not an object\")\n return errors\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by typeof/null checks above\n const object = value as Record<string, unknown>\n collectStringErrors(object, { key: \"name\" }, errors)\n collectStringErrors(object, { key: \"host\" }, errors)\n collectSshErrors(object, errors)\n collectArrayErrors(object, { key: \"run\" }, errors)\n return errors\n}\n\n/**\n * Assertion function that ensures `value` is a valid {@link ServerDefinition}.\n *\n * When validation fails, all collected errors are printed to stderr and the\n * process exits with code `2`, so callers can treat the function as a\n * narrowing assertion without additional error handling.\n *\n * @param value - The value to validate.\n * @param file - Path of the file that exported `value`, used in the error message.\n */\nfunction validateServerDefinition(value: unknown, file: string): asserts value is ServerDefinition {\n if (isServerDefinitionLike(value)) {\n return\n }\n const errors = collectDefinitionErrors(value)\n const details = errors.map((entry) => ` - ${entry}`).join(\"\\n\")\n console.error(\n `Error: ${file} does not export a valid ServerDefinition.\\n${details}\\n Use the server() helper to create a valid definition.`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n}\n\n/**\n * Returns a human-readable string for any caught value.\n * Uses `.message` for `Error` instances and falls back to the string\n * representation for primitives. For plain objects that have no meaningful\n * `toString`, the JSON representation is used instead of `[object Object]`.\n *\n * @param value - The value to convert to a string.\n * @returns A human-readable string representation of `value`.\n */\nfunction errorToString(value: unknown): string {\n if (value instanceof Error) return value.message\n if (typeof value === \"object\" && value !== null) {\n try {\n return JSON.stringify(value)\n } catch {\n return inspect(value, { breakLength: Infinity, depth: 5 })\n }\n }\n return String(value)\n}\n\n/**\n * Walks the cause chain of `error` and prints each cause to stderr.\n *\n * @param error - The root `Error` whose `.cause` chain should be printed.\n */\nfunction printCauseChain(error: Error): void {\n let cause = error.cause\n while (cause != null) {\n console.error(` Caused by: ${errorToString(cause)}`)\n cause = cause instanceof Error ? cause.cause : undefined\n }\n}\n\n/**\n * Prints a structured error message to stderr, including the cause chain and\n * optionally the full stack trace when `verbose` is `true`.\n *\n * @param error - The caught value (may be any type).\n * @param verbose - When `true`, the stack trace of `error` is printed.\n */\nexport function printExceptionError(error: unknown, verbose: boolean): void {\n console.error(`Error: ${errorToString(error)}`)\n\n if (error instanceof Error) {\n printCauseChain(error)\n\n if (verbose && error.stack != null) {\n console.error(`\\n${error.stack}`)\n }\n }\n}\n\n/**\n * Handles a failed attempt to load the `tsx` runtime.\n *\n * When the failed import is for a TypeScript file (`.ts`, `.mts`, `.cts`), a\n * clear error message is printed to stderr and the process exits with code 2.\n * For JavaScript files the failure is silently ignored because `tsx` is not\n * required there.\n *\n * @param filePath - The resolved path of the playbook file being loaded.\n */\nexport function handleTsxLoadFailure(filePath: string): void {\n if (/\\.[cm]?ts$/v.test(filePath)) {\n console.error(\n `${pc.red(\"Error:\")} tsx is required to run TypeScript playbooks but could not be loaded.\\n` +\n ` Install it with: ${pc.bold(\"npm install -g tsx\")} or add it as a devDependency.`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n}\n\n/**\n * Returns whether the current CLI module is the direct process entrypoint.\n *\n * This resolves symlinks on both sides so pnpm-style executable shims and\n * symlinked `node_modules` entries still count as direct execution.\n *\n * @param moduleUrl - The current module URL, usually `import.meta.url`.\n * @param candidateEntryScript - The process entry script path, usually `process.argv[1]`.\n * @returns `true` when both paths resolve to the same file on disk.\n */\nexport function isDirectCliExecution(moduleUrl: string, candidateEntryScript?: string): boolean {\n if (candidateEntryScript == null) {\n return false\n }\n\n return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(candidateEntryScript)\n}\n\nexport function applyCliEnvironmentOverrides(\n environment: Environment,\n options: { firstRun: boolean }\n): Environment {\n if (!options.firstRun) return environment\n return { ...environment, [FIRST_RUN_ENV_NAME]: \"true\" }\n}\n\nexport function applyCliProcessEnvironment(options: { firstRun: boolean }): void {\n if (!options.firstRun) return\n process.env[FIRST_RUN_ENV_NAME] = \"true\"\n}\n\nexport async function loadServerDefinitionFromFile(\n file: string,\n options: { firstRun: boolean }\n): Promise<ServerDefinition> {\n const filePath = resolve(file)\n const fileUrl = pathToFileURL(filePath).href\n\n applyCliProcessEnvironment(options)\n\n // Register tsx for TypeScript imports\n await import(\"tsx/esm/api\")\n .then((tsx: { register: () => void }) => {\n tsx.register()\n })\n .catch(() => {\n handleTsxLoadFailure(filePath)\n })\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Dynamic import has unknown shape\n const imported = await import(fileUrl)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-type-assertion -- Accessing .default on dynamic import\n const definition = (imported.default ?? imported) as ServerDefinition\n\n validateServerDefinition(definition, filePath)\n return definition\n}\n\nconst program = new Command()\n\nprogram\n .name(\"paratix\")\n .description(\"Idempotent VPS setup tool in TypeScript\")\n .version(PACKAGE_DISPLAY_VERSION)\n\nprogram\n .command(\"apply <file>\")\n .description(\"Apply a server definition\")\n .option(\n \"--dry-run\",\n \"Only check, do not apply. Some modules validate prospective config but cannot verify runtime restarts.\",\n false\n )\n .option(\"--env <key=value...>\", \"Set env values\", collectEnvironment, {})\n .option(\"--env-file <path>\", \"Load dotenv file\")\n .option(\"--first-run\", \"Set PARATIX_FIRST_RUN=true before loading the playbook\", false)\n .option(\n \"--reconnect-timeout <seconds>\",\n \"SSH reconnect timeout\",\n parsePositiveNumber,\n DEFAULT_RECONNECT_TIMEOUT_SECONDS\n )\n .option(\"--verbose\", \"Show full stack traces on error\", false)\n .action(async (file: string, options: Record<string, unknown>) => {\n try {\n printCliHeader(PACKAGE_DISPLAY_VERSION)\n const environmentOverrides = applyCliEnvironmentOverrides(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n options.env as Environment,\n {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n firstRun: options.firstRun as boolean,\n }\n )\n const definition = await loadServerDefinitionFromFile(file, {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n firstRun: options.firstRun as boolean,\n })\n\n await runPlaybook(definition, {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n dryRun: options.dryRun as boolean,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n envFile: options.envFile as string | undefined,\n envOverrides: environmentOverrides,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n reconnectTimeout: (options.reconnectTimeout as number) * SECONDS_TO_MS,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n verbose: options.verbose as boolean,\n })\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n printExceptionError(error, options.verbose as boolean)\n // eslint-disable-next-line node/no-process-exit\n process.exit(process.exitCode ?? 2)\n }\n })\n\nexport function parsePositiveNumber(value: string): number {\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) {\n console.error(`Invalid --reconnect-timeout value: ${value} (expected a positive number)`)\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n return parsed\n}\n\nexport function collectEnvironment(\n value: string,\n previous: Record<string, string>\n): Record<string, string> {\n const eqIndex = value.indexOf(\"=\")\n if (eqIndex === -1) {\n console.error(`Invalid --env format: ${value} (expected key=value)`)\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n const key = value.slice(0, eqIndex)\n const value_ = value.slice(eqIndex + 1)\n if (!ENVIRONMENT_KEY_PATTERN.test(key)) {\n console.error(\n `Invalid --env name: ${key === \"\" ? \"(empty)\" : key} (expected [A-Za-z_][A-Za-z0-9_]*)`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n return { ...previous, [key]: value_ }\n}\n\n// Only parse when executed directly, not when imported (e.g. in tests)\nconst entryScript = process.argv[1]\nif (isDirectCliExecution(import.meta.url, entryScript)) {\n await program.parseAsync()\n}\n","import type { RecipeModule } from \"./recipe.js\"\nimport type { SshConnectionImpl } from \"./ssh.js\"\nimport type { Environment, ModuleStatus } from \"./types.js\"\n\nimport { mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\n\ntype StepResult = { env: Environment; shouldBreak: boolean; status?: ModuleStatus; stopRun?: true }\n\nfunction shouldExecuteApplyDuringDryRun(module: RecipeModule[\"_modules\"][number]): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nasync function executeDryRunBlockingModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n connection: null | SshConnectionImpl\n environment: Environment\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, connection, environment, verbose } = parameters\n startModuleSpinner(childModule.name)\n const result =\n childModule._applyDryRun == null\n ? await childModule.apply(connection, environment)\n : await childModule._applyDryRun(connection, environment)\n const nextEnvironment =\n result.meta == null ? environment : await mergeEnvironmentFromMeta(environment, result.meta)\n printModuleResult(childModule.name, result.status, result._dryRunDetail ?? \"(dry-run)\")\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n return {\n env: nextEnvironment,\n shouldBreak: result.status === \"failed\" || result._stopRun === true,\n status: result.status,\n stopRun: result._stopRun,\n }\n}\n\nasync function executeDryRunChildModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n environment: Environment\n ssh: SshConnectionImpl\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, environment, ssh, verbose } = parameters\n const connection = childModule.local === true ? null : ssh\n startModuleSpinner(childModule.name)\n const checkResult = await childModule.check(connection, environment)\n if (checkResult !== \"ok\" && shouldExecuteApplyDuringDryRun(childModule)) {\n return executeDryRunBlockingModule({ childModule, connection, environment, verbose })\n }\n const status = checkResult === \"ok\" ? \"ok\" : \"changed\"\n const suffix = checkResult === \"ok\" ? undefined : \"(dry-run)\"\n printModuleResult(childModule.name, status, suffix)\n return { env: environment, shouldBreak: false, status }\n}\n\nexport async function dryRunRecipeModule(parameters: {\n environment: Environment\n options?: {\n verbose?: boolean\n }\n recipeModule: RecipeModule\n ssh: SshConnectionImpl\n}): Promise<StepResult> {\n return withRecipeOutputScope(async () => {\n const { environment, recipeModule, ssh } = parameters\n printRecipeHeader(recipeModule.name)\n let aggregatedStatus: \"changed\" | \"ok\" = \"ok\"\n let currentEnvironment = environment\n const verbose = parameters.options?.verbose ?? false\n\n for (const childModule of recipeModule._modules) {\n // eslint-disable-next-line no-await-in-loop\n const result = await executeDryRunChildModule({\n childModule,\n environment: currentEnvironment,\n ssh,\n verbose,\n })\n if (result.shouldBreak) return result\n currentEnvironment = result.env\n if (result.status === \"changed\") aggregatedStatus = \"changed\"\n }\n\n return { env: currentEnvironment, shouldBreak: false, status: aggregatedStatus }\n })\n}\n","const SIGNAL_EXIT_BASE = 128\nconst SIGTERM_NUMBER = 15\nconst SIGINT_NUMBER = 2\n\n/**\n * Returns the conventional exit code for a termination signal.\n * Follows the POSIX convention of 128 + signal number.\n *\n * @param signal - The received signal (`SIGTERM` or `SIGINT`).\n * @returns The exit code to use when the process is terminated by `signal`.\n */\nexport function signalExitCode(signal: NodeJS.Signals): number {\n return SIGNAL_EXIT_BASE + (signal === \"SIGTERM\" ? SIGTERM_NUMBER : SIGINT_NUMBER)\n}\n\n/**\n * Sets `process.exitCode` based on the run outcome.\n * A received shutdown signal takes precedence over module failures.\n *\n * @param shutdownSignal - The signal that interrupted the run, or `null` if the\n * run completed normally.\n * @param stats - Accumulated run statistics used to detect module failures.\n * @param stats.failed - Number of modules that failed.\n */\nexport function resolveExitCode(\n shutdownSignal: NodeJS.Signals | null,\n stats: { failed: number }\n): void {\n if (shutdownSignal != null) {\n process.exitCode = signalExitCode(shutdownSignal)\n } else if (stats.failed > 0) {\n process.exitCode = 1\n }\n}\n","/* eslint-disable max-lines -- central runner orchestration stays intentionally co-located */\nimport type { RecipeModule } from \"./recipe.js\"\nimport type {\n Environment,\n Module,\n ModuleResult,\n ModuleStatus,\n OrchestrationStep,\n ServerDefinition,\n} from \"./types.js\"\n\nimport { dryRunRecipeModule } from \"./dryRunRecipe.js\"\nimport { loadDotEnvironment, mergeEnvironment } from \"./environment.js\"\nimport {\n assertValidModuleMetaEntries,\n isSshdPortMetaEntry,\n isSystemHostMetaEntry,\n isSystemRebootMetaEntry,\n mergeEnvironmentFromMeta,\n} from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n printRunContext,\n printSummary,\n startModuleSpinner,\n} from \"./output.js\"\nimport { resolveExitCode, signalExitCode } from \"./runnerHelpers.js\"\nimport { runSignalModules, type SignalRunStatus } from \"./signalOrchestration.js\"\nimport { SshConnectionImpl } from \"./ssh.js\"\n\n/** Holds the shutdown listener, SSH setter, and getter for the first received signal. */\ntype ShutdownState = {\n handleShutdownSignal: (signal: NodeJS.Signals) => void\n promptAbortSignal: AbortSignal\n setSsh: (connection: SshConnectionImpl) => void\n shutdownSignal: () => NodeJS.Signals | null\n}\n\n/**\n * Registers shutdown handlers.\n * @returns The listener state and first-signal getter.\n */\nfunction setupShutdownHandlers(): ShutdownState {\n let receivedSignal: NodeJS.Signals | null = null\n let ssh: null | SshConnectionImpl = null\n const promptAbortController = new AbortController()\n\n const handleShutdownSignal = (signal: NodeJS.Signals): void => {\n if (receivedSignal != null) {\n // eslint-disable-next-line node/no-process-exit\n process.exit(signalExitCode(signal))\n }\n receivedSignal = signal\n promptAbortController.abort(new Error(`Terminal prompt interrupted by ${signal}`))\n console.error(`\\nReceived ${signal}, shutting down…`)\n ssh?.disconnect()\n }\n\n process.on(\"SIGINT\", handleShutdownSignal)\n process.on(\"SIGTERM\", handleShutdownSignal)\n\n return {\n handleShutdownSignal,\n promptAbortSignal: promptAbortController.signal,\n setSsh: (connection: SshConnectionImpl) => {\n ssh = connection\n },\n shutdownSignal: () => receivedSignal,\n }\n}\n\nexport type RunOptions = {\n /** When `true`, modules report what would change without applying anything. Defaults to `false`. */\n dryRun?: boolean\n /** Path to a `.env` file whose variables are merged into the run environment. */\n envFile?: string\n /** Additional environment variables that override values from `envFile` and the server definition. */\n envOverrides?: Environment\n /** Custom reconnect timeout in milliseconds passed to SSH, overriding the config default. */\n reconnectTimeout?: number\n /** When `true`, failed commands print full stdout/stderr in addition to the summary error. */\n verbose?: boolean\n}\n\nclass RunStats {\n public changed = 0\n public failed = 0\n public ok = 0\n public signals = 0\n public skipped = 0\n\n public incrementSignals(): void {\n this.signals++\n }\n\n public update(status: ModuleStatus): void {\n switch (status) {\n case \"changed\": {\n this.changed++\n break\n }\n case \"failed\": {\n this.failed++\n break\n }\n case \"ok\": {\n this.ok++\n break\n }\n case \"skipped\": {\n this.skipped++\n break\n }\n }\n }\n}\n\ntype StepResult = {\n env: Environment\n flushSignals?: true\n shouldBreak: boolean\n status?: ModuleStatus\n stopRun?: true\n}\n\nfunction interruptedStepResult(environment: Environment): StepResult {\n return { env: environment, shouldBreak: true }\n}\n\nfunction shouldBreakAfterResult(result: Pick<ModuleResult, \"_stopRun\" | \"status\">): boolean {\n return result.status === \"failed\" || result._stopRun === true\n}\n\nfunction interruptedBeforeApply(\n environment: Environment,\n shutdownSignal: () => NodeJS.Signals | null\n): StepResult | undefined {\n if (shutdownSignal() == null) return undefined\n return interruptedStepResult(environment)\n}\n\nasync function applyCheckedModule(parameters: {\n currentEnvironment: Environment\n dryRun?: boolean\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}): Promise<StepResult> {\n const interrupted = interruptedBeforeApply(\n parameters.currentEnvironment,\n parameters.shutdownSignal\n )\n if (interrupted != null) return interrupted\n\n return applyModule({\n currentEnvironment: parameters.currentEnvironment,\n dryRun: parameters.dryRun,\n ssh: parameters.ssh,\n targetModule: parameters.targetModule,\n verbose: parameters.verbose,\n })\n}\n\nfunction shouldExecuteApplyDuringDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction handleCaughtStepError(parameters: {\n environment: Environment\n error: unknown\n moduleName: string\n shutdownSignal: () => NodeJS.Signals | null\n verbose: boolean\n}): StepResult {\n if (parameters.shutdownSignal() != null) {\n return interruptedStepResult(parameters.environment)\n }\n printModuleResult(parameters.moduleName, \"failed\")\n printCommandFailure(parameters.error, parameters.verbose)\n return { env: parameters.environment, shouldBreak: true, status: \"failed\" }\n}\n\nfunction isRecipe(target: Module): target is RecipeModule {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- RecipeModule uses _isRecipe as discriminator\n return \"_isRecipe\" in target && (target as RecipeModule)._isRecipe\n}\n\nasync function initializeEnvironment(\n options: RunOptions,\n definition: ServerDefinition\n): Promise<Environment> {\n const dotEnvironment =\n options.envFile == null ? undefined : await loadDotEnvironment(options.envFile)\n return mergeEnvironment({}, dotEnvironment, definition.env, options.envOverrides)\n}\n\nasync function handlePortChange(\n ssh: SshConnectionImpl,\n metaEntries: ModuleResult[\"meta\"]\n): Promise<void> {\n const portEntry = metaEntries?.find((entry) => isSshdPortMetaEntry(entry))\n if (portEntry == null) return\n\n const newPort = portEntry.port\n ssh.addPort(newPort)\n\n // Skip reconnect when a reboot is pending — the reboot handler will\n // reconnect on all registered ports (including the newly added one).\n if (metaEntries?.some((entry) => isSystemRebootMetaEntry(entry)) ?? false) return\n\n try {\n await ssh.reconnect()\n } catch (error) {\n console.error(\n `Failed to reconnect on port ${newPort} after port change: ${String(error)}. ` +\n `Verify that port ${newPort} is allowed by the server's firewall rules.`\n )\n throw error\n }\n}\n\nasync function handleReboot(\n ssh: SshConnectionImpl,\n metaEntries: ModuleResult[\"meta\"]\n): Promise<void> {\n if (!(metaEntries?.some((entry) => isSystemRebootMetaEntry(entry)) ?? false)) return\n\n const hostEntry = metaEntries?.find((entry) => isSystemHostMetaEntry(entry))\n if (hostEntry != null) {\n ssh.updateHost(hostEntry.host)\n }\n\n try {\n await ssh.reconnect()\n } catch (error) {\n console.error(`Failed to reconnect after reboot: ${String(error)}`)\n throw error\n }\n}\n\nasync function applyRunnerControlPlaneMeta(\n ssh: SshConnectionImpl,\n step: Pick<OrchestrationStep, \"meta\">\n): Promise<void> {\n if (step.meta == null) return\n assertValidModuleMetaEntries(step.meta)\n await handlePortChange(ssh, step.meta)\n await handleReboot(ssh, step.meta)\n}\n\nasync function handleMetaAndBuildResult(\n ssh: SshConnectionImpl,\n environment: Environment,\n result: ModuleResult\n): Promise<StepResult> {\n let currentEnvironment = environment\n\n if (result.meta != null) {\n currentEnvironment = await mergeEnvironmentFromMeta(currentEnvironment, result.meta)\n await applyRunnerControlPlaneMeta(ssh, { meta: result.meta })\n }\n\n return {\n env: currentEnvironment,\n flushSignals: result._flushSignals,\n shouldBreak: shouldBreakAfterResult(result),\n status: result.status,\n stopRun: result._stopRun,\n }\n}\n\n// eslint-disable-next-line max-params -- verbose and dryRun flags need to be threaded through\nasync function runDryRunRecipeModule(\n recipeModule: RecipeModule,\n environment: Environment,\n ssh: SshConnectionImpl,\n verbose: boolean\n): Promise<StepResult> {\n return dryRunRecipeModule({\n environment,\n options: { verbose },\n recipeModule,\n ssh,\n })\n}\n\n// eslint-disable-next-line max-params -- verbose and dryRun flags need to be threaded through\nasync function runRecipeModule(\n recipeModule: RecipeModule,\n environment: Environment,\n ssh: SshConnectionImpl,\n stats: RunStats,\n verbose: boolean,\n dryRun: boolean,\n shutdownSignal: () => NodeJS.Signals | null\n): Promise<StepResult> {\n try {\n if (dryRun) return await runDryRunRecipeModule(recipeModule, environment, ssh, verbose)\n\n // check() iterates all child modules; apply() checks them again internally via executeModules().\n startModuleSpinner(recipeModule.name)\n const checkResult = await recipeModule.check(ssh, environment)\n if (checkResult === \"ok\") {\n printModuleResult(recipeModule.name, \"ok\")\n return { env: environment, shouldBreak: false, status: \"ok\" }\n }\n\n const result = await recipeModule.apply(ssh, environment, {\n onChildStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n onSignalStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n shutdownSignal,\n signalHooks: {\n onSignalFinished: (status: ModuleStatus) => {\n stats.update(status)\n },\n onSignalStarted: () => {\n stats.incrementSignals()\n },\n },\n verbose,\n })\n return await handleMetaAndBuildResult(ssh, environment, result)\n } catch (error) {\n return handleCaughtStepError({\n environment,\n error,\n moduleName: recipeModule.name,\n shutdownSignal,\n verbose,\n })\n }\n}\n\nasync function applyModule(parameters: {\n currentEnvironment: Environment\n dryRun?: boolean\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}): Promise<StepResult> {\n const { currentEnvironment, dryRun = false, ssh, targetModule, verbose } = parameters\n const connection = targetModule.local === true ? null : ssh\n const result =\n dryRun && targetModule._applyDryRun != null\n ? await targetModule._applyDryRun(connection, currentEnvironment)\n : await targetModule.apply(connection, currentEnvironment)\n const stepResult = await handleMetaAndBuildResult(ssh, currentEnvironment, result)\n const detail = dryRun ? (result._dryRunDetail ?? \"(dry-run)\") : result.detail\n printModuleResult(targetModule.name, result.status, detail)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n return stepResult\n}\n\nasync function checkRegularModule(parameters: {\n env: Environment\n ssh: SshConnectionImpl\n targetModule: Module\n}): Promise<\"needs-apply\" | \"ok\"> {\n const { env, ssh, targetModule } = parameters\n const connection = targetModule.local === true ? null : ssh\n startModuleSpinner(targetModule.name)\n return targetModule.check(connection, env)\n}\n\nfunction buildDryRunChangedResult(environment: Environment): StepResult {\n return { env: environment, shouldBreak: false, status: \"changed\" }\n}\n\ntype RegularModuleArguments = {\n dryRun: boolean\n env: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}\n\nasync function runRegularModule(parameters: RegularModuleArguments): Promise<StepResult> {\n const { dryRun, env, ssh, targetModule, verbose } = parameters\n const shutdownSignal = parameters.shutdownSignal\n\n try {\n const checkResult = await checkRegularModule({ env, ssh, targetModule })\n\n if (checkResult === \"ok\") {\n printModuleResult(targetModule.name, \"ok\")\n return { env, shouldBreak: false, status: \"ok\" }\n }\n\n if (dryRun) {\n if (shouldExecuteApplyDuringDryRun(targetModule)) {\n return await applyCheckedModule({\n currentEnvironment: env,\n dryRun: true,\n shutdownSignal,\n ssh,\n targetModule,\n verbose,\n })\n }\n printModuleResult(targetModule.name, \"changed\", \"(dry-run)\")\n return buildDryRunChangedResult(env)\n }\n\n return await applyCheckedModule({\n currentEnvironment: env,\n dryRun: false,\n shutdownSignal,\n ssh,\n targetModule,\n verbose,\n })\n } catch (error) {\n return handleCaughtStepError({\n environment: env,\n error,\n moduleName: targetModule.name,\n shutdownSignal,\n verbose,\n })\n }\n}\n\ntype LoopArguments = {\n definitionSignals?: Module[]\n dryRun: boolean\n env: Environment\n modules: Module[]\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\ntype ModuleLoopState = {\n currentEnvironment: Environment\n signalsPending: boolean\n stopRun?: true\n}\n\nfunction updateLoopSignalState(input: {\n currentSignalsPending: boolean\n result: StepResult\n stats: RunStats\n}): boolean {\n if (input.result.status == null) return input.currentSignalsPending\n input.stats.update(input.result.status)\n return input.result.status === \"changed\" ? true : input.currentSignalsPending\n}\n\nfunction shouldFlushTopLevelSignals(input: {\n definitionSignals?: Module[]\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalsPending: boolean\n stats: RunStats\n stepResult: StepResult\n}): input is {\n definitionSignals: Module[]\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalsPending: boolean\n stats: RunStats\n stepResult: { flushSignals: true } & StepResult\n} {\n return (\n input.stepResult.flushSignals === true &&\n !input.dryRun &&\n input.shutdownSignal() == null &&\n input.signalsPending &&\n input.stats.failed === 0 &&\n input.definitionSignals != null\n )\n}\n\nasync function flushPendingTopLevelSignals(input: {\n currentEnvironment: Environment\n definitionSignals: Module[]\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<SignalRunStatus> {\n return runSignals({\n env: input.currentEnvironment,\n shutdownSignal: input.shutdownSignal,\n signals: input.definitionSignals,\n ssh: input.ssh,\n stats: input.stats,\n verbose: input.verbose,\n })\n}\n\nfunction applyLoopResultToState(\n state: ModuleLoopState,\n result: StepResult,\n stats: RunStats\n): ModuleLoopState {\n return {\n currentEnvironment: result.env,\n signalsPending: updateLoopSignalState({\n currentSignalsPending: state.signalsPending,\n result,\n stats,\n }),\n stopRun: result.stopRun === true ? true : state.stopRun,\n }\n}\n\nasync function flushTopLevelSignalsIfRequested(parameters: {\n definitionSignals?: Module[]\n dryRun: boolean\n loopState: ModuleLoopState\n result: StepResult\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<{ nextSignalsPending: boolean; outcome: \"break\" | \"continue\" }> {\n if (\n !shouldFlushTopLevelSignals({\n definitionSignals: parameters.definitionSignals,\n dryRun: parameters.dryRun,\n shutdownSignal: parameters.shutdownSignal,\n signalsPending: parameters.loopState.signalsPending,\n stats: parameters.stats,\n stepResult: parameters.result,\n })\n ) {\n return { nextSignalsPending: parameters.loopState.signalsPending, outcome: \"continue\" }\n }\n const definitionSignals = parameters.definitionSignals\n if (definitionSignals == null) {\n return { nextSignalsPending: parameters.loopState.signalsPending, outcome: \"continue\" }\n }\n\n const signalStatus = await flushPendingTopLevelSignals({\n currentEnvironment: parameters.loopState.currentEnvironment,\n definitionSignals,\n shutdownSignal: parameters.shutdownSignal,\n ssh: parameters.ssh,\n stats: parameters.stats,\n verbose: parameters.verbose,\n })\n return {\n nextSignalsPending: false,\n outcome: signalStatus === \"failed\" ? \"break\" : \"continue\",\n }\n}\n\nasync function createModuleStepPromise(parameters: {\n currentEnvironment: Environment\n currentModule: Module\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<StepResult> {\n const { currentEnvironment, currentModule, dryRun, shutdownSignal, ssh, stats, verbose } =\n parameters\n\n return isRecipe(currentModule)\n ? runRecipeModule(\n currentModule,\n currentEnvironment,\n ssh,\n stats,\n verbose,\n dryRun,\n shutdownSignal\n )\n : runRegularModule({\n dryRun,\n env: currentEnvironment,\n shutdownSignal,\n ssh,\n targetModule: currentModule,\n verbose,\n })\n}\n\nasync function runModuleLoop(parameters: LoopArguments): Promise<{\n env: Environment\n signalsPending: boolean\n stopRun?: true\n}> {\n const { definitionSignals, dryRun, modules, shutdownSignal, ssh, stats, verbose } = parameters\n const loopState: ModuleLoopState = {\n currentEnvironment: parameters.env,\n signalsPending: false,\n stopRun: undefined,\n }\n\n for (const currentModule of modules) {\n // A module already running when the signal arrived completes normally\n // and its result is still counted in stats before the loop exits here.\n if (shutdownSignal() != null) break\n const stepPromise = createModuleStepPromise({\n currentEnvironment: loopState.currentEnvironment,\n currentModule,\n dryRun,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n\n // eslint-disable-next-line no-await-in-loop\n const result = await stepPromise\n\n Object.assign(loopState, applyLoopResultToState(loopState, result, stats))\n // eslint-disable-next-line no-await-in-loop\n const flushResult = await flushTopLevelSignalsIfRequested({\n definitionSignals,\n dryRun,\n loopState,\n result,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n loopState.signalsPending = flushResult.nextSignalsPending\n if (flushResult.outcome === \"break\") break\n if (result.shouldBreak) break\n }\n\n return {\n env: loopState.currentEnvironment,\n signalsPending: loopState.signalsPending,\n stopRun: loopState.stopRun,\n }\n}\n\ntype SignalArguments = {\n env: Environment\n shutdownSignal: () => NodeJS.Signals | null\n signals: Module[]\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\nasync function runSignals(parameters: SignalArguments): Promise<SignalRunStatus> {\n const { env, shutdownSignal, signals, ssh, stats, verbose } = parameters\n return runSignalModules({\n environment: env,\n hooks: {\n onSignalFinished: (status: ModuleStatus) => {\n stats.update(status)\n },\n onSignalStarted: () => {\n stats.incrementSignals()\n },\n },\n onSignalStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n shutdownSignal,\n signals,\n ssh,\n verbose,\n })\n}\n\nfunction throwIfShutdownRequested(shutdownSignal: () => NodeJS.Signals | null): void {\n const signal = shutdownSignal()\n if (signal == null) return\n throw new Error(`Bootstrap interrupted by ${signal}`)\n}\n\nasync function connectAndRegister(parameters: {\n definition: ServerDefinition\n options: RunOptions\n promptAbortSignal: AbortSignal\n setSsh: (c: SshConnectionImpl) => void\n shutdownSignal: () => NodeJS.Signals | null\n}): Promise<SshConnectionImpl> {\n const { definition, options, promptAbortSignal, setSsh, shutdownSignal } = parameters\n const sshConfig = {\n ...definition.ssh,\n ports: [...definition.ssh.ports],\n ...(options.reconnectTimeout == null ? {} : { reconnectTimeout: options.reconnectTimeout }),\n }\n const ssh = new SshConnectionImpl(definition.host, sshConfig)\n setSsh(ssh)\n throwIfShutdownRequested(shutdownSignal)\n await ssh.connect({ abortSignal: promptAbortSignal })\n throwIfShutdownRequested(shutdownSignal)\n return ssh\n}\n\ntype ExecuteRunArguments = {\n definition: ServerDefinition\n dryRun: boolean\n environment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\nasync function executeRun(parameters: ExecuteRunArguments): Promise<void> {\n const { definition, dryRun, environment, shutdownSignal, ssh, stats, verbose } = parameters\n\n printRecipeHeader(definition.name)\n const loopResult = await runModuleLoop({\n definitionSignals: definition.signals,\n dryRun,\n env: environment,\n modules: definition.run,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n const finalEnvironment = loopResult.env\n\n if (\n !dryRun &&\n shutdownSignal() == null &&\n loopResult.signalsPending &&\n stats.failed === 0 &&\n definition.signals != null\n )\n await runSignals({\n env: finalEnvironment,\n shutdownSignal,\n signals: definition.signals,\n ssh,\n stats,\n verbose,\n })\n\n printSummary(stats)\n}\n\nfunction rethrowIfNotShutdown(error: unknown, shutdownSignal: () => NodeJS.Signals | null): void {\n if (shutdownSignal() == null) throw error\n}\n\nexport async function runPlaybook(\n definition: ServerDefinition,\n options: RunOptions = {}\n): Promise<void> {\n const { dryRun = false, verbose = false } = options\n const environment = await initializeEnvironment(options, definition)\n const { handleShutdownSignal, promptAbortSignal, setSsh, shutdownSignal } =\n setupShutdownHandlers()\n const stats = new RunStats()\n let ssh: SshConnectionImpl | undefined\n\n printRunContext({\n dryRun,\n host: definition.host,\n name: definition.name,\n ports: definition.ssh.ports,\n })\n\n // No catch block: connect errors propagate to cli.ts, which prints them and exits with code 2.\n try {\n ssh = await connectAndRegister({\n definition,\n options,\n promptAbortSignal,\n setSsh,\n shutdownSignal,\n })\n await executeRun({ definition, dryRun, environment, shutdownSignal, ssh, stats, verbose })\n } catch (error) {\n rethrowIfNotShutdown(error, shutdownSignal)\n } finally {\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const)\n process.removeListener(signal, handleShutdownSignal)\n ssh?.disconnect()\n }\n\n resolveExitCode(shutdownSignal(), stats)\n}\n"],"mappings":"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","names":["shouldExecuteApplyDuringDryRun"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/dryRunRecipe.ts","../src/runnerHelpers.ts","../src/runner.ts"],"sourcesContent":["import { Command } from \"commander\"\nimport { realpathSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { inspect } from \"node:util\"\nimport pc from \"picocolors\"\n\nimport type { Environment, ServerDefinition } from \"./types.js\"\n\nimport { printCliHeader } from \"./output.js\"\nimport { runPlaybook } from \"./runner.js\"\nimport { collectSshConfigErrors } from \"./serverDefinitionValidation.js\"\n\ndeclare const PACKAGE_DISPLAY_VERSION: string\n\nconst SECONDS_TO_MS = 1000\nconst DEFAULT_RECONNECT_TIMEOUT_SECONDS = 300\nconst ENVIRONMENT_KEY_PATTERN = /^[A-Za-z_]\\w*$/v\nconst FIRST_RUN_ENV_NAME = \"PARATIX_FIRST_RUN\"\n\nfunction resolveRealPath(path: string): string {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- path is either import.meta-derived or process argv entrypoint; direct file resolution is the intended check\n return realpathSync(path)\n}\n\n/**\n * Type guard that checks whether `value` has the shape of a\n * {@link ServerDefinition} — an object with a non-empty string `name`,\n * a non-empty string `host`, a valid `ssh` config, and a non-empty `run` array.\n *\n * @param value - The value to inspect.\n * @returns `true` when `value` satisfies the structural requirements of `ServerDefinition`.\n */\nexport function isServerDefinitionLike(value: unknown): value is ServerDefinition {\n return collectDefinitionErrors(value).length === 0\n}\n\n/** Descriptor for a property validation check. */\ntype PropertyCheck = {\n /** The key to look up in the object. */\n key: string\n /** The label to use in error messages (defaults to `key`). */\n label?: string\n}\n\n/**\n * Validates that a required string property exists, has the correct type, and\n * is not empty. Pushes a human-readable error into `errors` when any check\n * fails.\n *\n * @param object - The object to inspect.\n * @param check - Property key and optional display label.\n * @param errors - Accumulator for error messages.\n */\nfunction collectStringErrors(\n object: Record<string, unknown>,\n check: PropertyCheck,\n errors: string[]\n): void {\n const name = check.label ?? check.key\n if (!(check.key in object)) {\n errors.push(`Missing property '${name}' (expected string)`)\n } else if (typeof object[check.key] !== \"string\") {\n errors.push(`Invalid property '${name}' (expected string, got ${typeof object[check.key]})`)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by typeof check above\n } else if ((object[check.key] as string).length === 0) {\n errors.push(`Property '${name}' must not be empty`)\n }\n}\n\n/**\n * Validates that a required array property exists, has the correct type, and\n * is not empty. Pushes a human-readable error into `errors` when any check\n * fails.\n *\n * @param object - The object to inspect.\n * @param check - Property key and optional display label.\n * @param errors - Accumulator for error messages.\n */\nfunction collectArrayErrors(\n object: Record<string, unknown>,\n check: PropertyCheck,\n errors: string[]\n): void {\n const name = check.label ?? check.key\n if (!(check.key in object)) {\n errors.push(`Missing property '${name}' (expected array)`)\n } else if (!Array.isArray(object[check.key])) {\n errors.push(`Invalid property '${name}' (expected array, got ${typeof object[check.key]})`)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by Array.isArray check above\n } else if ((object[check.key] as unknown[]).length === 0) {\n errors.push(`Property '${name}' must not be empty`)\n }\n}\n\nfunction collectSshErrors(value: Record<string, unknown>, errors: string[]): void {\n if (!(\"ssh\" in value)) {\n errors.push(\"Missing property 'ssh' (expected object)\")\n return\n }\n errors.push(...collectSshConfigErrors(value.ssh))\n}\n\n/**\n * Collects human-readable error messages for every property of `value` that\n * does not conform to the `ServerDefinition` shape.\n *\n * @param value - The value to validate.\n * @returns An array of error strings, empty when `value` is structurally valid.\n */\nexport function collectDefinitionErrors(value: unknown): string[] {\n const errors: string[] = []\n if (typeof value !== \"object\" || value === null) {\n errors.push(\"Export is not an object\")\n return errors\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- narrowed by typeof/null checks above\n const object = value as Record<string, unknown>\n collectStringErrors(object, { key: \"name\" }, errors)\n collectStringErrors(object, { key: \"host\" }, errors)\n collectSshErrors(object, errors)\n collectArrayErrors(object, { key: \"run\" }, errors)\n return errors\n}\n\n/**\n * Assertion function that ensures `value` is a valid {@link ServerDefinition}.\n *\n * When validation fails, all collected errors are printed to stderr and the\n * process exits with code `2`, so callers can treat the function as a\n * narrowing assertion without additional error handling.\n *\n * @param value - The value to validate.\n * @param file - Path of the file that exported `value`, used in the error message.\n */\nfunction validateServerDefinition(value: unknown, file: string): asserts value is ServerDefinition {\n if (isServerDefinitionLike(value)) {\n return\n }\n const errors = collectDefinitionErrors(value)\n const details = errors.map((entry) => ` - ${entry}`).join(\"\\n\")\n console.error(\n `Error: ${file} does not export a valid ServerDefinition.\\n${details}\\n Use the server() helper to create a valid definition.`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n}\n\n/**\n * Returns a human-readable string for any caught value.\n * Uses `.message` for `Error` instances and falls back to the string\n * representation for primitives. For plain objects that have no meaningful\n * `toString`, the JSON representation is used instead of `[object Object]`.\n *\n * @param value - The value to convert to a string.\n * @returns A human-readable string representation of `value`.\n */\nfunction errorToString(value: unknown): string {\n if (value instanceof Error) return value.message\n if (typeof value === \"object\" && value !== null) {\n try {\n return JSON.stringify(value)\n } catch {\n return inspect(value, { breakLength: Infinity, depth: 5 })\n }\n }\n return String(value)\n}\n\n/**\n * Walks the cause chain of `error` and prints each cause to stderr.\n *\n * @param error - The root `Error` whose `.cause` chain should be printed.\n */\nfunction printCauseChain(error: Error): void {\n let cause = error.cause\n while (cause != null) {\n console.error(` Caused by: ${errorToString(cause)}`)\n cause = cause instanceof Error ? cause.cause : undefined\n }\n}\n\n/**\n * Prints a structured error message to stderr, including the cause chain and\n * optionally the full stack trace when `verbose` is `true`.\n *\n * @param error - The caught value (may be any type).\n * @param verbose - When `true`, the stack trace of `error` is printed.\n */\nexport function printExceptionError(error: unknown, verbose: boolean): void {\n console.error(`Error: ${errorToString(error)}`)\n\n if (error instanceof Error) {\n printCauseChain(error)\n\n if (verbose && error.stack != null) {\n console.error(`\\n${error.stack}`)\n }\n }\n}\n\n/**\n * Handles a failed attempt to load the `tsx` runtime.\n *\n * When the failed import is for a TypeScript file (`.ts`, `.mts`, `.cts`), a\n * clear error message is printed to stderr and the process exits with code 2.\n * For JavaScript files the failure is silently ignored because `tsx` is not\n * required there.\n *\n * @param filePath - The resolved path of the playbook file being loaded.\n */\nexport function handleTsxLoadFailure(filePath: string): void {\n if (/\\.[cm]?ts$/v.test(filePath)) {\n console.error(\n `${pc.red(\"Error:\")} tsx is required to run TypeScript playbooks but could not be loaded.\\n` +\n ` Install it with: ${pc.bold(\"npm install -g tsx\")} or add it as a devDependency.`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n}\n\n/**\n * Returns whether the current CLI module is the direct process entrypoint.\n *\n * This resolves symlinks on both sides so pnpm-style executable shims and\n * symlinked `node_modules` entries still count as direct execution.\n *\n * @param moduleUrl - The current module URL, usually `import.meta.url`.\n * @param candidateEntryScript - The process entry script path, usually `process.argv[1]`.\n * @returns `true` when both paths resolve to the same file on disk.\n */\nexport function isDirectCliExecution(moduleUrl: string, candidateEntryScript?: string): boolean {\n if (candidateEntryScript == null) {\n return false\n }\n\n return resolveRealPath(fileURLToPath(moduleUrl)) === resolveRealPath(candidateEntryScript)\n}\n\nexport function applyCliEnvironmentOverrides(\n environment: Environment,\n options: { firstRun: boolean }\n): Environment {\n if (!options.firstRun) return environment\n return { ...environment, [FIRST_RUN_ENV_NAME]: \"true\" }\n}\n\nexport function applyCliProcessEnvironment(options: { firstRun: boolean }): void {\n if (!options.firstRun) return\n process.env[FIRST_RUN_ENV_NAME] = \"true\"\n}\n\nexport async function loadServerDefinitionFromFile(\n file: string,\n options: { firstRun: boolean }\n): Promise<ServerDefinition> {\n const filePath = resolve(file)\n const fileUrl = pathToFileURL(filePath).href\n\n applyCliProcessEnvironment(options)\n\n // Register tsx for TypeScript imports\n await import(\"tsx/esm/api\")\n .then((tsx: { register: () => void }) => {\n tsx.register()\n })\n .catch(() => {\n handleTsxLoadFailure(filePath)\n })\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Dynamic import has unknown shape\n const imported = await import(fileUrl)\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-type-assertion -- Accessing .default on dynamic import\n const definition = (imported.default ?? imported) as ServerDefinition\n\n validateServerDefinition(definition, filePath)\n return definition\n}\n\nconst program = new Command()\n\nprogram\n .name(\"paratix\")\n .description(\"Idempotent VPS setup tool in TypeScript\")\n .version(PACKAGE_DISPLAY_VERSION)\n\nprogram\n .command(\"apply <file>\")\n .description(\"Apply a server definition\")\n .option(\n \"--dry-run\",\n \"Only check, do not apply. Some modules validate prospective config but cannot verify runtime restarts.\",\n false\n )\n .option(\"--env <key=value...>\", \"Set env values\", collectEnvironment, {})\n .option(\"--env-file <path>\", \"Load dotenv file\")\n .option(\"--first-run\", \"Set PARATIX_FIRST_RUN=true before loading the playbook\", false)\n .option(\n \"--reconnect-timeout <seconds>\",\n \"SSH reconnect timeout\",\n parsePositiveNumber,\n DEFAULT_RECONNECT_TIMEOUT_SECONDS\n )\n .option(\"--verbose\", \"Show full stack traces on error\", false)\n .action(async (file: string, options: Record<string, unknown>) => {\n try {\n printCliHeader(PACKAGE_DISPLAY_VERSION)\n const environmentOverrides = applyCliEnvironmentOverrides(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n options.env as Environment,\n {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n firstRun: options.firstRun as boolean,\n }\n )\n const definition = await loadServerDefinitionFromFile(file, {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n firstRun: options.firstRun as boolean,\n })\n\n await runPlaybook(definition, {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n dryRun: options.dryRun as boolean,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n envFile: options.envFile as string | undefined,\n envOverrides: environmentOverrides,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n reconnectTimeout: (options.reconnectTimeout as number) * SECONDS_TO_MS,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n verbose: options.verbose as boolean,\n })\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Commander options typed as Record<string, unknown>\n printExceptionError(error, options.verbose as boolean)\n // eslint-disable-next-line node/no-process-exit\n process.exit(process.exitCode ?? 2)\n }\n })\n\nexport function parsePositiveNumber(value: string): number {\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed <= 0) {\n console.error(`Invalid --reconnect-timeout value: ${value} (expected a positive number)`)\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n return parsed\n}\n\nexport function collectEnvironment(\n value: string,\n previous: Record<string, string>\n): Record<string, string> {\n const eqIndex = value.indexOf(\"=\")\n if (eqIndex === -1) {\n console.error(`Invalid --env format: ${value} (expected key=value)`)\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n const key = value.slice(0, eqIndex)\n const value_ = value.slice(eqIndex + 1)\n if (!ENVIRONMENT_KEY_PATTERN.test(key)) {\n console.error(\n `Invalid --env name: ${key === \"\" ? \"(empty)\" : key} (expected [A-Za-z_][A-Za-z0-9_]*)`\n )\n // eslint-disable-next-line node/no-process-exit\n process.exit(2)\n }\n return { ...previous, [key]: value_ }\n}\n\n// Only parse when executed directly, not when imported (e.g. in tests)\nconst entryScript = process.argv[1]\nif (isDirectCliExecution(import.meta.url, entryScript)) {\n await program.parseAsync()\n}\n","import type { RecipeModule } from \"./recipe.js\"\nimport type { SshConnectionImpl } from \"./ssh.js\"\nimport type { Environment, ModuleStatus } from \"./types.js\"\n\nimport { mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\n\ntype StepResult = { env: Environment; shouldBreak: boolean; status?: ModuleStatus; stopRun?: true }\n\nfunction shouldExecuteApplyDuringDryRun(module: RecipeModule[\"_modules\"][number]): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nasync function executeDryRunBlockingModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n connection: null | SshConnectionImpl\n environment: Environment\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, connection, environment, verbose } = parameters\n startModuleSpinner(childModule.name)\n const result =\n childModule._applyDryRun == null\n ? await childModule.apply(connection, environment)\n : await childModule._applyDryRun(connection, environment)\n const nextEnvironment =\n result.meta == null ? environment : await mergeEnvironmentFromMeta(environment, result.meta)\n printModuleResult(childModule.name, result.status, result._dryRunDetail ?? \"(dry-run)\")\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n return {\n env: nextEnvironment,\n shouldBreak: result.status === \"failed\" || result._stopRun === true,\n status: result.status,\n stopRun: result._stopRun,\n }\n}\n\nasync function executeDryRunChildModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n environment: Environment\n ssh: SshConnectionImpl\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, environment, ssh, verbose } = parameters\n const connection = childModule.local === true ? null : ssh\n startModuleSpinner(childModule.name)\n const checkResult = await childModule.check(connection, environment)\n if (checkResult !== \"ok\" && shouldExecuteApplyDuringDryRun(childModule)) {\n return executeDryRunBlockingModule({ childModule, connection, environment, verbose })\n }\n const status = checkResult === \"ok\" ? \"ok\" : \"changed\"\n const suffix = checkResult === \"ok\" ? undefined : \"(dry-run)\"\n printModuleResult(childModule.name, status, suffix)\n return { env: environment, shouldBreak: false, status }\n}\n\nexport async function dryRunRecipeModule(parameters: {\n environment: Environment\n options?: {\n verbose?: boolean\n }\n recipeModule: RecipeModule\n ssh: SshConnectionImpl\n}): Promise<StepResult> {\n return withRecipeOutputScope(async () => {\n const { environment, recipeModule, ssh } = parameters\n printRecipeHeader(recipeModule.name)\n let aggregatedStatus: \"changed\" | \"ok\" = \"ok\"\n let currentEnvironment = environment\n const verbose = parameters.options?.verbose ?? false\n\n for (const childModule of recipeModule._modules) {\n // eslint-disable-next-line no-await-in-loop\n const result = await executeDryRunChildModule({\n childModule,\n environment: currentEnvironment,\n ssh,\n verbose,\n })\n if (result.shouldBreak) return result\n currentEnvironment = result.env\n if (result.status === \"changed\") aggregatedStatus = \"changed\"\n }\n\n return { env: currentEnvironment, shouldBreak: false, status: aggregatedStatus }\n })\n}\n","const SIGNAL_EXIT_BASE = 128\nconst SIGTERM_NUMBER = 15\nconst SIGINT_NUMBER = 2\n\n/**\n * Returns the conventional exit code for a termination signal.\n * Follows the POSIX convention of 128 + signal number.\n *\n * @param signal - The received signal (`SIGTERM` or `SIGINT`).\n * @returns The exit code to use when the process is terminated by `signal`.\n */\nexport function signalExitCode(signal: NodeJS.Signals): number {\n return SIGNAL_EXIT_BASE + (signal === \"SIGTERM\" ? SIGTERM_NUMBER : SIGINT_NUMBER)\n}\n\n/**\n * Sets `process.exitCode` based on the run outcome.\n * A received shutdown signal takes precedence over module failures.\n *\n * @param shutdownSignal - The signal that interrupted the run, or `null` if the\n * run completed normally.\n * @param stats - Accumulated run statistics used to detect module failures.\n * @param stats.failed - Number of modules that failed.\n */\nexport function resolveExitCode(\n shutdownSignal: NodeJS.Signals | null,\n stats: { failed: number }\n): void {\n if (shutdownSignal != null) {\n process.exitCode = signalExitCode(shutdownSignal)\n } else if (stats.failed > 0) {\n process.exitCode = 1\n }\n}\n","/* eslint-disable max-lines -- central runner orchestration stays intentionally co-located */\nimport type { RecipeModule } from \"./recipe.js\"\nimport type {\n Environment,\n Module,\n ModuleResult,\n ModuleStatus,\n OrchestrationStep,\n ServerDefinition,\n} from \"./types.js\"\n\nimport { dryRunRecipeModule } from \"./dryRunRecipe.js\"\nimport { loadDotEnvironment, mergeEnvironment } from \"./environment.js\"\nimport {\n assertValidModuleMetaEntries,\n isSshdPortMetaEntry,\n isSystemHostMetaEntry,\n isSystemRebootMetaEntry,\n mergeEnvironmentFromMeta,\n} from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n printRunContext,\n printSummary,\n startModuleSpinner,\n} from \"./output.js\"\nimport { resolveExitCode, signalExitCode } from \"./runnerHelpers.js\"\nimport { runSignalModules, type SignalRunStatus } from \"./signalOrchestration.js\"\nimport { SshConnectionImpl } from \"./ssh.js\"\n\n/** Holds the shutdown listener, SSH setter, and getter for the first received signal. */\ntype ShutdownState = {\n handleShutdownSignal: (signal: NodeJS.Signals) => void\n promptAbortSignal: AbortSignal\n setSsh: (connection: SshConnectionImpl) => void\n shutdownSignal: () => NodeJS.Signals | null\n}\n\n/**\n * Registers shutdown handlers.\n * @returns The listener state and first-signal getter.\n */\nfunction setupShutdownHandlers(): ShutdownState {\n let receivedSignal: NodeJS.Signals | null = null\n let ssh: null | SshConnectionImpl = null\n const promptAbortController = new AbortController()\n\n const handleShutdownSignal = (signal: NodeJS.Signals): void => {\n if (receivedSignal != null) {\n // eslint-disable-next-line node/no-process-exit\n process.exit(signalExitCode(signal))\n }\n receivedSignal = signal\n promptAbortController.abort(new Error(`Terminal prompt interrupted by ${signal}`))\n console.error(`\\nReceived ${signal}, shutting down…`)\n ssh?.disconnect()\n }\n\n process.on(\"SIGINT\", handleShutdownSignal)\n process.on(\"SIGTERM\", handleShutdownSignal)\n\n return {\n handleShutdownSignal,\n promptAbortSignal: promptAbortController.signal,\n setSsh: (connection: SshConnectionImpl) => {\n ssh = connection\n },\n shutdownSignal: () => receivedSignal,\n }\n}\n\nexport type RunOptions = {\n /** When `true`, modules report what would change without applying anything. Defaults to `false`. */\n dryRun?: boolean\n /** Path to a `.env` file whose variables are merged into the run environment. */\n envFile?: string\n /** Additional environment variables that override values from `envFile` and the server definition. */\n envOverrides?: Environment\n /** Custom reconnect timeout in milliseconds passed to SSH, overriding the config default. */\n reconnectTimeout?: number\n /** When `true`, failed commands print full stdout/stderr in addition to the summary error. */\n verbose?: boolean\n}\n\nclass RunStats {\n public changed = 0\n public failed = 0\n public ok = 0\n public signals = 0\n public skipped = 0\n\n public incrementSignals(): void {\n this.signals++\n }\n\n public update(status: ModuleStatus): void {\n switch (status) {\n case \"changed\": {\n this.changed++\n break\n }\n case \"failed\": {\n this.failed++\n break\n }\n case \"ok\": {\n this.ok++\n break\n }\n case \"skipped\": {\n this.skipped++\n break\n }\n }\n }\n}\n\ntype StepResult = {\n env: Environment\n flushSignals?: true\n shouldBreak: boolean\n status?: ModuleStatus\n stopRun?: true\n}\n\nfunction interruptedStepResult(environment: Environment): StepResult {\n return { env: environment, shouldBreak: true }\n}\n\nfunction shouldBreakAfterResult(result: Pick<ModuleResult, \"_stopRun\" | \"status\">): boolean {\n return result.status === \"failed\" || result._stopRun === true\n}\n\nfunction interruptedBeforeApply(\n environment: Environment,\n shutdownSignal: () => NodeJS.Signals | null\n): StepResult | undefined {\n if (shutdownSignal() == null) return undefined\n return interruptedStepResult(environment)\n}\n\nasync function applyCheckedModule(parameters: {\n currentEnvironment: Environment\n dryRun?: boolean\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}): Promise<StepResult> {\n const interrupted = interruptedBeforeApply(\n parameters.currentEnvironment,\n parameters.shutdownSignal\n )\n if (interrupted != null) return interrupted\n\n return applyModule({\n currentEnvironment: parameters.currentEnvironment,\n dryRun: parameters.dryRun,\n ssh: parameters.ssh,\n targetModule: parameters.targetModule,\n verbose: parameters.verbose,\n })\n}\n\nfunction shouldExecuteApplyDuringDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction handleCaughtStepError(parameters: {\n environment: Environment\n error: unknown\n moduleName: string\n shutdownSignal: () => NodeJS.Signals | null\n verbose: boolean\n}): StepResult {\n if (parameters.shutdownSignal() != null) {\n return interruptedStepResult(parameters.environment)\n }\n printModuleResult(parameters.moduleName, \"failed\")\n printCommandFailure(parameters.error, parameters.verbose)\n return { env: parameters.environment, shouldBreak: true, status: \"failed\" }\n}\n\nfunction isRecipe(target: Module): target is RecipeModule {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- RecipeModule uses _isRecipe as discriminator\n return \"_isRecipe\" in target && (target as RecipeModule)._isRecipe\n}\n\nasync function initializeEnvironment(\n options: RunOptions,\n definition: ServerDefinition\n): Promise<Environment> {\n const dotEnvironment =\n options.envFile == null ? undefined : await loadDotEnvironment(options.envFile)\n return mergeEnvironment({}, dotEnvironment, definition.env, options.envOverrides)\n}\n\nasync function handlePortChange(\n ssh: SshConnectionImpl,\n metaEntries: ModuleResult[\"meta\"]\n): Promise<void> {\n const portEntry = metaEntries?.find((entry) => isSshdPortMetaEntry(entry))\n if (portEntry == null) return\n\n const newPort = portEntry.port\n ssh.addPort(newPort)\n\n // Skip reconnect when a reboot is pending — the reboot handler will\n // reconnect on all registered ports (including the newly added one).\n if (metaEntries?.some((entry) => isSystemRebootMetaEntry(entry)) ?? false) return\n\n try {\n await ssh.reconnect()\n } catch (error) {\n console.error(\n `Failed to reconnect on port ${newPort} after port change: ${String(error)}. ` +\n `Verify that port ${newPort} is allowed by the server's firewall rules.`\n )\n throw error\n }\n}\n\nasync function handleReboot(\n ssh: SshConnectionImpl,\n metaEntries: ModuleResult[\"meta\"]\n): Promise<void> {\n if (!(metaEntries?.some((entry) => isSystemRebootMetaEntry(entry)) ?? false)) return\n\n const hostEntry = metaEntries?.find((entry) => isSystemHostMetaEntry(entry))\n if (hostEntry != null) {\n ssh.updateHost(hostEntry.host)\n }\n\n try {\n await ssh.reconnect()\n } catch (error) {\n console.error(`Failed to reconnect after reboot: ${String(error)}`)\n throw error\n }\n}\n\nasync function applyRunnerControlPlaneMeta(\n ssh: SshConnectionImpl,\n step: Pick<OrchestrationStep, \"meta\">\n): Promise<void> {\n if (step.meta == null) return\n assertValidModuleMetaEntries(step.meta)\n await handlePortChange(ssh, step.meta)\n await handleReboot(ssh, step.meta)\n}\n\nasync function handleMetaAndBuildResult(\n ssh: SshConnectionImpl,\n environment: Environment,\n result: ModuleResult\n): Promise<StepResult> {\n let currentEnvironment = environment\n\n if (result.meta != null) {\n currentEnvironment = await mergeEnvironmentFromMeta(currentEnvironment, result.meta)\n await applyRunnerControlPlaneMeta(ssh, { meta: result.meta })\n }\n\n return {\n env: currentEnvironment,\n flushSignals: result._flushSignals,\n shouldBreak: shouldBreakAfterResult(result),\n status: result.status,\n stopRun: result._stopRun,\n }\n}\n\n// eslint-disable-next-line max-params -- verbose and dryRun flags need to be threaded through\nasync function runDryRunRecipeModule(\n recipeModule: RecipeModule,\n environment: Environment,\n ssh: SshConnectionImpl,\n verbose: boolean\n): Promise<StepResult> {\n return dryRunRecipeModule({\n environment,\n options: { verbose },\n recipeModule,\n ssh,\n })\n}\n\n// eslint-disable-next-line max-params -- verbose and dryRun flags need to be threaded through\nasync function runRecipeModule(\n recipeModule: RecipeModule,\n environment: Environment,\n ssh: SshConnectionImpl,\n stats: RunStats,\n verbose: boolean,\n dryRun: boolean,\n shutdownSignal: () => NodeJS.Signals | null\n): Promise<StepResult> {\n try {\n if (dryRun) return await runDryRunRecipeModule(recipeModule, environment, ssh, verbose)\n\n // check() iterates all child modules; apply() checks them again internally via executeModules().\n startModuleSpinner(recipeModule.name)\n const checkResult = await recipeModule.check(ssh, environment)\n if (checkResult === \"ok\") {\n printModuleResult(recipeModule.name, \"ok\")\n return { env: environment, shouldBreak: false, status: \"ok\" }\n }\n\n const result = await recipeModule.apply(ssh, environment, {\n onChildStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n onSignalStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n shutdownSignal,\n signalHooks: {\n onSignalFinished: (status: ModuleStatus) => {\n stats.update(status)\n },\n onSignalStarted: () => {\n stats.incrementSignals()\n },\n },\n verbose,\n })\n return await handleMetaAndBuildResult(ssh, environment, result)\n } catch (error) {\n return handleCaughtStepError({\n environment,\n error,\n moduleName: recipeModule.name,\n shutdownSignal,\n verbose,\n })\n }\n}\n\nasync function applyModule(parameters: {\n currentEnvironment: Environment\n dryRun?: boolean\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}): Promise<StepResult> {\n const { currentEnvironment, dryRun = false, ssh, targetModule, verbose } = parameters\n const connection = targetModule.local === true ? null : ssh\n const result =\n dryRun && targetModule._applyDryRun != null\n ? await targetModule._applyDryRun(connection, currentEnvironment)\n : await targetModule.apply(connection, currentEnvironment)\n const stepResult = await handleMetaAndBuildResult(ssh, currentEnvironment, result)\n const detail = dryRun ? (result._dryRunDetail ?? \"(dry-run)\") : result.detail\n printModuleResult(targetModule.name, result.status, detail)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n return stepResult\n}\n\nasync function checkRegularModule(parameters: {\n env: Environment\n ssh: SshConnectionImpl\n targetModule: Module\n}): Promise<\"needs-apply\" | \"ok\"> {\n const { env, ssh, targetModule } = parameters\n const connection = targetModule.local === true ? null : ssh\n startModuleSpinner(targetModule.name)\n return targetModule.check(connection, env)\n}\n\nfunction buildDryRunChangedResult(environment: Environment): StepResult {\n return { env: environment, shouldBreak: false, status: \"changed\" }\n}\n\ntype RegularModuleArguments = {\n dryRun: boolean\n env: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n targetModule: Module\n verbose: boolean\n}\n\nasync function runRegularModule(parameters: RegularModuleArguments): Promise<StepResult> {\n const { dryRun, env, ssh, targetModule, verbose } = parameters\n const shutdownSignal = parameters.shutdownSignal\n\n try {\n const checkResult = await checkRegularModule({ env, ssh, targetModule })\n\n if (checkResult === \"ok\") {\n printModuleResult(targetModule.name, \"ok\")\n return { env, shouldBreak: false, status: \"ok\" }\n }\n\n if (dryRun) {\n if (shouldExecuteApplyDuringDryRun(targetModule)) {\n return await applyCheckedModule({\n currentEnvironment: env,\n dryRun: true,\n shutdownSignal,\n ssh,\n targetModule,\n verbose,\n })\n }\n printModuleResult(targetModule.name, \"changed\", \"(dry-run)\")\n return buildDryRunChangedResult(env)\n }\n\n return await applyCheckedModule({\n currentEnvironment: env,\n dryRun: false,\n shutdownSignal,\n ssh,\n targetModule,\n verbose,\n })\n } catch (error) {\n return handleCaughtStepError({\n environment: env,\n error,\n moduleName: targetModule.name,\n shutdownSignal,\n verbose,\n })\n }\n}\n\ntype LoopArguments = {\n definitionSignals?: Module[]\n dryRun: boolean\n env: Environment\n modules: Module[]\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\ntype ModuleLoopState = {\n currentEnvironment: Environment\n signalsPending: boolean\n stopRun?: true\n}\n\nfunction updateLoopSignalState(input: {\n currentSignalsPending: boolean\n result: StepResult\n stats: RunStats\n}): boolean {\n if (input.result.status == null) return input.currentSignalsPending\n input.stats.update(input.result.status)\n return input.result.status === \"changed\" ? true : input.currentSignalsPending\n}\n\nfunction shouldFlushTopLevelSignals(input: {\n definitionSignals?: Module[]\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalsPending: boolean\n stats: RunStats\n stepResult: StepResult\n}): input is {\n definitionSignals: Module[]\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalsPending: boolean\n stats: RunStats\n stepResult: { flushSignals: true } & StepResult\n} {\n return (\n input.stepResult.flushSignals === true &&\n !input.dryRun &&\n input.shutdownSignal() == null &&\n input.signalsPending &&\n input.stats.failed === 0 &&\n input.definitionSignals != null\n )\n}\n\nasync function flushPendingTopLevelSignals(input: {\n currentEnvironment: Environment\n definitionSignals: Module[]\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<SignalRunStatus> {\n return runSignals({\n env: input.currentEnvironment,\n shutdownSignal: input.shutdownSignal,\n signals: input.definitionSignals,\n ssh: input.ssh,\n stats: input.stats,\n verbose: input.verbose,\n })\n}\n\nfunction applyLoopResultToState(\n state: ModuleLoopState,\n result: StepResult,\n stats: RunStats\n): ModuleLoopState {\n return {\n currentEnvironment: result.env,\n signalsPending: updateLoopSignalState({\n currentSignalsPending: state.signalsPending,\n result,\n stats,\n }),\n stopRun: result.stopRun === true ? true : state.stopRun,\n }\n}\n\nasync function flushTopLevelSignalsIfRequested(parameters: {\n definitionSignals?: Module[]\n dryRun: boolean\n loopState: ModuleLoopState\n result: StepResult\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<{ nextSignalsPending: boolean; outcome: \"break\" | \"continue\" }> {\n if (\n !shouldFlushTopLevelSignals({\n definitionSignals: parameters.definitionSignals,\n dryRun: parameters.dryRun,\n shutdownSignal: parameters.shutdownSignal,\n signalsPending: parameters.loopState.signalsPending,\n stats: parameters.stats,\n stepResult: parameters.result,\n })\n ) {\n return { nextSignalsPending: parameters.loopState.signalsPending, outcome: \"continue\" }\n }\n const definitionSignals = parameters.definitionSignals\n if (definitionSignals == null) {\n return { nextSignalsPending: parameters.loopState.signalsPending, outcome: \"continue\" }\n }\n\n const signalStatus = await flushPendingTopLevelSignals({\n currentEnvironment: parameters.loopState.currentEnvironment,\n definitionSignals,\n shutdownSignal: parameters.shutdownSignal,\n ssh: parameters.ssh,\n stats: parameters.stats,\n verbose: parameters.verbose,\n })\n return {\n nextSignalsPending: false,\n outcome: signalStatus === \"failed\" ? \"break\" : \"continue\",\n }\n}\n\nasync function createModuleStepPromise(parameters: {\n currentEnvironment: Environment\n currentModule: Module\n dryRun: boolean\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}): Promise<StepResult> {\n const { currentEnvironment, currentModule, dryRun, shutdownSignal, ssh, stats, verbose } =\n parameters\n\n return isRecipe(currentModule)\n ? runRecipeModule(\n currentModule,\n currentEnvironment,\n ssh,\n stats,\n verbose,\n dryRun,\n shutdownSignal\n )\n : runRegularModule({\n dryRun,\n env: currentEnvironment,\n shutdownSignal,\n ssh,\n targetModule: currentModule,\n verbose,\n })\n}\n\nasync function runModuleLoop(parameters: LoopArguments): Promise<{\n env: Environment\n signalsPending: boolean\n stopRun?: true\n}> {\n const { definitionSignals, dryRun, modules, shutdownSignal, ssh, stats, verbose } = parameters\n const loopState: ModuleLoopState = {\n currentEnvironment: parameters.env,\n signalsPending: false,\n stopRun: undefined,\n }\n\n for (const currentModule of modules) {\n // A module already running when the signal arrived completes normally\n // and its result is still counted in stats before the loop exits here.\n if (shutdownSignal() != null) break\n const stepPromise = createModuleStepPromise({\n currentEnvironment: loopState.currentEnvironment,\n currentModule,\n dryRun,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n\n // eslint-disable-next-line no-await-in-loop\n const result = await stepPromise\n\n Object.assign(loopState, applyLoopResultToState(loopState, result, stats))\n // eslint-disable-next-line no-await-in-loop\n const flushResult = await flushTopLevelSignalsIfRequested({\n definitionSignals,\n dryRun,\n loopState,\n result,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n loopState.signalsPending = flushResult.nextSignalsPending\n if (flushResult.outcome === \"break\") break\n if (result.shouldBreak) break\n }\n\n return {\n env: loopState.currentEnvironment,\n signalsPending: loopState.signalsPending,\n stopRun: loopState.stopRun,\n }\n}\n\ntype SignalArguments = {\n env: Environment\n shutdownSignal: () => NodeJS.Signals | null\n signals: Module[]\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\nasync function runSignals(parameters: SignalArguments): Promise<SignalRunStatus> {\n const { env, shutdownSignal, signals, ssh, stats, verbose } = parameters\n return runSignalModules({\n environment: env,\n hooks: {\n onSignalFinished: (status: ModuleStatus) => {\n stats.update(status)\n },\n onSignalStarted: () => {\n stats.incrementSignals()\n },\n },\n onSignalStep: async (step) => {\n await applyRunnerControlPlaneMeta(ssh, step)\n },\n shutdownSignal,\n signals,\n ssh,\n verbose,\n })\n}\n\nfunction throwIfShutdownRequested(shutdownSignal: () => NodeJS.Signals | null): void {\n const signal = shutdownSignal()\n if (signal == null) return\n throw new Error(`Bootstrap interrupted by ${signal}`)\n}\n\nasync function connectAndRegister(parameters: {\n definition: ServerDefinition\n options: RunOptions\n promptAbortSignal: AbortSignal\n setSsh: (c: SshConnectionImpl) => void\n shutdownSignal: () => NodeJS.Signals | null\n}): Promise<SshConnectionImpl> {\n const { definition, options, promptAbortSignal, setSsh, shutdownSignal } = parameters\n const sshConfig = {\n ...definition.ssh,\n ports: [...definition.ssh.ports],\n ...(options.reconnectTimeout == null ? {} : { reconnectTimeout: options.reconnectTimeout }),\n }\n const ssh = new SshConnectionImpl(definition.host, sshConfig)\n setSsh(ssh)\n throwIfShutdownRequested(shutdownSignal)\n await ssh.connect({ abortSignal: promptAbortSignal })\n throwIfShutdownRequested(shutdownSignal)\n return ssh\n}\n\ntype ExecuteRunArguments = {\n definition: ServerDefinition\n dryRun: boolean\n environment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: SshConnectionImpl\n stats: RunStats\n verbose: boolean\n}\n\nasync function executeRun(parameters: ExecuteRunArguments): Promise<void> {\n const { definition, dryRun, environment, shutdownSignal, ssh, stats, verbose } = parameters\n\n printRecipeHeader(definition.name)\n const loopResult = await runModuleLoop({\n definitionSignals: definition.signals,\n dryRun,\n env: environment,\n modules: definition.run,\n shutdownSignal,\n ssh,\n stats,\n verbose,\n })\n const finalEnvironment = loopResult.env\n\n if (\n !dryRun &&\n shutdownSignal() == null &&\n loopResult.signalsPending &&\n stats.failed === 0 &&\n definition.signals != null\n )\n await runSignals({\n env: finalEnvironment,\n shutdownSignal,\n signals: definition.signals,\n ssh,\n stats,\n verbose,\n })\n\n printSummary(stats)\n}\n\nfunction rethrowIfNotShutdown(error: unknown, shutdownSignal: () => NodeJS.Signals | null): void {\n if (shutdownSignal() == null) throw error\n}\n\nexport async function runPlaybook(\n definition: ServerDefinition,\n options: RunOptions = {}\n): Promise<void> {\n const { dryRun = false, verbose = false } = options\n const environment = await initializeEnvironment(options, definition)\n const { handleShutdownSignal, promptAbortSignal, setSsh, shutdownSignal } =\n setupShutdownHandlers()\n const stats = new RunStats()\n let ssh: SshConnectionImpl | undefined\n\n printRunContext({\n dryRun,\n host: definition.host,\n name: definition.name,\n ports: definition.ssh.ports,\n })\n\n // No catch block: connect errors propagate to cli.ts, which prints them and exits with code 2.\n try {\n ssh = await connectAndRegister({\n definition,\n options,\n promptAbortSignal,\n setSsh,\n shutdownSignal,\n })\n await executeRun({ definition, dryRun, environment, shutdownSignal, ssh, stats, verbose })\n } catch (error) {\n rethrowIfNotShutdown(error, shutdownSignal)\n } finally {\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const)\n process.removeListener(signal, handleShutdownSignal)\n ssh?.disconnect()\n }\n\n resolveExitCode(shutdownSignal(), stats)\n}\n"],"mappings":"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","names":["shouldExecuteApplyDuringDryRun"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { E as Environment, M as Module, a as ModuleMetaEntry, b as MetaEnvironmentValue, c as EnvironmentMetaEntry, d as SshdPortMetaEntry, e as SystemHostMetaEntry, f as SystemRebootMetaEntry, g as ModuleResult, h as ExecResult, i as ModuleStatus, j as SshConnection, O as OrchestrationStep, S as ServerDefinition } from './types-Cl2Muw1x.js';
2
2
  export { k as EnvironmentValue, l as ExecOptions, N as NEEDS_APPLY, m as SshConfig } from './types-Cl2Muw1x.js';
3
- export { a as apt, b as archive, c as command, d as compose, e as cron, f as download, g as file, h as git, i as group, j as hostname, m as mount, n as net, o as op, p as package, q as quadlet, r as releaseUpgrade, k as rsync, s as script, l as service, t as ssh, u as sshd, v as sysctl, w as system, x as systemd, y as ufw, z as user } from './user-BJMqDePy.js';
3
+ export { a as apt, b as archive, c as command, d as compose, e as cron, f as download, g as file, h as git, i as group, j as hostname, m as mount, n as net, o as op, p as package, q as quadlet, r as releaseUpgrade, k as rsync, s as script, l as service, t as ssh, u as sshd, v as sysctl, w as system, x as systemd, y as ufw, z as user } from './user-B9lkXr0X.js';
4
4
 
5
5
  /**
6
6
  * Fail the run if a condition on the current env is not satisfied.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  runSignalModules,
7
7
  startModuleSpinner,
8
8
  withRecipeOutputScope
9
- } from "./chunk-IUY5BJHA.js";
9
+ } from "./chunk-47PTUZZR.js";
10
10
  import {
11
11
  NEEDS_APPLY,
12
12
  apt,
@@ -39,7 +39,7 @@ import {
39
39
  systemd,
40
40
  ufw,
41
41
  user
42
- } from "./chunk-D4CS2GCH.js";
42
+ } from "./chunk-7Y2RBVG4.js";
43
43
  import {
44
44
  CommandError,
45
45
  assertValidModuleMetaEntries,
@@ -62,7 +62,7 @@ import {
62
62
  systemHostMeta,
63
63
  systemRebootMeta,
64
64
  validateSshConfig
65
- } from "./chunk-JJRF37BP.js";
65
+ } from "./chunk-NRDLYHJL.js";
66
66
 
67
67
  // src/conditionalModules.ts
68
68
  function createConditionalApplyState(environment) {
@@ -1,4 +1,4 @@
1
- export { a as apt, b as archive, c as command, d as compose, e as cron, f as download, g as file, h as git, i as group, j as hostname, m as mount, n as net, o as op, p as package, q as quadlet, r as releaseUpgrade, k as rsync, s as script, l as service, t as ssh, u as sshd, v as sysctl, w as system, x as systemd, y as ufw, z as user } from '../user-BJMqDePy.js';
1
+ export { U as UpgradeOptions, a as apt, b as archive, c as command, d as compose, e as cron, f as download, g as file, h as git, i as group, j as hostname, m as mount, n as net, o as op, p as package, q as quadlet, r as releaseUpgrade, k as rsync, s as script, l as service, t as ssh, u as sshd, v as sysctl, w as system, x as systemd, y as ufw, z as user } from '../user-B9lkXr0X.js';
2
2
  import { M as Module } from '../types-Cl2Muw1x.js';
3
3
 
4
4
  /**
@@ -26,8 +26,8 @@ import {
26
26
  systemd,
27
27
  ufw,
28
28
  user
29
- } from "../chunk-D4CS2GCH.js";
30
- import "../chunk-JJRF37BP.js";
29
+ } from "../chunk-7Y2RBVG4.js";
30
+ import "../chunk-NRDLYHJL.js";
31
31
  export {
32
32
  apt,
33
33
  archive,
@@ -1,5 +1,108 @@
1
1
  import { M as Module } from './types-Cl2Muw1x.js';
2
2
 
3
+ /** Per-call overrides for package operations that can take a long time. */
4
+ type UpgradeOptions = {
5
+ /** Override the SSH layer's command timeout (milliseconds). */
6
+ timeout?: number;
7
+ };
8
+ /**
9
+ * Distro-agnostic package management module.
10
+ *
11
+ * Automatically detects the system package manager (apt, dnf, yum, apk)
12
+ * and delegates to the appropriate commands. All methods are idempotent.
13
+ *
14
+ * For Debian/Ubuntu-specific configuration (debconf pre-seeding, GPG keys,
15
+ * apt repositories) use the `apt` module instead.
16
+ *
17
+ * @example
18
+ * // Install packages
19
+ * pkg.installed("git", "curl")
20
+ *
21
+ * @example
22
+ * // Refresh package lists and upgrade all packages on a specific date
23
+ * pkg.update("2024-01-15")
24
+ * pkg.upgrade("2024-01-15")
25
+ */
26
+ declare const pkg: {
27
+ /**
28
+ * Ensure the given packages are not installed.
29
+ *
30
+ * The check phase queries the package database for each package individually;
31
+ * the remove command is only executed when at least one package is present.
32
+ *
33
+ * Pass an `UpgradeOptions` object as the last argument to override the SSH
34
+ * timeout for slow remove operations.
35
+ *
36
+ * @param packagesAndOptions - One or more package names, optionally followed
37
+ * by an `UpgradeOptions` object as the last argument.
38
+ * @returns A Module that removes the packages if any are present.
39
+ *
40
+ * @example
41
+ * pkg.absent("vim", "nano")
42
+ * pkg.absent("vim", "nano", { timeout: 600_000 })
43
+ */
44
+ absent(...packagesAndOptions: Array<string | UpgradeOptions>): Module;
45
+ /**
46
+ * Ensure the given packages are installed.
47
+ *
48
+ * The check phase queries the package database for each package individually;
49
+ * the install command is only executed when at least one package is missing.
50
+ *
51
+ * Pass an `UpgradeOptions` object as the last argument to override the SSH
52
+ * timeout for slow install operations.
53
+ *
54
+ * @param packagesAndOptions - One or more package names, optionally followed
55
+ * by an `UpgradeOptions` object as the last argument.
56
+ * @returns A Module that installs missing packages.
57
+ *
58
+ * @example
59
+ * pkg.installed("git", "curl", "unzip")
60
+ * pkg.installed("texlive-full", { timeout: 900_000 })
61
+ */
62
+ installed(...packagesAndOptions: Array<string | UpgradeOptions>): Module;
63
+ /**
64
+ * Refresh the package manager's package lists once per dated flag.
65
+ *
66
+ * A flag file at `${FLAGS_DIRECTORY}/package-update-<date>` is created after
67
+ * a successful run. On the next run the flag is detected and the module
68
+ * reports `"ok"` without running the update again. Changing `date` to a new
69
+ * value invalidates all previous flags for this operation.
70
+ *
71
+ * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
72
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
73
+ * @returns A Module that refreshes package lists.
74
+ *
75
+ * @example
76
+ * pkg.update("2024-01-15")
77
+ * pkg.update("2024-01-15", { timeout: 600_000 })
78
+ */
79
+ update(date: string, options?: UpgradeOptions): Module;
80
+ /**
81
+ * Upgrade all installed packages once per dated flag.
82
+ *
83
+ * A flag file at `${FLAGS_DIRECTORY}/package-upgrade-<date>` is created after
84
+ * a successful run. On the next run the flag is detected and the module
85
+ * reports `"ok"` without running the upgrade again. Changing `date` to a new
86
+ * value invalidates all previous flags for this operation.
87
+ *
88
+ * On apt systems this runs `dpkg --configure -a`, `apt-get update`, and
89
+ * `apt-get upgrade -y` as three separate commands (each subject to its own
90
+ * SSH `timeout`). For full dependency resolution use `apt.distUpgrade`.
91
+ *
92
+ * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
93
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
94
+ * The same `timeout` is applied to every step of the upgrade pipeline.
95
+ * @returns A Module that upgrades all packages.
96
+ *
97
+ * @example
98
+ * pkg.upgrade("2024-01-15")
99
+ * pkg.upgrade("2024-01-15", { timeout: 900_000 })
100
+ *
101
+ * @see apt.distUpgrade
102
+ */
103
+ upgrade(date: string, options?: UpgradeOptions): Module;
104
+ };
105
+
3
106
  /**
4
107
  * Modules for Debian/Ubuntu-specific apt configuration.
5
108
  *
@@ -35,10 +138,19 @@ declare const apt: {
35
138
  * Run `apt-get update && apt-get dist-upgrade` once per dated flag.
36
139
  * Performs a full distribution upgrade with dependency resolution.
37
140
  *
141
+ * The pipeline is split into three separate SSH commands so that each step
142
+ * gets its own timeout window and produces a precise failure label.
143
+ *
38
144
  * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
145
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
146
+ * The same `timeout` is applied to every step of the dist-upgrade pipeline.
39
147
  * @returns A Module that performs the dist-upgrade.
148
+ *
149
+ * @example
150
+ * apt.distUpgrade("2024-01-15")
151
+ * apt.distUpgrade("2024-01-15", { timeout: 900_000 })
40
152
  */
41
- distUpgrade(date: string): Module;
153
+ distUpgrade(date: string, options?: UpgradeOptions): Module;
42
154
  /**
43
155
  * Import a GPG key into `/etc/apt/keyrings/` for use with signed repositories.
44
156
  * @param name - Key file name (without `.gpg` extension).
@@ -811,88 +923,6 @@ declare const op: {
811
923
  resolve(references: Record<string, string>): Module;
812
924
  };
813
925
 
814
- /**
815
- * Distro-agnostic package management module.
816
- *
817
- * Automatically detects the system package manager (apt, dnf, yum, apk)
818
- * and delegates to the appropriate commands. All methods are idempotent.
819
- *
820
- * For Debian/Ubuntu-specific configuration (debconf pre-seeding, GPG keys,
821
- * apt repositories) use the `apt` module instead.
822
- *
823
- * @example
824
- * // Install packages
825
- * pkg.installed("git", "curl")
826
- *
827
- * @example
828
- * // Refresh package lists and upgrade all packages on a specific date
829
- * pkg.update("2024-01-15")
830
- * pkg.upgrade("2024-01-15")
831
- */
832
- declare const pkg: {
833
- /**
834
- * Ensure the given packages are not installed.
835
- *
836
- * The check phase queries the package database for each package individually;
837
- * the remove command is only executed when at least one package is present.
838
- *
839
- * @param packages - One or more package names to remove.
840
- * @returns A Module that removes the packages if any are present.
841
- *
842
- * @example
843
- * pkg.absent("vim", "nano")
844
- */
845
- absent(...packages: string[]): Module;
846
- /**
847
- * Ensure the given packages are installed.
848
- *
849
- * The check phase queries the package database for each package individually;
850
- * the install command is only executed when at least one package is missing.
851
- *
852
- * @param packages - One or more package names to install.
853
- * @returns A Module that installs missing packages.
854
- *
855
- * @example
856
- * pkg.installed("git", "curl", "unzip")
857
- */
858
- installed(...packages: string[]): Module;
859
- /**
860
- * Refresh the package manager's package lists once per dated flag.
861
- *
862
- * A flag file at `${FLAGS_DIRECTORY}/package-update-<date>` is created after
863
- * a successful run. On the next run the flag is detected and the module
864
- * reports `"ok"` without running the update again. Changing `date` to a new
865
- * value invalidates all previous flags for this operation.
866
- *
867
- * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
868
- * @returns A Module that refreshes package lists.
869
- *
870
- * @example
871
- * pkg.update("2024-01-15")
872
- */
873
- update(date: string): Module;
874
- /**
875
- * Upgrade all installed packages once per dated flag.
876
- *
877
- * A flag file at `${FLAGS_DIRECTORY}/package-upgrade-<date>` is created after
878
- * a successful run. On the next run the flag is detected and the module
879
- * reports `"ok"` without running the upgrade again. Changing `date` to a new
880
- * value invalidates all previous flags for this operation.
881
- *
882
- * On apt systems this runs `apt-get update && apt-get upgrade -y` (not
883
- * `dist-upgrade`); use `apt.distUpgrade` for full dependency resolution.
884
- *
885
- * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
886
- * @returns A Module that upgrades all packages.
887
- *
888
- * @example
889
- * pkg.upgrade("2024-01-15")
890
- *
891
- * @see apt.distUpgrade
892
- */
893
- upgrade(date: string): Module;
894
- };
895
-
896
926
  type QuadletAutoUpdate = "local" | "registry";
897
927
  type QuadletHealthOnFailure = "kill" | "none" | "restart" | "stop";
898
928
  type QuadletPullPolicy = "always" | "missing" | "never" | "newer";
@@ -1440,4 +1470,4 @@ declare const user: {
1440
1470
  present(name: string, options?: UserOptions): Module;
1441
1471
  };
1442
1472
 
1443
- export { apt as a, archive as b, command as c, compose as d, cron as e, download as f, file as g, git as h, group as i, hostname as j, rsync as k, service as l, mount as m, net as n, op as o, pkg as p, quadlet as q, releaseUpgrade as r, script as s, ssh as t, sshd as u, sysctl as v, system as w, systemd as x, ufw as y, user as z };
1473
+ export { type UpgradeOptions as U, apt as a, archive as b, command as c, compose as d, cron as e, download as f, file as g, git as h, group as i, hostname as j, rsync as k, service as l, mount as m, net as n, op as o, pkg as p, quadlet as q, releaseUpgrade as r, script as s, ssh as t, sshd as u, sysctl as v, system as w, systemd as x, ufw as y, user as z };