Skip to content

Commit 103456b

Browse files
committed
feat: preview-create becomes preview-deploy to allow updating of preview environments rather than always creating new ones
1 parent 942b909 commit 103456b

File tree

1 file changed

+119
-21
lines changed

1 file changed

+119
-21
lines changed

src/cli/deploy.ts

Lines changed: 119 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import {
1717
CONVEX_SELF_HOSTED_URL_VAR_NAME,
1818
CONVEX_DEPLOYMENT_ENV_VAR_NAME,
1919
bigBrainAPI,
20+
bigBrainAPIMaybeThrows,
21+
logAndHandleFetchError,
22+
ThrowingFetchError,
2023
} from "./lib/utils/utils.js";
2124
import { runFunctionAndLog } from "./lib/run.js";
2225
import { usageStateWarning } from "./lib/usage.js";
@@ -27,6 +30,49 @@ import { deployToDeployment, runCommand } from "./lib/deploy2.js";
2730
import { getDeploymentSelection } from "./lib/deploymentSelection.js";
2831
import { deploymentNameAndTypeFromSelection } from "./lib/deploymentSelection.js";
2932
import { checkVersion } from "./lib/updates.js";
33+
34+
async function checkPreviewDeploymentExists(
35+
ctx: Context,
36+
previewName: string,
37+
projectSelection: {
38+
kind: "teamAndProjectSlugs";
39+
teamSlug: string;
40+
projectSlug: string;
41+
},
42+
): Promise<{
43+
adminKey: string;
44+
url: string;
45+
deploymentName: string;
46+
} | null> {
47+
try {
48+
const result = await bigBrainAPIMaybeThrows({
49+
ctx,
50+
method: "POST",
51+
url: "deployment/authorize_preview",
52+
data: {
53+
previewName: previewName,
54+
projectSelection: projectSelection,
55+
},
56+
});
57+
return {
58+
adminKey: result.adminKey,
59+
url: result.url,
60+
deploymentName: result.deploymentName,
61+
};
62+
} catch (err) {
63+
// Only return null for the specific "PreviewNotFound" code
64+
const isNotFoundError =
65+
err instanceof ThrowingFetchError &&
66+
err.response.status === 404 &&
67+
err.serverErrorData?.code === "PreviewNotFound";
68+
if (isNotFoundError) {
69+
return null;
70+
}
71+
72+
return await logAndHandleFetchError(ctx, err);
73+
}
74+
}
75+
3076
export const deploy = new Command("deploy")
3177
.summary("Deploy to your prod deployment")
3278
.description(
@@ -43,8 +89,8 @@ export const deploy = new Command("deploy")
4389
)
4490
.addOption(
4591
new Option(
46-
"--preview-create <name>",
47-
"The name to associate with this deployment if deploying to a newly created preview deployment. Defaults to the current Git branch name in Vercel, Netlify and GitHub CI. This is ignored if deploying to a production deployment.",
92+
"--preview-deploy <name>",
93+
"The name to associate with this deployment if deploying to a preview deployment. Creates a new preview deployment if it doesn't exist, or updates an existing one. Defaults to the current Git branch name in Vercel, Netlify and GitHub CI. This is ignored if deploying to a production deployment.",
4894
).conflicts("preview-name"),
4995
)
5096
.addOption(
@@ -59,13 +105,21 @@ export const deploy = new Command("deploy")
59105
// Hidden options to pass in admin key and url for tests and local development
60106
.addOption(new Option("--admin-key <adminKey>").hideHelp())
61107
.addOption(new Option("--url <url>").hideHelp())
108+
.addOption(
109+
new Option(
110+
"--preview-create <name>",
111+
"[deprecated] Use `--preview-deploy` instead. The name to associate with this deployment if deploying to a newly created preview deployment.",
112+
)
113+
.hideHelp()
114+
.conflicts("preview-deploy"),
115+
)
62116
.addOption(
63117
new Option(
64118
"--preview-name <name>",
65-
"[deprecated] Use `--preview-create` instead. The name to associate with this deployment if deploying to a preview deployment.",
119+
"[deprecated] Use `--preview-deploy` instead. The name to associate with this deployment if deploying to a preview deployment.",
66120
)
67121
.hideHelp()
68-
.conflicts("preview-create"),
122+
.conflicts("preview-deploy"),
69123
)
70124
.addOption(
71125
new Option(
@@ -113,39 +167,83 @@ Same format as .env.local or .env files, and overrides them.`,
113167
if (deploymentSelection.kind === "preview") {
114168
// TODO -- add usage state warnings here too once we can do it without a deployment name
115169
// await usageStateWarning(ctx);
170+
if (cmdOptions.previewCreate !== undefined) {
171+
await ctx.crash({
172+
exitCode: 1,
173+
errorType: "fatal",
174+
printedMessage:
175+
"The `--preview-create` flag has been deprecated in favor of `--preview-deploy`. Please re-run the command using `--preview-deploy` instead.",
176+
});
177+
}
116178
if (cmdOptions.previewName !== undefined) {
117179
await ctx.crash({
118180
exitCode: 1,
119181
errorType: "fatal",
120182
printedMessage:
121-
"The `--preview-name` flag has been deprecated in favor of `--preview-create`. Please re-run the command using `--preview-create` instead.",
183+
"The `--preview-name` flag has been deprecated in favor of `--preview-deploy`. Please re-run the command using `--preview-deploy` instead.",
184+
});
185+
}
186+
187+
const previewName =
188+
cmdOptions.previewDeploy ?? gitBranchFromEnvironment();
189+
if (previewName === null) {
190+
await ctx.crash({
191+
exitCode: 1,
192+
errorType: "fatal",
193+
printedMessage:
194+
"`npx convex deploy` to a preview deployment could not determine the preview name. Provide one using `--preview-deploy`",
122195
});
123196
}
124197

125198
const teamAndProjectSlugs = await getTeamAndProjectFromPreviewAdminKey(
126199
ctx,
127200
deploymentSelection.previewDeployKey,
128201
);
129-
await deployToNewPreviewDeployment(
202+
203+
const existingPreview = await checkPreviewDeploymentExists(
130204
ctx,
205+
previewName!, // checked above
131206
{
132-
previewDeployKey: deploymentSelection.previewDeployKey,
133-
projectSelection: {
134-
kind: "teamAndProjectSlugs",
135-
teamSlug: teamAndProjectSlugs.teamSlug,
136-
projectSlug: teamAndProjectSlugs.projectSlug,
137-
},
138-
},
139-
{
140-
...cmdOptions,
207+
kind: "teamAndProjectSlugs",
208+
teamSlug: teamAndProjectSlugs.teamSlug,
209+
projectSlug: teamAndProjectSlugs.projectSlug,
141210
},
142211
);
212+
213+
if (existingPreview) {
214+
logMessage(
215+
`Preview deployment "${previewName}" exists. Updating deployment.`,
216+
);
217+
await deployToExistingDeployment(ctx, {
218+
...cmdOptions,
219+
adminKey: existingPreview.adminKey,
220+
url: existingPreview.url,
221+
});
222+
} else {
223+
logMessage(
224+
`Preview deployment "${previewName}" does not exist. Creating new deployment.`,
225+
);
226+
await deployToPreviewDeployment(
227+
ctx,
228+
{
229+
previewDeployKey: deploymentSelection.previewDeployKey,
230+
projectSelection: {
231+
kind: "teamAndProjectSlugs",
232+
teamSlug: teamAndProjectSlugs.teamSlug,
233+
projectSlug: teamAndProjectSlugs.projectSlug,
234+
},
235+
},
236+
{
237+
...cmdOptions,
238+
},
239+
);
240+
}
143241
} else {
144242
await deployToExistingDeployment(ctx, cmdOptions);
145243
}
146244
});
147245

148-
async function deployToNewPreviewDeployment(
246+
async function deployToPreviewDeployment(
149247
ctx: Context,
150248
deploymentSelection: {
151249
previewDeployKey: string;
@@ -157,7 +255,7 @@ async function deployToNewPreviewDeployment(
157255
},
158256
options: {
159257
dryRun?: boolean | undefined;
160-
previewCreate?: string | undefined;
258+
previewDeploy?: string | undefined;
161259
previewRun?: string | undefined;
162260
cmdUrlEnvVarName?: string | undefined;
163261
cmd?: string | undefined;
@@ -170,19 +268,19 @@ async function deployToNewPreviewDeployment(
170268
debugBundlePath?: string | undefined;
171269
},
172270
) {
173-
const previewName = options.previewCreate ?? gitBranchFromEnvironment();
271+
const previewName = options.previewDeploy ?? gitBranchFromEnvironment();
174272
if (previewName === null) {
175273
await ctx.crash({
176274
exitCode: 1,
177275
errorType: "fatal",
178276
printedMessage:
179-
"`npx convex deploy` to a preview deployment could not determine the preview name. Provide one using `--preview-create`",
277+
"`npx convex deploy` to a preview deployment could not determine the preview name. Provide one using `--preview-deploy`",
180278
});
181279
}
182280

183281
if (options.dryRun) {
184282
logFinishedStep(
185-
`Would have claimed preview deployment for "${previewName}"`,
283+
`Would have deployed to preview deployment "${previewName}"`,
186284
);
187285
await runCommand(ctx, {
188286
cmdUrlEnvVarName: options.cmdUrlEnvVarName,
@@ -192,7 +290,7 @@ async function deployToNewPreviewDeployment(
192290
adminKey: "preview-deployment-admin-key",
193291
});
194292
logFinishedStep(
195-
`Would have deployed Convex functions to preview deployment for "${previewName}"`,
293+
`Would have deployed Convex functions to preview deployment "${previewName}"`,
196294
);
197295
if (options.previewRun !== undefined) {
198296
logMessage(`Would have run function "${options.previewRun}"`);

0 commit comments

Comments
 (0)