diff --git a/.server-changes/vercel-auto-promote-toggle.md b/.server-changes/vercel-auto-promote-toggle.md new file mode 100644 index 00000000000..bb5f25a21c1 --- /dev/null +++ b/.server-changes/vercel-auto-promote-toggle.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: feature +--- + +Vercel integration option to disable auto promotions diff --git a/apps/webapp/app/components/integrations/VercelBuildSettings.tsx b/apps/webapp/app/components/integrations/VercelBuildSettings.tsx index 3a2da69d7ee..d665a53f1a1 100644 --- a/apps/webapp/app/components/integrations/VercelBuildSettings.tsx +++ b/apps/webapp/app/components/integrations/VercelBuildSettings.tsx @@ -21,6 +21,8 @@ type BuildSettingsFieldsProps = { envVarsConfigLink?: string; /** Slugs that should be forced off and disabled, with tooltip reason. */ disabledEnvSlugs?: Partial>; + autoPromote?: boolean; + onAutoPromoteChange?: (value: boolean) => void; }; export function BuildSettingsFields({ @@ -33,6 +35,8 @@ export function BuildSettingsFields({ onAtomicBuildsChange, envVarsConfigLink, disabledEnvSlugs, + autoPromote, + onAutoPromoteChange, }: BuildSettingsFieldsProps) { const isSlugDisabled = (slug: EnvSlug) => !!disabledEnvSlugs?.[slug]; const enabledSlugs = availableEnvSlugs.filter((s) => !isSlugDisabled(s)); @@ -202,6 +206,25 @@ export function BuildSettingsFields({ . + + {/* Auto promotion — only visible when atomic deployments are on */} + {atomicBuilds.includes("prod") && onAutoPromoteChange !== undefined && ( +
+
+ + +
+ + When enabled, the integration automatically promotes the Vercel deployment after + the Trigger.dev build completes. Turn off to manually promote from your Vercel + dashboard — Trigger.dev will then promote automatically once you do. + +
+ )} ); } diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx index d1c4715a1c6..d6131083bdf 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx @@ -98,6 +98,7 @@ const UpdateVercelConfigFormSchema = z.object({ pullEnvVarsBeforeBuild: envSlugArrayField, discoverEnvVars: envSlugArrayField, vercelStagingEnvironment: z.string().nullable().optional(), + autoPromote: z.string().optional().transform((val) => val !== "false"), }); const DisconnectVercelFormSchema = z.object({ @@ -241,6 +242,7 @@ export async function action({ request, params }: ActionFunctionArgs) { pullEnvVarsBeforeBuild, discoverEnvVars, vercelStagingEnvironment, + autoPromote, } = submission.value; const parsedStagingEnv = parseVercelStagingEnvironment(vercelStagingEnvironment); @@ -256,6 +258,7 @@ export async function action({ request, params }: ActionFunctionArgs) { pullEnvVarsBeforeBuild, discoverEnvVars, vercelStagingEnvironment: parsedStagingEnv, + autoPromote, }); if (result) { @@ -593,12 +596,14 @@ function ConnectedVercelProjectForm({ discoverEnvVars: connectedProject.integrationData.config.discoverEnvVars ?? [], vercelStagingEnvironment: connectedProject.integrationData.config.vercelStagingEnvironment ?? null, + autoPromote: connectedProject.integrationData.config.autoPromote ?? true, }); const originalAtomicBuilds = connectedProject.integrationData.config.atomicBuilds ?? []; const originalPullEnvVars = connectedProject.integrationData.config.pullEnvVarsBeforeBuild ?? []; const originalDiscoverEnvVars = connectedProject.integrationData.config.discoverEnvVars ?? []; const originalStagingEnv = connectedProject.integrationData.config.vercelStagingEnvironment ?? null; + const originalAutoPromote = connectedProject.integrationData.config.autoPromote ?? true; useEffect(() => { const atomicBuildsChanged = @@ -611,9 +616,23 @@ function ConnectedVercelProjectForm({ JSON.stringify([...configValues.discoverEnvVars].sort()) !== JSON.stringify([...originalDiscoverEnvVars].sort()); const stagingEnvChanged = configValues.vercelStagingEnvironment?.environmentId !== originalStagingEnv?.environmentId; - - setHasConfigChanges(atomicBuildsChanged || pullEnvVarsChanged || discoverEnvVarsChanged || stagingEnvChanged); - }, [configValues, originalAtomicBuilds, originalPullEnvVars, originalDiscoverEnvVars, originalStagingEnv]); + const autoPromoteChanged = configValues.autoPromote !== originalAutoPromote; + + setHasConfigChanges( + atomicBuildsChanged || + pullEnvVarsChanged || + discoverEnvVarsChanged || + stagingEnvChanged || + autoPromoteChanged + ); + }, [ + configValues, + originalAtomicBuilds, + originalPullEnvVars, + originalDiscoverEnvVars, + originalStagingEnv, + originalAutoPromote, + ]); const [configForm, fields] = useForm({ id: "update-vercel-config", @@ -718,6 +737,11 @@ function ConnectedVercelProjectForm({ name="vercelStagingEnvironment" value={configValues.vercelStagingEnvironment ? JSON.stringify(configValues.vercelStagingEnvironment) : ""} /> +
@@ -791,6 +815,10 @@ function ConnectedVercelProjectForm({ } envVarsConfigLink={`/orgs/${organizationSlug}/projects/${projectSlug}/env/${environmentSlug}/environment-variables`} disabledEnvSlugs={disabledEnvSlugsForBuildSettings} + autoPromote={configValues.autoPromote} + onAutoPromoteChange={(value) => + setConfigValues((prev) => ({ ...prev, autoPromote: value })) + } /> {/* Warning: autoAssignCustomDomains must be disabled for atomic deployments */} diff --git a/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts b/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts index 9e39febe0ab..95cff1a480e 100644 --- a/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts +++ b/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts @@ -48,6 +48,7 @@ export const VercelIntegrationConfigSchema = z.object({ displayName: z.string(), }).nullable().optional(), discoverEnvVars: z.array(EnvSlugSchema).nullable().optional(), + autoPromote: z.boolean().optional().default(true), }); export type VercelIntegrationConfig = z.infer; @@ -89,6 +90,7 @@ export function createDefaultVercelIntegrationData( pullEnvVarsBeforeBuild: ["prod", "preview"], discoverEnvVars: ["prod", "preview"], vercelStagingEnvironment: null, + autoPromote: true, }, syncEnvVarsMapping: {}, vercelProjectId,