@@ -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" ;
2124import { runFunctionAndLog } from "./lib/run.js" ;
2225import { usageStateWarning } from "./lib/usage.js" ;
@@ -27,6 +30,49 @@ import { deployToDeployment, runCommand } from "./lib/deploy2.js";
2730import { getDeploymentSelection } from "./lib/deploymentSelection.js" ;
2831import { deploymentNameAndTypeFromSelection } from "./lib/deploymentSelection.js" ;
2932import { 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+
3076export 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