import { confirm, log } from "@clack/prompts" import { defineCommand } from "../lib/command" import { globalOptions, globalOptionsSchema } from "../lib/global-options" import { z } from "zod" import { apiClient } from "../lib/api-client" import { deploymentSessionIntro } from "../lib/deployment-lifecycle" import { accent, dimmed } from "../lib/io" import { clearSpinner, startSpinner } from "../lib/spinner" import { output, safelyPrompt } from "../utils" /** Options accepted by the deployment cancellation command. */ const cancelDeploymentOptsSchema = globalOptionsSchema.extend({ yes: z.boolean().default(false), positionals: z.tuple([z.string().min(1)]), }) export const deploymentCancelCommand = defineCommand({ name: "cancel", description: "Cancel an in-progress deployment", options: { ...globalOptions, yes: { type: "boolean", short: "y", default: false, }, }, optionDescriptions: { yes: "Skip confirmation prompt", }, positionals: [ { name: "deploymentId", description: "Deployment ID", required: true, }, ], schema: globalOptionsSchema.extend(cancelDeploymentOptsSchema.shape), run: cancelDeployment, }) /** * Cancels one eligible deployment after confirmation. * * @param opts - Deployment identity and confirmation preference. */ export async function cancelDeployment( opts: z.infer, ) { await deploymentSessionIntro() const [deploymentId] = opts.positionals startSpinner("Fetching deployment") const deployment = await apiClient.deployment.get({ deploymentId, }) clearSpinner() if ( !opts.yes && !(await safelyPrompt( () => confirm({ message: `Cancel deployment ${accent(deployment.id)} for ${accent(deployment.project.name)}?`, initialValue: false, }), "--yes", )) ) { output.json({ cancelled: true }) return } startSpinner("Cancelling deployment") await apiClient.deployment.cancel({ deploymentId: deployment.id }) clearSpinner() output .normal(() => log.success( `Cancelled deployment ${accent(deployment.id)} ${dimmed(deployment.project.name)}`, ), ) .json({ deploymentId: deployment.id, status: "cancelled" }) }