convex
Version:
Client for the Convex Cloud
342 lines (336 loc) • 16 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var deploy_exports = {};
__export(deploy_exports, {
deploy: () => deploy
});
module.exports = __toCommonJS(deploy_exports);
var import_chalk = require("chalk");
var import_extra_typings = require("@commander-js/extra-typings");
var import_context = require("../bundler/context.js");
var import_log = require("../bundler/log.js");
var import_api = require("./lib/api.js");
var import_announceDeploymentTarget = require("./lib/announceDeploymentTarget.js");
var import_envvars = require("./lib/envvars.js");
var import_utils = require("./lib/utils/utils.js");
var import_run = require("./lib/run.js");
var import_usage = require("./lib/usage.js");
var import_deployment = require("./lib/deployment.js");
var import_components2 = require("./lib/components.js");
var import_prompts = require("./lib/utils/prompts.js");
var import_deploy2 = require("./lib/deploy2.js");
var import_deploymentSelection = require("./lib/deploymentSelection.js");
var import_deploymentSelection2 = require("./lib/deploymentSelection.js");
var import_updates = require("./lib/updates.js");
var import_config = require("./lib/config.js");
var import_workos = require("./lib/workos/workos.js");
var import_dashboard = require("./lib/dashboard.js");
var import_extractDeploymentNameForWorkOS = require("./lib/extractDeploymentNameForWorkOS.js");
const deploy = new import_extra_typings.Command("deploy").summary("Deploy to a production or preview deployment").description(
[
"Deploys code to a deployment.",
"This is typically used for deploying to a prod or preview deployment manually or from CI; to deploy to your dev deployment when developing, use `npx convex dev`.",
"",
"The target deployment is chosen like this:",
`\u2022 If the \`${import_utils.CONVEX_DEPLOYMENT_ENV_VAR_NAME}\` environment variable is set (typical during local development), the target is the project\u2019s default production deployment.`,
`\u2022 If the \`${import_utils.CONVEX_DEPLOY_KEY_ENV_VAR_NAME}\` environment variable is set (typical in CI), it is the deployment associated with that key.`,
` \u2022 When it\u2019s set to a preview deploy key, it will deploy to a preview deployment:`,
` \u2022 with the name of the current Git branch when running in CI (Vercel, Netlify, Cloudflare Pages, GitHub)`,
` \u2022 or with the name specified by the \`--preview-name\` or \`--preview-create\` flags`,
"",
"`npx convex deploy` will:",
" 1. Run a command if specified with `--cmd`, with the deployment URL available as an environment variable.",
" 2. Typecheck your Convex functions.",
" 3. Regenerate the generated code in the `convex/_generated` directory.",
" 4. Bundle your Convex functions and their dependencies.",
" 5. Push your functions, indexes, and schema to the deployment.",
" 6. When deploying to a preview deployment, it runs the function specified by `--preview-run`.",
"If any step fails, the next steps do not run."
].join("\n")
).allowExcessArguments(false).addDeployOptions().addOption(
new import_extra_typings.Option(
"--preview-run <functionName>",
"Function to run if deploying to a preview deployment. This is ignored if deploying to a production deployment."
)
).addOption(
new import_extra_typings.Option(
"--preview-name <name>",
"The name to associate with this preview deployment. Defaults to the current Git branch name in Vercel, Netlify, Cloudflare Pages and GitHub CI. Reuses the existing deployment if one exists."
).conflicts("preview-create")
).addOption(
new import_extra_typings.Option(
"--preview-create <name>",
"Like --preview-name, but deletes and recreates an existing preview deployment with the same name. This parameter can only be used with a preview deploy key (when used with another type of key, the command will return an error)."
).conflicts("preview-name")
).addOption(
new import_extra_typings.Option(
"--check-build-environment <mode>",
"Whether to check for a non-production build environment before deploying to a production Convex deployment."
).choices(["enable", "disable"]).default("enable").hideHelp()
).addOption(new import_extra_typings.Option("--admin-key <adminKey>").hideHelp()).addOption(new import_extra_typings.Option("--url <url>").hideHelp()).addOption(
new import_extra_typings.Option(
"--env-file <envFile>",
`Path to a custom file of environment variables, for choosing the deployment, e.g. ${import_utils.CONVEX_DEPLOYMENT_ENV_VAR_NAME} or ${import_utils.CONVEX_SELF_HOSTED_URL_VAR_NAME}. Same format as .env.local or .env files, and overrides them.`
)
).addOption(
new import_extra_typings.Option(
"--message <message>",
"Optional message to attach to this deployment in the audit log."
)
).addOption(
new import_extra_typings.Option(
"--skip-workos-check",
"Skip WorkOS AuthKit provisioning and credential checks during deploy."
).hideHelp()
).addOption(
new import_extra_typings.Option("--allow-deleting-large-indexes").hideHelp().conflicts("preview-create").conflicts("preview-name")
).showHelpAfterError().action(async (cmdOptions) => {
const ctx = await (0, import_context.oneoffContext)(cmdOptions);
const deploymentSelection = await (0, import_deploymentSelection.getDeploymentSelection)(ctx, {
...cmdOptions,
implicitProd: true
});
if (cmdOptions.checkBuildEnvironment === "enable" && (0, import_envvars.isNonProdBuildEnvironment)() && deploymentSelection.kind === "existingDeployment" && deploymentSelection.deploymentToActOn.source === "deployKey" && deploymentSelection.deploymentToActOn.deploymentFields?.deploymentType === "prod") {
await ctx.crash({
exitCode: 1,
errorType: "invalid filesystem data",
printedMessage: `Detected a non-production build environment and "${import_utils.CONVEX_DEPLOY_KEY_ENV_VAR_NAME}" for a production Convex deployment.
This is probably unintentional.
`
});
}
if (deploymentSelection.kind === "anonymous") {
(0, import_log.logMessage)(
"You are currently developing anonymously with a locally running project.\nTo deploy your Convex app to the cloud, log in by running `npx convex login`.\nSee https://docs.convex.dev/production for more information on how Convex cloud works and instructions on how to set up hosting."
);
return await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: null
});
}
if (deploymentSelection.kind === "preview") {
const previewName = cmdOptions.previewCreate ?? cmdOptions.previewName ?? (0, import_envvars.gitBranchFromEnvironment)();
const reuse = cmdOptions.previewCreate === void 0;
const teamAndProjectSlugs = await (0, import_deployment.getTeamAndProjectFromPreviewAdminKey)(
ctx,
deploymentSelection.previewDeployKey
);
await deployToNewPreviewDeployment(
ctx,
{
previewDeployKey: deploymentSelection.previewDeployKey,
projectSelection: {
kind: "teamAndProjectSlugs",
teamSlug: teamAndProjectSlugs.teamSlug,
projectSlug: teamAndProjectSlugs.projectSlug
}
},
{
...cmdOptions,
previewName: previewName ?? void 0,
reuse,
message: cmdOptions.message ?? (0, import_envvars.getDefaultDeployMessage)()
}
);
} else {
if (cmdOptions.previewCreate !== void 0) {
const source = deploymentSelection.kind === "deploymentWithinProject" && deploymentSelection.targetProject.kind === "deploymentName" ? `at ${import_chalk.chalkStderr.blue.underline(`${import_dashboard.DASHBOARD_HOST}/dp/${deploymentSelection.targetProject.deploymentName}/settings#preview-deploy-keys`)}` : deploymentSelection.kind === "existingDeployment" && deploymentSelection.deploymentToActOn.deploymentFields !== null ? `at ${import_chalk.chalkStderr.blue.underline(`${import_dashboard.DASHBOARD_HOST}/dp/${deploymentSelection.deploymentToActOn.deploymentFields.deploymentName}/settings#preview-deploy-keys`)}` : "on the dashboard";
await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: `Preview deployments can only be created with preview deploy keys. Generate a preview deploy key ${source} and set the ${import_chalk.chalkStderr.bold(`CONVEX_DEPLOY_KEY`)} environment variable with it.`
});
}
await deployToExistingDeployment(ctx, deploymentSelection, {
...cmdOptions,
skipWorkosCheck: cmdOptions.skipWorkosCheck ?? false,
allowDeletingLargeIndexes: cmdOptions.allowDeletingLargeIndexes ?? false,
message: cmdOptions.message ?? (0, import_envvars.getDefaultDeployMessage)()
});
}
});
async function deployToNewPreviewDeployment(ctx, deploymentSelection, options) {
const previewName = options.previewName ?? null;
if (previewName === null) {
await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: "`npx convex deploy` to a preview deployment could not determine the preview name. Provide one using `--preview-name`"
});
}
if (options.dryRun) {
(0, import_log.logFinishedStep)(
`Would have claimed preview deployment for "${previewName}"`
);
await (0, import_deploy2.runCommand)(ctx, {
cmdUrlEnvVarName: options.cmdUrlEnvVarName,
cmd: options.cmd,
dryRun: !!options.dryRun,
url: "https://<PREVIEW DEPLOYMENT>.convex.cloud",
adminKey: "preview-deployment-admin-key"
});
(0, import_log.logFinishedStep)(
`Would have deployed Convex functions to preview deployment for "${previewName}"`
);
if (options.previewRun !== void 0) {
(0, import_log.logMessage)(`Would have run function "${options.previewRun}"`);
}
return;
}
const data = await (0, import_utils.bigBrainAPI)({
ctx,
method: "POST",
path: "claim_preview_deployment",
data: {
projectSelection: deploymentSelection.projectSelection,
identifier: previewName,
reuse: options.reuse
}
});
const previewAdminKey = data.adminKey;
const previewUrl = data.instanceUrl;
const deploymentNameForWorkOS = (0, import_extractDeploymentNameForWorkOS.extractDeploymentNameForWorkOS)(previewUrl);
(0, import_announceDeploymentTarget.announceDeploymentTarget)("Deploying code to deployment:", {
url: previewUrl,
deploymentFields: {
deploymentName: data.deploymentName,
deploymentType: "preview",
teamSlug: deploymentSelection.projectSelection.teamSlug,
projectSlug: deploymentSelection.projectSelection.projectSlug,
reference: data.reference,
// Preview deployments are never default
isDefault: false
}
});
const { projectConfig } = await (0, import_config.readProjectConfig)(ctx);
const authKitConfig = await (0, import_config.getAuthKitConfig)(ctx, projectConfig);
if (authKitConfig && deploymentNameForWorkOS && !options.skipWorkosCheck) {
await (0, import_workos.ensureAuthKitProvisionedBeforeBuild)(
ctx,
deploymentNameForWorkOS,
{ deploymentUrl: previewUrl, adminKey: previewAdminKey },
"preview"
);
}
await (0, import_deploy2.runCommand)(ctx, {
...options,
url: previewUrl,
adminKey: previewAdminKey
});
const pushOptions = {
deploymentName: null,
adminKey: previewAdminKey,
verbose: !!options.verbose,
dryRun: false,
typecheck: options.typecheck,
typecheckComponents: options.typecheckComponents,
debug: !!options.debug,
debugBundlePath: options.debugBundlePath,
debugNodeApis: false,
codegen: options.codegen === "enable",
url: previewUrl,
liveComponentSources: false,
pushAllModules: !!options.pushAllModules,
largeIndexDeletionCheck: "no verification",
// fine for preview deployments
message: options.message
};
(0, import_log.showSpinner)(`Deploying to ${previewUrl}...`);
await (0, import_components2.runPush)(ctx, pushOptions);
(0, import_log.logFinishedStep)(`Deployed Convex functions to ${previewUrl}`);
if (options.previewRun !== void 0 && data.isNewDeployment) {
await (0, import_run.runFunctionAndLog)(ctx, {
deploymentUrl: previewUrl,
adminKey: previewAdminKey,
functionName: options.previewRun,
argsString: "{}",
componentPath: void 0,
callbacks: {
onSuccess: () => {
(0, import_log.logFinishedStep)(`Finished running function "${options.previewRun}"`);
}
}
});
}
}
async function deployToExistingDeployment(ctx, deploymentSelection, options) {
const deploymentToActOn = await (0, import_api.loadSelectedDeploymentCredentials)(
ctx,
deploymentSelection
);
(0, import_announceDeploymentTarget.announceDeploymentTarget)("Deploying code to deployment:", deploymentToActOn);
const { deploymentFields } = deploymentToActOn;
const configuredDeployment = (0, import_deploymentSelection2.deploymentNameAndTypeFromSelection)(deploymentSelection);
if (configuredDeployment !== null && configuredDeployment.name !== null) {
const shouldPushToProd = configuredDeployment.name === deploymentFields?.deploymentName || (options.yes ?? await askToConfirmPush(
ctx,
{
configuredName: configuredDeployment.name,
configuredType: configuredDeployment.type,
requestedName: deploymentFields?.deploymentName,
requestedType: deploymentFields?.deploymentType
},
deploymentToActOn.url
));
if (!shouldPushToProd) {
await ctx.crash({
exitCode: 1,
printedMessage: null,
errorType: "fatal"
});
}
}
const isCloudDeployment = deploymentFields !== null;
await Promise.all([
(0, import_deploy2.deployToDeployment)(
ctx,
{
url: deploymentToActOn.url,
adminKey: deploymentToActOn.adminKey,
deploymentName: deploymentFields?.deploymentName ?? null,
...deploymentFields?.deploymentType !== void 0 ? { deploymentType: deploymentFields.deploymentType } : {}
},
{ ...options, skipWorkosCheck: options.skipWorkosCheck }
),
...isCloudDeployment ? [
(0, import_usage.usageStateWarning)(ctx, deploymentFields.deploymentName),
(0, import_updates.checkVersionAndAiFilesStaleness)(ctx)
] : []
]);
}
async function askToConfirmPush(ctx, deployment, prodUrl) {
(0, import_log.logMessage)(
`You're currently developing against your ${import_chalk.chalkStderr.bold(
deployment.configuredType ?? "dev"
)} deployment
${deployment.configuredName} (set in CONVEX_DEPLOYMENT)
Your ${import_chalk.chalkStderr.bold(deployment.requestedType)} deployment ${import_chalk.chalkStderr.bold(
deployment.requestedName
)} serves traffic at:
${(await (0, import_envvars.suggestedEnvVarNames)(ctx)).convexUrlEnvVar}=${import_chalk.chalkStderr.bold(prodUrl)}
Make sure that your published client is configured with this URL (for instructions see https://docs.convex.dev/hosting)
`
);
return (0, import_prompts.promptYesNo)(ctx, {
message: `Do you want to push your code to your ${deployment.requestedType} deployment ${deployment.requestedName} now?`,
default: true
});
}
//# sourceMappingURL=deploy.js.map