/* werkstatt.platform.update — bump @warpgogol/* platform dependencies in a workshop and report Sternsystems whose pinnedPlatform drifts from the new installed version (RFC-1125). Do not rewrite pinnedPlatform — drift is reported, not auto-fixed (pin is a deliberate act). Do not touch non-@warpgogol dependencies. RFC-1125: initial werkstatt.platform.update — pure platformUpdate() + thin kernel handler, idempotent, drift report. RFC-1149: migrate ambient filesystem and process IO to the WorkspaceIO port across engine and shared */ import { join } from "node:path"; import type { KernelCommandInput, KernelCommandResult, KernelRuntimeContext, } from "@warpgogol/werkstatt-engine/kernel"; import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; import { discoverSystems } from "../sternsystem/registry-io.ts"; export interface PlatformUpdateData { command: "werkstatt.platform.update"; /** Installed @warpgogol/werkstatt-engine version before the update (null if unreadable). */ from: string | null; /** Installed version after the update. */ to: string | null; /** True when the installed version changed. */ changed: boolean; /** The pnpm command that was run. */ pnpmCommand: string; /** Sternsystems whose pinnedPlatform differs from the new installed version. */ driftedSystems: Array<{ id: string; pinnedPlatform: string }>; } /** Injectable dependencies for testing. */ export interface PlatformUpdateDeps { /** Run the pnpm command (defaults to io.exec sh -c, throws on non-zero). */ exec?: (cmd: string, cwd: string) => void | Promise; /** Read the installed engine version (defaults to node_modules read). */ installedVersion?: (root: string) => Promise; /** List registered systems (defaults to discoverSystems). */ listSystems?: (root: string) => Promise>; } const ENGINE_PKG = "@warpgogol/werkstatt-engine"; const UPDATE_SCOPE = "@warpgogol/*"; async function defaultInstalledVersion(workspaceRoot: string): Promise { const pkgPath = join( workspaceRoot, "node_modules", "@warpgogol", "werkstatt-engine", "package.json", ); const io = createDefaultIO().io; if (!(await io.exists(pkgPath))) return null; try { const pkg = JSON.parse(await io.readFile(pkgPath)) as { version?: string }; return pkg.version ?? null; } catch { return null; } } async function defaultExec(cmd: string, cwd: string): Promise { const result = await createDefaultIO().io.exec("sh", ["-c", cmd], { cwd, timeoutMs: 300_000, }); if (result.exitCode !== 0) { throw new Error(`pnpm exec failed (${result.exitCode}): ${result.stderr.slice(-500)}`); } } async function defaultListSystems( workspaceRoot: string, ): Promise> { const { systems } = await discoverSystems(workspaceRoot); return systems.map((s) => ({ id: s.id, pinnedPlatform: s.pinnedPlatform })); } /** * Pure update logic — callable from tests and non-kernel contexts. * `to` is a semver string or "latest". Idempotent: re-running on an * up-to-date workshop reports changed=false and no drift delta. */ export async function platformUpdate( workspaceRoot: string, to: string, deps: PlatformUpdateDeps = {}, ): Promise { const exec = deps.exec ?? defaultExec; const installedVersion = deps.installedVersion ?? defaultInstalledVersion; const listSystems = deps.listSystems ?? defaultListSystems; const from = await installedVersion(workspaceRoot); const pnpmCommand = to === "latest" ? `pnpm up -rL "${UPDATE_SCOPE}"` : `pnpm up -r "${UPDATE_SCOPE}@${to}"`; await exec(pnpmCommand, workspaceRoot); const after = await installedVersion(workspaceRoot); const systems = await listSystems(workspaceRoot); const driftedSystems = after === null ? [] : systems .filter((s) => s.pinnedPlatform !== after) .map((s) => ({ id: s.id, pinnedPlatform: s.pinnedPlatform })); return { command: "werkstatt.platform.update", from, to: after, changed: from !== after, pnpmCommand, driftedSystems, }; } function flagString(input: KernelCommandInput, key: string): string | undefined { const v = input.flags[key]; return typeof v === "string" ? v : undefined; } /** Thin kernel handler — delegates to the pure platformUpdate(). */ export async function runPlatformUpdate( input: KernelCommandInput, context: KernelRuntimeContext, ): Promise> { const { workspaceRoot, logger } = context; const to = flagString(input, "to") ?? "latest"; const result = await platformUpdate(workspaceRoot, to); if (result.changed) { logger.info(`[werkstatt.platform.update] ${ENGINE_PKG} ${result.from} → ${result.to}`); } else { logger.info(`[werkstatt.platform.update] already at ${result.to ?? "unknown"} — no change`); } for (const s of result.driftedSystems) { logger.warn( ` [werkstatt.platform.update] '${s.id}' pinnedPlatform ${s.pinnedPlatform} drifts from installed ${result.to} — re-pin via sternsystem.pin when ready`, ); } return { data: result, summary: result.driftedSystems.length > 0 ? `[werkstatt.platform.update] ${result.from} → ${result.to}; ${result.driftedSystems.length} system(s) drift` : `[werkstatt.platform.update] ${result.from} → ${result.to}`, exitCode: 0, }; }