UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

95 lines (94 loc) 4.09 kB
import { execFileSync } from "child_process"; import { logError, logWarn } from "../utils/log.js"; import { findAppRoot, getCurrentStack, readStackConfig } from "../utils/pulumi.js"; import { resolvePulumiEnv } from "../utils/pulumi-run.js"; import { assertDeployDepsFresh } from "../utils/deploy-preflight.js"; import { resolveWorktreeIdentity, stackNeedsWorktreeIsolation } from "../utils/worktree-identity.js"; const OPS_WITHOUT_STACK_FLAG = new Set([ "whoami", "version", "plugin", "login", "logout", "about", "org", "env", "help", ]); const STACK_SUBCOMMANDS_WITHOUT_FLAG = new Set([ "rm", "select", "init", "rename", "unselect", "ls", ]); export function buildPulumiArgs(pulumiArgs, stack) { const base = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? ["up", ...pulumiArgs] : [...pulumiArgs]; const op = base[0]; const takesStackFlag = !!op && !OPS_WITHOUT_STACK_FLAG.has(op) && !(op === "stack" && STACK_SUBCOMMANDS_WITHOUT_FLAG.has(base[1] ?? "")); return takesStackFlag ? [...base, "-s", stack] : base; } export function parseStackFromArgs(args) { for (let i = 0; i < args.length; i++) { if (args[i] === "--stack" || args[i] === "-s") { return args[i + 1] ?? null; } if (args[i]?.startsWith("--stack=")) { return args[i].split("=")[1] ?? null; } } return null; } export function stripStackFromArgs(args) { for (let i = args.length - 1; i >= 0; i--) { if (args[i] === "--stack" || args[i] === "-s") { args.splice(i, 2); } else if (args[i]?.startsWith("--stack=")) { args.splice(i, 1); } } } export function registerDeployCommand(program) { program .command("deploy") .description("Run pulumi with the app's deployer role (passthrough)") .allowUnknownOption(true) .allowExcessArguments(true) .helpOption(false) .action(async (_opts, cmd) => { const pulumiArgs = cmd.args; const appRoot = findAppRoot(process.cwd()); if (!appRoot) { logError("No Pulumi.yaml found. Run from within a Pulumi app directory."); process.exit(1); } const stack = parseStackFromArgs(pulumiArgs) ?? getCurrentStack(appRoot); stripStackFromArgs(pulumiArgs); if (!stack) { logError("Could not determine stack. Either:\n" + " - Pass --stack <name> (e.g., mesh deploy up --stack dev-<you> --yes)\n" + " - Create a personal dev stack first: mesh stack init (it prints the exact --stack command)"); process.exit(1); } const op = pulumiArgs.length === 0 || pulumiArgs[0]?.startsWith("-") ? "up" : pulumiArgs[0]; assertDeployDepsFresh(appRoot, op ?? "up"); if (op === "up" || op === "destroy") { const wt = resolveWorktreeIdentity(appRoot); const isPersonal = readStackConfig(appRoot, stack, "mesh:deploy") === "false"; if (isPersonal && stackNeedsWorktreeIsolation(stack, wt)) { logWarn(`Stack "${stack}" carries no worktree token, but you're in linked worktree "${wt.slug}".\n` + ` Deploying it from multiple worktrees at once collides on SSM export paths\n` + ` (ParameterAlreadyExists) and the Temporal namespace. To isolate this worktree:\n` + ` mesh stack init --worktree\n` + ` Continuing with "${stack}" — pass --stack to target a different one.`); } } const credEnv = await resolvePulumiEnv({ appRoot, stack }); const env = { ...process.env, ...credEnv }; delete env.AWS_PROFILE; const finalArgs = buildPulumiArgs(pulumiArgs, stack); try { execFileSync("pulumi", finalArgs, { cwd: appRoot, env, stdio: "inherit", }); } catch (err) { process.exit(err.status ?? 1); } }); }