UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

1 lines • 26.1 kB
{"version":3,"file":"legs.mjs","names":[],"sources":["../../../src/services/deploy/legs.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport type { DeployLegOutcome } from \"../../contract\";\nimport { colors, icons } from \"../../utils/theme\";\nimport {\n buildAndPushImage,\n deployImageToRailway,\n hasDocker,\n imageRefWithDigest,\n type ResolvedImageRef,\n repointServiceSource,\n resolveImageRef,\n resolveRegistryDigest,\n} from \"./image\";\nimport { deriveImageRefFromPlatformManifest } from \"./platform-manifest\";\nimport { rollbackToPreviousSnapshot } from \"./rollback-flow\";\nimport { runSmoke } from \"./smoke\";\n\nexport type { DeployLegOutcome };\nexport type DeployLegStatus = DeployLegOutcome[\"status\"];\n\nexport interface DeployLegs {\n image?: DeployLegOutcome;\n smoke?: DeployLegOutcome;\n railway?: DeployLegOutcome;\n}\n\n/**\n * Runtime tier (ADR 0020/0021): a \"root\" train owns its runtime image —\n * `BOS_IMAGE` names one (with a pre-pushed digest) or a Dockerfile exists at\n * the config root, so the train can produce a digest and pin Railway to\n * exactly the bytes it validated. A \"child\" runtime inherits the platform's\n * universal image (identity-driven boot); it has no image bytes to build,\n * pin, or boot — its image ref derives from the platform release manifest\n * (ADR 0034).\n */\nexport type RuntimeTier = \"root\" | \"child\";\n\nexport function resolveRuntimeTier(input: {\n imageEnv?: string;\n hasDockerfile: boolean;\n}): RuntimeTier {\n return input.imageEnv || input.hasDockerfile ? \"root\" : \"child\";\n}\n\n/** The child image pin (ADR 0034): `ref:v<version>` from the resolved\n * platform release manifest, with the tag's digest resolved from the\n * registry. The Railway service source repoints to the ref and the smoke\n * gate boots the digest. */\nexport interface ChildImagePin {\n image: string;\n tag: string;\n digest: string;\n}\n\n/**\n * The child image pin: the platform release manifest resolves through the\n * extends chain (authored `platform` pin, else nearest-wins) to `ref:tag`,\n * and the registry resolves the tag's digest. Undefined when no manifest\n * resolves — the additive fallback (ADR 0034) is today's float redeploy.\n */\nexport async function resolveChildImagePin(input: {\n configDir: string;\n localConfig: Record<string, unknown>;\n fetchManifest?: Parameters<typeof deriveImageRefFromPlatformManifest>[0][\"fetchManifest\"];\n fetchDigest?: typeof resolveRegistryDigest;\n}): Promise<ChildImagePin | undefined> {\n const derived = await deriveImageRefFromPlatformManifest({\n configDir: input.configDir,\n localConfig: input.localConfig,\n fetchManifest: input.fetchManifest,\n });\n if (!derived) return undefined;\n const digest = await (input.fetchDigest ?? resolveRegistryDigest)({\n ref: `${derived.image}:${derived.tag}`,\n });\n return { image: derived.image, tag: derived.tag, digest };\n}\n\n/**\n * The post-publish decision table (ADR 0020/0021/0034) — pure data, one\n * place. Precedence for the image leg: an explicit digest handover\n * (`BOS_IMAGE_DIGEST`, any tier) wins; a child pins the platform manifest's\n * ref or skips with the inheritance reason; a root runs the\n * build/prebuilt/skip chain. Smoke boots only pinned/known-digest refs.\n * Every step is executable without further decisions; the railway step is\n * decided separately (`resolveRailwayStep`) because it legitimately depends\n * on the image leg's execution outcome (the captured digest).\n */\n\nexport type ImageStep =\n | { action: \"pinned\"; image: string; tag: string; digest: string }\n | { action: \"prebuilt\"; image: string; digest: string }\n | { action: \"build\"; image: string }\n | { action: \"skip\"; reason: string };\n\nexport type SmokeStep =\n /** `ref` is precomputed when the digest is known at plan time; a build\n * boots `image` and the interpreter appends the captured digest. */\n { action: \"boot\"; image: string; ref?: string } | { action: \"skip\"; reason: string };\n\nexport interface DeployPlan {\n tier: RuntimeTier;\n image: ImageStep;\n /** Absent when `--smoke` was not requested. */\n smoke?: SmokeStep;\n}\n\nexport function resolveDeployPlan(input: {\n tier: RuntimeTier;\n childPin?: ChildImagePin;\n imageRef?: ResolvedImageRef;\n digestHandover?: string;\n hasDocker: boolean;\n hasDockerfile: boolean;\n smokeRequested: boolean;\n}): DeployPlan {\n let image: ImageStep;\n if (input.digestHandover && input.imageRef) {\n image = {\n action: \"prebuilt\",\n image: input.imageRef.image,\n digest: input.digestHandover,\n };\n } else if (input.tier === \"child\") {\n image = input.childPin\n ? {\n action: \"pinned\",\n image: input.childPin.image,\n tag: input.childPin.tag,\n digest: input.childPin.digest,\n }\n : {\n action: \"skip\",\n reason:\n \"child runtime inherits the root's universal image (ADR 0020) — no image leg in this train\",\n };\n } else if (!input.imageRef) {\n image = {\n action: \"skip\",\n reason:\n \"set BOS_IMAGE (or repository in bos.config.json) to build and push the runtime image\",\n };\n } else if (!input.hasDocker) {\n image = { action: \"skip\", reason: \"docker is not available\" };\n } else if (!input.hasDockerfile) {\n image = { action: \"skip\", reason: \"no Dockerfile at the config root\" };\n } else {\n image = { action: \"build\", image: input.imageRef.image };\n }\n\n let smoke: SmokeStep | undefined;\n if (input.smokeRequested) {\n if (image.action === \"pinned\" || image.action === \"prebuilt\") {\n smoke = {\n action: \"boot\",\n image: image.image,\n ref: imageRefWithDigest(image.image, image.digest),\n };\n } else if (image.action === \"build\") {\n smoke = { action: \"boot\", image: image.image };\n } else {\n smoke = {\n action: \"skip\",\n reason:\n input.tier === \"child\"\n ? \"child runtime inherits the root's universal image — no image digest to boot in this train\"\n : \"no runtime image digest — pass BOS_IMAGE_DIGEST for a pre-pushed image\",\n };\n }\n }\n\n return { tier: input.tier, image, ...(smoke ? { smoke } : {}) };\n}\n\nexport type RailwayStep =\n | { action: \"repoint-and-redeploy\"; service: string; image: string }\n | { action: \"redeploy\"; service: string }\n | { action: \"fail\"; detail: string }\n | { action: \"skip\"; detail: string };\n\n/**\n * The railway step, decided after the image leg ran: a root runtime must\n * have pinned bytes (`pinnedDigest` — handover, pin, or build capture);\n * without them the \"pulled bytes are what this train validated\" guarantee\n * (ADR 0021) is unmeetable, and that is a failure, not a skip. A child\n * runtime never needs a digest: with a pin the service source repoints to\n * the pinned ref before the redeploy pulls it; without one, `redeploy\n * --from-source` is just a reload trigger for the freshly published config.\n * The token is project-scoped and cannot touch another project's service.\n */\nexport function resolveRailwayStep(input: {\n tier: RuntimeTier;\n token?: string;\n service?: string;\n pinnedDigest?: string;\n childPin?: ChildImagePin;\n}): RailwayStep {\n if (!input.token) {\n return { action: \"skip\", detail: \"RAILWAY_TOKEN not set\" };\n }\n if (!input.service) {\n return input.tier === \"root\"\n ? {\n action: \"fail\",\n detail: \"ci.railway.service is not configured in bos.config.json\",\n }\n : {\n action: \"skip\",\n detail: \"set ci.railway.service in bos.config.json to trigger a Railway redeploy\",\n };\n }\n if (input.tier === \"root\" && !input.pinnedDigest) {\n return {\n action: \"fail\",\n detail:\n \"Railway deploy requires a runtime image (pass BOS_IMAGE_DIGEST for a pre-pushed image)\",\n };\n }\n if (input.childPin) {\n return {\n action: \"repoint-and-redeploy\",\n service: input.service,\n image: `${input.childPin.image}:${input.childPin.tag}`,\n };\n }\n return { action: \"redeploy\", service: input.service };\n}\n\n/**\n * The post-publish legs (ticket #422): image → smoke boot gate → Railway,\n * executing the pure decision table (`resolveDeployPlan` +\n * `resolveRailwayStep`). The deploy handler hands over the published-config\n * context; leg outcomes derive from leg results — a red boot gate rolls the\n * config back here, and the handler maps the returned outcome onto the wire\n * result mechanically.\n */\n\nexport interface PostPublishLegsInput {\n sessionRoot: string;\n env: \"production\" | \"staging\";\n smoke: boolean;\n imageDigestInput?: string;\n railwayService?: string;\n registry?: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n wallet?: boolean;\n verbose?: boolean;\n /** post-publish session identity (the possibly-refreshed runtime) */\n account?: string;\n domain?: string;\n runtimeConfig: Parameters<typeof rollbackToPreviousSnapshot>[0][\"runtimeConfig\"];\n /** The authored leaf config — the platform pin + extends chain a child's\n * image pin resolves from (ADR 0034). */\n localConfig?: Record<string, unknown>;\n repository?: string;\n}\n\nexport interface PostPublishLegsOutcome {\n image?: string;\n imageDigest?: string;\n service?: string;\n legs: DeployLegs;\n /** terminal failure after a published config — the config stays published */\n failure?: string;\n}\n\n/** The seams the legs need — injectable for tests, real modules by default. */\nexport interface PostPublishLegsDeps {\n buildAndPushImage: typeof buildAndPushImage;\n hasDocker: typeof hasDocker;\n runSmoke: typeof runSmoke;\n deployImageToRailway: typeof deployImageToRailway;\n repointServiceSource: typeof repointServiceSource;\n resolveChildImagePin: typeof resolveChildImagePin;\n rollbackToPreviousSnapshot: typeof rollbackToPreviousSnapshot;\n}\n\nexport const realPostPublishLegsDeps: PostPublishLegsDeps = {\n buildAndPushImage,\n hasDocker,\n runSmoke,\n deployImageToRailway,\n repointServiceSource,\n resolveChildImagePin,\n rollbackToPreviousSnapshot,\n};\n\nexport async function runPostPublishLegs(\n input: PostPublishLegsInput,\n deps: PostPublishLegsDeps = realPostPublishLegsDeps,\n): Promise<PostPublishLegsOutcome> {\n const legs: DeployLegs = {};\n let image: string | undefined;\n let imageDigest: string | undefined;\n let service: string | undefined;\n\n const hasDockerfile = existsSync(join(input.sessionRoot, \"Dockerfile\"));\n const tier = resolveRuntimeTier({\n imageEnv: process.env.BOS_IMAGE,\n hasDockerfile,\n });\n\n let childPin: ChildImagePin | undefined;\n if (tier === \"child\") {\n try {\n childPin = await deps.resolveChildImagePin({\n configDir: input.sessionRoot,\n localConfig: input.localConfig ?? {},\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const detail = `Image pin failed: ${message}`;\n legs.image = { status: \"failed\", detail };\n console.log(colors.yellow(` ${detail}`));\n return { legs, failure: `Config published but ${detail}` };\n }\n }\n\n const plan = resolveDeployPlan({\n tier,\n childPin,\n imageRef: resolveImageRef({\n repository: input.repository,\n env: process.env,\n }),\n digestHandover: input.imageDigestInput,\n hasDocker: tier === \"root\" ? await deps.hasDocker() : false,\n hasDockerfile,\n smokeRequested: input.smoke,\n });\n\n switch (plan.image.action) {\n case \"skip\": {\n legs.image = { status: \"skipped\", detail: plan.image.reason };\n console.log();\n console.log(colors.yellow(` Image skipped: ${plan.image.reason}`));\n break;\n }\n case \"pinned\": {\n image = plan.image.image;\n imageDigest = plan.image.digest;\n legs.image = {\n status: \"ok\",\n detail: `pinned ${plan.image.image}:${plan.image.tag} @ ${plan.image.digest.slice(0, 19)}…`,\n };\n console.log();\n console.log(\n colors.green(\n ` ${icons.ok} Pinned ${plan.image.image}:${plan.image.tag} @ ${plan.image.digest.slice(0, 19)}… (platform manifest)`,\n ),\n );\n break;\n }\n case \"prebuilt\": {\n image = plan.image.image;\n imageDigest = plan.image.digest;\n legs.image = {\n status: \"ok\",\n detail: `pre-pushed ${plan.image.image}@${plan.image.digest.slice(0, 19)}…`,\n };\n console.log();\n console.log(\n colors.green(\n ` ${icons.ok} Using pre-pushed image ${plan.image.image}@${plan.image.digest.slice(0, 19)}…`,\n ),\n );\n break;\n }\n case \"build\": {\n console.log();\n try {\n const imageResult = await deps.buildAndPushImage({\n image: plan.image.image,\n configDir: input.sessionRoot,\n verbose: input.verbose,\n });\n image = imageResult.image;\n imageDigest = imageResult.digest;\n legs.image = {\n status: \"ok\",\n detail: `built and pushed ${imageResult.image}:${imageResult.tag}`,\n };\n console.log(\n colors.green(\n ` ${icons.ok} Image pushed ${imageResult.image}:${imageResult.tag}${imageResult.digest ? ` (${imageResult.digest.slice(0, 19)}…)` : \"\"}`,\n ),\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const imageError =\n message.includes(\"not found\") || message.includes(\"ENOENT\")\n ? \"Docker not found — install Docker to build the runtime image\"\n : `Image build/push failed: ${message}`;\n legs.image = { status: \"failed\", detail: imageError };\n console.log(colors.yellow(` ${imageError}`));\n return {\n legs,\n failure: `Config published but ${imageError}`,\n };\n }\n break;\n }\n }\n\n // Post-publish boot gate (publish → smoke → Railway, ADR 0021): the\n // just-published config is only promoted to Railway if the runtime image —\n // the one this train pinned — boots against it. A red smoke rolls the\n // config back to the previous snapshot, so the new-config/old-image window\n // never survives the deploy run.\n if (plan.smoke) {\n if (plan.smoke.action === \"skip\") {\n legs.smoke = { status: \"skipped\", detail: plan.smoke.reason };\n console.log();\n console.log(colors.yellow(` Post-publish smoke skipped: ${plan.smoke.reason}`));\n } else {\n const ref =\n plan.smoke.ref ??\n (imageDigest ? imageRefWithDigest(plan.smoke.image, imageDigest) : undefined);\n if (!ref || !imageDigest) {\n const smokeSkip = \"no runtime image digest — pass BOS_IMAGE_DIGEST for a pre-pushed image\";\n legs.smoke = { status: \"skipped\", detail: smokeSkip };\n console.log();\n console.log(colors.yellow(` Post-publish smoke skipped: ${smokeSkip}`));\n } else {\n console.log();\n console.log(colors.cyan(\" ▶ Post-publish smoke (boot gate)\"));\n const smokeResult = await deps.runSmoke({\n image: ref,\n account: input.account,\n gateway: input.domain,\n });\n if (!smokeResult.ok) {\n const cause = smokeResult.error ?? \"unknown failure\";\n console.log();\n console.log(colors.error(` ${icons.err} Post-publish smoke failed: ${cause}`));\n if (smokeResult.diagnosis) {\n console.log(colors.yellow(` ${smokeResult.diagnosis}`));\n }\n legs.smoke = { status: \"failed\", detail: cause };\n if (!input.account || !input.domain) {\n console.log(colors.yellow(\" Rollback skipped: no registry identity in the session\"));\n return {\n legs,\n failure: `Post-publish smoke failed (${cause}) — config NOT rolled back: no registry identity`,\n };\n }\n console.log(colors.yellow(\" Rolling the published config back…\"));\n const rollback = await deps.rollbackToPreviousSnapshot({\n account: input.account,\n domain: input.domain,\n env: input.env,\n runtimeConfig: input.runtimeConfig,\n configDir: input.sessionRoot,\n registry: input.registry,\n network: input.network,\n privateKey: input.privateKey,\n verbose: input.verbose,\n });\n const rollbackNote = rollback.ok\n ? `config rolled back to block ${rollback.blockHeight}`\n : `config NOT rolled back: ${rollback.error ?? \"unknown\"}`;\n console.log(colors.yellow(` ${rollbackNote}`));\n return {\n legs,\n failure: `Post-publish smoke failed (${cause}) — ${rollbackNote}`,\n };\n }\n legs.smoke = { status: \"ok\", detail: smokeResult.summary };\n console.log(\n colors.green(` ${icons.ok} Post-publish smoke passed: ${smokeResult.summary ?? \"\"}`),\n );\n }\n }\n }\n\n const railwayStep = resolveRailwayStep({\n tier,\n token: process.env.RAILWAY_TOKEN,\n service: input.railwayService,\n pinnedDigest: imageDigest,\n childPin,\n });\n\n if (railwayStep.action === \"skip\") {\n legs.railway = { status: \"skipped\", detail: railwayStep.detail };\n console.log();\n console.log(colors.yellow(` Railway deploy skipped: ${railwayStep.detail}`));\n } else if (railwayStep.action === \"fail\") {\n legs.railway = { status: \"failed\", detail: railwayStep.detail };\n console.log();\n console.log(colors.yellow(` Railway deploy failed: ${railwayStep.detail}`));\n return {\n legs,\n failure: `Config published but ${railwayStep.detail}`,\n };\n } else {\n service = railwayStep.service;\n try {\n // A pinned child's service source follows the platform manifest pin\n // before the redeploy pulls from it (ADR 0034): the pulled bytes are\n // the pin, not whatever the dashboard's floating ref moved to.\n if (railwayStep.action === \"repoint-and-redeploy\") {\n await deps.repointServiceSource({\n service: railwayStep.service,\n image: railwayStep.image,\n configDir: input.sessionRoot,\n });\n }\n await deps.deployImageToRailway({\n service: railwayStep.service,\n configDir: input.sessionRoot,\n });\n legs.railway = {\n status: \"ok\",\n detail:\n railwayStep.action === \"repoint-and-redeploy\"\n ? `pinned ${railwayStep.image}, redeployed ${service}`\n : `redeployed ${service}`,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const railError =\n message.includes(\"not found\") || message.includes(\"ENOENT\")\n ? \"Railway CLI not found. Install it: npm i -g @railway/cli\"\n : `Railway deploy failed: ${message}`;\n legs.railway = { status: \"failed\", detail: railError };\n console.log(colors.yellow(` ${railError}`));\n return {\n legs,\n image,\n imageDigest,\n service,\n failure: `Config published but ${railError}`,\n };\n }\n }\n\n return { image, imageDigest, service, legs };\n}\n"],"mappings":"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"}