@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
95 lines (94 loc) • 4.09 kB
JavaScript
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);
}
});
}