import { Command } from "@commander-js/extra-typings"; import { chalkStderr } from "chalk"; import { oneoffContext } from "../bundler/context.js"; import { logFinishedStep, logOutput, showSpinner } from "../bundler/log.js"; import { loadSelectedDeploymentCredentials } from "./lib/api.js"; import { actionDescription } from "./lib/command.js"; import { getDeploymentSelection } from "./lib/deploymentSelection.js"; import { changedEnvVarFile } from "./lib/envvars.js"; import { CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME, CONVEX_DEPLOY_KEY_ENV_VAR_NAME, ENV_VAR_FILE_PATH, typedPlatformClient, } from "./lib/utils/utils.js"; export const deploymentTokenCreate = new Command("create") .summary("Create an access token") .description( `Creates a deploy key that, when set as ${chalkStderr.bold(CONVEX_DEPLOY_KEY_ENV_VAR_NAME)}, scopes all commands to the target deployment. Defaults to the current deployment if '--deployment' isn't passed\n\n` + " Print a new deploy key to stdout: `npx convex deployment token create my-token`\n" + ` Save a new deploy key in ${ENV_VAR_FILE_PATH}: \`npx convex deployment token create my-token --save-env\`\n` + " Save a new deploy key in a custom env file: `npx convex deployment token create ci-token --save-env .env.production`\n" + " Create a key for the project's prod: `npx convex deployment token create ci-token --deployment prod`\n", ) .argument("", "Name for the new deploy key") .allowExcessArguments(false) .option( "--save-env [path]", `Save the new key as ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME} in an env file instead of printing it. Defaults to ${ENV_VAR_FILE_PATH}.`, ) .addDeploymentSelectionOptions(actionDescription("Create a deploy key for")) .showHelpAfterError() .action(async (name, options) => { const ctx = await oneoffContext(options); const auth = ctx.bigBrainAuth(); if (auth === null || auth.kind !== "accessToken") { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Creating a deploy key currently requires being logged in with a personal access token. ${ process.env[CONVEX_DEPLOY_KEY_ENV_VAR_NAME] ? `Unset ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME}` : process.env[CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME] ? `Unset ${CONVEX_DEPLOYMENT_TOKEN_ENV_VAR_NAME}` : `Run ${chalkStderr.bold("npx convex login")}` } and try again.`, }); } const deploymentSelection = await getDeploymentSelection(ctx, options); const deployment = await loadSelectedDeploymentCredentials( ctx, deploymentSelection, { ensureLocalRunning: false }, ); if (deployment.deploymentFields === null) { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: "Cannot create a deploy key for a self-hosted deployment.", }); } const { deploymentName, deploymentType } = deployment.deploymentFields; if (deploymentType === "local" || deploymentType === "anonymous") { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Cannot create a deploy key for a ${deploymentType} deployment.`, }); } showSpinner(`Creating deploy key for ${deploymentName}...`); const response = await typedPlatformClient(ctx).POST( "/deployments/{deployment_name}/create_deploy_key", { params: { path: { deployment_name: deploymentName } }, body: { name }, }, ); const deployKey = response.data!.deployKey; if (options.saveEnv === undefined) { logFinishedStep(`Created deploy key "${name}" for ${deploymentName}.`); logOutput(deployKey); return; } const envFile = typeof options.saveEnv === "string" ? options.saveEnv : ENV_VAR_FILE_PATH; const existingFileContent = ctx.fs.exists(envFile) ? ctx.fs.readUtf8File(envFile) : null; const updatedContent = changedEnvVarFile({ existingFileContent, envVarName: CONVEX_DEPLOY_KEY_ENV_VAR_NAME, envVarValue: deployKey, commentAfterValue: null, commentOnPreviousLine: null, }); if (updatedContent === null) { logFinishedStep( `Deploy key for ${deploymentName} already present in ${envFile}; no changes made.`, ); return; } ctx.fs.writeUtf8File(envFile, updatedContent); logFinishedStep( `Saved deploy key "${name}" for ${deploymentName} as ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME} in ${envFile}.`, ); });