UNPKG

convex

Version:

Client for the Convex Cloud

228 lines (220 loc) 8.36 kB
import { Command } from "@commander-js/extra-typings"; import { chalkStderr } from "chalk"; import { Context, oneoffContext } from "../bundler/context.js"; import { DeploymentSelectionOptions, DetailedDeploymentCredentials, loadSelectedDeploymentCredentials, } from "./lib/api.js"; import { actionDescription } from "./lib/command.js"; import { ensureHasConvexDependency } from "./lib/utils/utils.js"; import { deploymentEnvBackend, envGet, envList, envRemove, envSet, } from "./lib/env.js"; import { getDeploymentSelection } from "./lib/deploymentSelection.js"; import { withRunningBackend } from "./lib/localDeployment/run.js"; import { envDefault } from "./envDefault.js"; const envSetCmd = new Command("set") // Pretend value is required .usage("[options] <name> <value>") .argument("[name]", "The name of the environment variable to set.") .argument( "[value]", "The value to set the variable to. Omit to set it interactively.", ) .summary("Set a variable") .description( [ "Set environment variables on your deployment.", "", "• `npx convex env set NAME 'value'`", "• `npx convex env set NAME # omit a value to set one interactively`", "• `npx convex env set NAME --from-file value.txt`", "• `npx convex env set --from-file .env.defaults`", "", "When setting multiple values, it will refuse all changes if any variables are already set to different values by default. Pass --force to overwrite the provided values.", "", "To keep secrets out of your shell history, omit the value to pipe it in via stdin, for instance:", "• `pbpaste | npx convex env set API_KEY` (macOS)", "• `Get-Clipboard | npx convex env set API_KEY` (Windows PowerShell)", "", "To update many variables at once, save them with `npx convex env list > .env.convex`, edit the file, then reapply the changes with `npx convex env set --force < .env.convex`.", ].join("\n"), ) .option( "--from-file <file>", "Read environment variables from a .env file. Without --force, fails if any existing variable has a different value.", ) .option( "--force", "When setting multiple variables, overwrite existing environment variable values instead of failing on mismatch.", ) .configureHelp({ showGlobalOptions: true }) .allowExcessArguments(false) .action(async (name, value, cmdOptions, cmd) => { // Note: We use `as` here because optsWithGlobals() type inference doesn't // include global options from the parent command (added via addDeploymentSelectionOptions) const options = cmd.optsWithGlobals() as DeploymentSelectionOptions; const { ctx, deployment } = await selectEnvDeployment(options); await ensureHasConvexDependency(ctx, "env set"); await withRunningBackend({ ctx, deployment, action: async () => { const backend = deploymentEnvBackend(ctx, deployment); const didAnything = await envSet(ctx, backend, name, value, cmdOptions); if (didAnything === false) { cmd.outputHelp({ error: true }); return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: "error: No environment variables specified to be set.", }); } }, }); }); export async function selectEnvDeployment( options: DeploymentSelectionOptions, ): Promise<{ ctx: Context; deployment: { deploymentUrl: string; adminKey: string; deploymentNotice: string; deploymentFields: DetailedDeploymentCredentials["deploymentFields"]; }; }> { const ctx = await oneoffContext(options); const deploymentSelection = await getDeploymentSelection(ctx, options); const { adminKey, url: deploymentUrl, deploymentFields, } = await loadSelectedDeploymentCredentials(ctx, deploymentSelection, { ensureLocalRunning: false, }); const deploymentNotice = deploymentFields !== null ? ` (on ${chalkStderr.bold(deploymentFields.deploymentType)} deployment ${chalkStderr.bold(deploymentFields.deploymentName)})` : ""; const result = { ctx, deployment: { deploymentUrl, adminKey, deploymentNotice, deploymentFields, }, }; return result; } const envGetCmd = new Command("get") .argument("<name>", "The name of the environment variable to print.") .summary("Print a variable's value") .description("Print a variable's value: `npx convex env get NAME`") .configureHelp({ showGlobalOptions: true }) .allowExcessArguments(false) .action(async (envVarName, _options, cmd) => { const options = cmd.optsWithGlobals(); const { ctx, deployment } = await selectEnvDeployment(options); await ensureHasConvexDependency(ctx, "env get"); await withRunningBackend({ ctx, deployment, action: async () => { const backend = deploymentEnvBackend(ctx, deployment); await envGet(ctx, backend, envVarName); }, }); }); const envRemoveCmd = new Command("remove") .alias("rm") .alias("unset") .argument("<name>", "The name of the environment variable to unset.") .summary("Unset a variable") .description( "Unset a variable: `npx convex env remove NAME`\n" + "If the variable doesn't exist, the command doesn't do anything and succeeds.", ) .configureHelp({ showGlobalOptions: true }) .allowExcessArguments(false) .action(async (name, _options, cmd) => { const options = cmd.optsWithGlobals(); const { ctx, deployment } = await selectEnvDeployment(options); await ensureHasConvexDependency(ctx, "env remove"); await withRunningBackend({ ctx, deployment, action: async () => { const backend = deploymentEnvBackend(ctx, deployment); await envRemove(ctx, backend, name); }, }); }); const envListCmd = new Command("list") .summary("List all environment variables and their values") .description( [ "• List all variables and their values: `npx convex env list`", "• List only variable names (no values): `npx convex env list --names-only`", "• Save all variables to a file: `npx convex env list > .env.convex`", "• Append to a file: `npx convex env list >> .env.convex`", ].join("\n"), ) .option( "--names-only", "List only the names of environment variables, without their values", ) .configureHelp({ showGlobalOptions: true }) .allowExcessArguments(false) .action(async (cmdOptions, cmd) => { // Note: We use `as` here because optsWithGlobals() type inference doesn't // include global options from the parent command (added via addDeploymentSelectionOptions) const options = cmd.optsWithGlobals() as DeploymentSelectionOptions; const { ctx, deployment } = await selectEnvDeployment(options); await ensureHasConvexDependency(ctx, "env list"); await withRunningBackend({ ctx, deployment, action: async () => { const backend = deploymentEnvBackend(ctx, deployment); await envList(ctx, backend, { namesOnly: cmdOptions.namesOnly ?? false, }); }, }); }); export const env = new Command("env") .summary("Set and view environment variables") .description( [ "Set and view environment variables on your deployment", "", "• Set a variable: `npx convex env set NAME 'value'`", "• Set interactively: `npx convex env set NAME`", "• Set multiple from file: `npx convex env set --from-file .env`", "• Unset a variable: `npx convex env remove NAME`", "• List all variables and their values: `npx convex env list`", "• List only variable names (no values): `npx convex env list --names-only`", "• Print a variable's value: `npx convex env get NAME`", "", "By default, this sets and views variables on your dev deployment.", "", "See the environment variables guide (https://docs.convex.dev/production/environment-variables) to learn more.", ].join("\n"), ) .addCommand(envSetCmd) .addCommand(envGetCmd) .addCommand(envRemoveCmd) .addCommand(envListCmd) .addCommand(envDefault) .helpCommand(false) .addDeploymentSelectionOptions( actionDescription("Set and view environment variables on"), );