111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import { defineCommand } from "citty";
|
|
import { ctx } from "../lib/context";
|
|
import { addAppToCompose, type AppOptions } from "../lib/scaffold";
|
|
import { readCompose, resolveComposeFile } from "../lib/yaml";
|
|
import {
|
|
resolveScaffoldInput,
|
|
terminalPrompt,
|
|
type AppInput,
|
|
type ScaffoldPrompt,
|
|
} from "../lib/scaffold-prompts";
|
|
|
|
export { terminalPrompt, type AppInput, type ScaffoldPrompt };
|
|
|
|
export async function resolveAppInput(
|
|
root: string,
|
|
input: AppInput,
|
|
prompt: ScaffoldPrompt = terminalPrompt,
|
|
composeFile?: string,
|
|
): Promise<AppOptions> {
|
|
const file = composeFile ?? (await resolveComposeFile(root));
|
|
const existingServices = file
|
|
? Object.keys((await readCompose(file)).services ?? {})
|
|
: [];
|
|
return (await resolveScaffoldInput(root, input, prompt, { existingServices }))
|
|
.app;
|
|
}
|
|
|
|
export const appArgs = {
|
|
name: { type: "string" as const, description: "Service name" },
|
|
path: {
|
|
type: "string" as const,
|
|
description: "App directory relative to the project",
|
|
},
|
|
source: {
|
|
type: "string" as const,
|
|
description: "image, auto (Buildpacks), or template",
|
|
},
|
|
image: {
|
|
type: "string" as const,
|
|
description: "Image reference when source=image",
|
|
},
|
|
cnb: {
|
|
type: "string" as const,
|
|
description: "Optional CNB URI when source=auto",
|
|
},
|
|
template: {
|
|
type: "string" as const,
|
|
description: "Dockerfile template ID when source=template",
|
|
},
|
|
postgres: {
|
|
type: "string" as const,
|
|
description: "Postgres claim name or user/database",
|
|
},
|
|
s3: { type: "string" as const, description: "S3 claim name or key/bucket" },
|
|
cpu: { type: "string" as const, description: "CPU limit in cores" },
|
|
memory: { type: "string" as const, description: "Memory limit, e.g. 512m" },
|
|
replicas: { type: "string" as const, description: "Replica count" },
|
|
"non-interactive": {
|
|
type: "boolean" as const,
|
|
description: "Use defaults and require explicit choices when ambiguous",
|
|
},
|
|
};
|
|
|
|
export function appInputFromArgs(args: Record<string, unknown>): AppInput {
|
|
return {
|
|
...Object.fromEntries(
|
|
[
|
|
"name",
|
|
"path",
|
|
"source",
|
|
"image",
|
|
"template",
|
|
"cnb",
|
|
"postgres",
|
|
"s3",
|
|
"cpu",
|
|
"memory",
|
|
]
|
|
.filter((key) => args[key] !== undefined)
|
|
.map((key) => [key, String(args[key])]),
|
|
),
|
|
...(args.replicas !== undefined ? { replicas: Number(args.replicas) } : {}),
|
|
nonInteractive: Boolean(args["non-interactive"]),
|
|
};
|
|
}
|
|
|
|
export const add = defineCommand({
|
|
meta: { name: "add", description: "Add a resource to this project" },
|
|
subCommands: {
|
|
app: defineCommand({
|
|
meta: {
|
|
name: "app",
|
|
description: "Add a service without changing existing services",
|
|
},
|
|
args: appArgs,
|
|
async run({ args }) {
|
|
const { cwd, path } = ctx();
|
|
const file = await path();
|
|
const app = await resolveAppInput(
|
|
cwd,
|
|
appInputFromArgs(args),
|
|
terminalPrompt,
|
|
file,
|
|
);
|
|
const composePath = await addAppToCompose(cwd, app, file);
|
|
console.log(`Added ${app.name} to ${composePath}`);
|
|
},
|
|
}),
|
|
},
|
|
});
|