Skip to content

Commit 494d1bd

Browse files
committed
feat(vercel): automatic version skew protection at connect + atomic deployments deprecation
Connecting a Vercel project now writes TRIGGER_AUTOMATIC_SKEW_VERSION_PROTECTION=1 (plain, create-if-absent only - an existing value, including "0", is never touched; presence is target-containment aware, branch-scoped records do not count, a truncated env listing skips the write). The onboarding wizard no longer offers automatic atomic deployments (default off); the settings row is labelled Deprecated and enabling it requires confirming a dialog that points to task version skew protection and the docs (TRI-13001).
1 parent c5c2ea9 commit 494d1bd

10 files changed

Lines changed: 500 additions & 58 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
New Vercel connections now get version skew protection turned on automatically, so each run uses the task version its deployment shipped with. Automatic atomic deployments are deprecated and no longer offered when you connect a project, but stay available in your Vercel integration settings.

apps/webapp/app/components/integrations/VercelBuildSettings.tsx

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Switch } from "~/components/primitives/Switch";
22
import { LinkButton } from "~/components/primitives/Buttons";
3+
import { Badge } from "~/components/primitives/Badge";
34
import { Label } from "~/components/primitives/Label";
45
import {
56
SettingsRow,
67
SettingsRowDescription,
78
SettingsRowTitle,
89
} from "~/components/primitives/SettingsLayout";
910
import { cn } from "~/utils/cn";
11+
import { docsPath } from "~/utils/pathBuilder";
1012
import { Hint } from "~/components/primitives/Hint";
1113
import { TextLink } from "~/components/primitives/TextLink";
1214
import { SimpleTooltip } from "~/components/primitives/Tooltip";
@@ -17,6 +19,16 @@ import {
1719
} from "~/components/environments/EnvironmentLabel";
1820
import { envSlugToType, type EnvSlug } from "~/v3/vercel/vercelProjectIntegrationSchema";
1921

22+
export const SKEW_PROTECTION_DOCS_PATH = docsPath("deployment/version-skew-protection");
23+
24+
const SKEW_PROTECTION_MIN_SDK_VERSION: string | null = "4.5.12";
25+
26+
export function skewProtectionVersionRequirement(): string {
27+
return SKEW_PROTECTION_MIN_SDK_VERSION
28+
? `from SDK and CLI v${SKEW_PROTECTION_MIN_SDK_VERSION} and later`
29+
: "from a recent SDK and CLI — see the docs for the exact version";
30+
}
31+
2032
type BuildSettingsFieldsProps = {
2133
availableEnvSlugs: EnvSlug[];
2234
pullEnvVarsBeforeBuild: EnvSlug[];
@@ -38,6 +50,7 @@ type BuildSettingsFieldsProps = {
3850
currentTriggerVersionFetchFailed?: boolean;
3951
/** Hide the section-level master toggles for "Pull env vars" and "Discover new env vars". */
4052
hideSectionToggles?: boolean;
53+
showAtomicDeployments?: boolean;
4154
layout?: "settings" | "card";
4255
};
4356

@@ -56,6 +69,7 @@ export function BuildSettingsFields({
5669
currentTriggerVersion,
5770
currentTriggerVersionFetchFailed,
5871
hideSectionToggles,
72+
showAtomicDeployments = true,
5973
layout = "card",
6074
}: BuildSettingsFieldsProps) {
6175
const isSlugDisabled = (slug: EnvSlug) => !!disabledEnvSlugs?.[slug];
@@ -126,7 +140,7 @@ export function BuildSettingsFields({
126140
) : null;
127141

128142
const atomicSections =
129-
layout === "settings" ? (
143+
layout === "settings" && showAtomicDeployments ? (
130144
<>
131145
<SettingsRow
132146
action={
@@ -140,11 +154,25 @@ export function BuildSettingsFields({
140154
}
141155
>
142156
<div className="flex-1 space-y-1">
143-
<SettingsRowTitle>Atomic deployments</SettingsRowTitle>
157+
<SettingsRowTitle>
158+
<span className="flex items-center gap-2">
159+
Atomic deployments <DeprecatedBadge />
160+
</span>
161+
</SettingsRowTitle>
144162
<SettingsRowDescription>
145-
Promotes your Vercel deployment and your tasks together in Production, so your app
146-
never runs against a mismatched task version. Requires turning off "Auto-assign Custom
147-
Production Domains" on your Vercel project, which Trigger.dev does for you.{" "}
163+
Version skew protection replaces this. It pins every run to the deployment that
164+
triggered it, and works on its own {skewProtectionVersionRequirement()}. Atomic
165+
deployments still work, so turn this off whenever you're ready.{" "}
166+
<TextLink href={SKEW_PROTECTION_DOCS_PATH} target="_blank">
167+
Read about version skew protection
168+
</TextLink>
169+
.
170+
</SettingsRowDescription>
171+
<SettingsRowDescription>
172+
Atomic deployments promote your Vercel deployment and your tasks together in
173+
Production, so your app never runs against a mismatched task version. This needs
174+
"Auto-assign Custom Production Domains" turned off on your Vercel project, and
175+
Trigger.dev takes care of that for you.{" "}
148176
<TextLink
149177
href="https://trigger.dev/docs/vercel-integration#atomic-deployments"
150178
target="_blank"
@@ -172,7 +200,7 @@ export function BuildSettingsFields({
172200
{atomicBuilds.includes("prod") && onAutoPromoteChange !== undefined && (
173201
<SettingsRow
174202
title="Auto promotion"
175-
description="Once your tasks finish deploying, Trigger.dev promotes the Vercel deployment for you. Turn this off to promote from the Vercel dashboard yourself, and Trigger.dev will follow as soon as you do."
203+
description="Part of atomic deployments, and only used while they are on. Once your tasks finish deploying, Trigger.dev promotes the Vercel deployment for you. Turn this off to promote from the Vercel dashboard yourself, and Trigger.dev will follow as soon as you do."
176204
action={
177205
<Switch
178206
variant="medium"
@@ -333,10 +361,14 @@ export function BuildSettingsFields({
333361
{atomicSections}
334362

335363
{/* Atomic deployments */}
336-
{layout === "card" && (
364+
{layout === "card" && showAtomicDeployments && (
337365
<div>
338366
<div className="flex items-center justify-between">
339-
<Label>Atomic deployments</Label>
367+
<Label>
368+
<span className="flex items-center gap-2">
369+
Atomic deployments <DeprecatedBadge />
370+
</span>
371+
</Label>
340372
<Switch
341373
variant="small"
342374
checked={atomicBuilds.includes("prod")}
@@ -346,10 +378,18 @@ export function BuildSettingsFields({
346378
/>
347379
</div>
348380
<Hint className="pr-6">
349-
When enabled, production deployments wait for Vercel deployment to complete before
350-
promoting the Trigger.dev deployment. This will disable the "Auto-assign Custom
351-
Production Domains" option in your Vercel project settings to perform staged
352-
deployments.{" "}
381+
Version skew protection replaces this, and works on its own{" "}
382+
{skewProtectionVersionRequirement()}.{" "}
383+
<TextLink href={SKEW_PROTECTION_DOCS_PATH} target="_blank">
384+
Read about version skew protection
385+
</TextLink>
386+
.
387+
</Hint>
388+
<Hint className="pr-6">
389+
Atomic deployments promote your Vercel deployment and your tasks together in Production,
390+
so your app never runs against a mismatched task version. This needs "Auto-assign Custom
391+
Production Domains" turned off on your Vercel project, and Trigger.dev takes care of
392+
that for you.{" "}
353393
<TextLink
354394
href="https://trigger.dev/docs/vercel-integration#atomic-deployments"
355395
target="_blank"
@@ -375,27 +415,48 @@ export function BuildSettingsFields({
375415
)}
376416

377417
{/* Auto promotion — only visible when atomic deployments are on */}
378-
{layout === "card" && atomicBuilds.includes("prod") && onAutoPromoteChange !== undefined && (
379-
<div>
380-
<div className="flex items-center justify-between">
381-
<Label>Auto promotion</Label>
382-
<Switch
383-
variant="small"
384-
checked={autoPromote ?? true}
385-
onCheckedChange={onAutoPromoteChange}
386-
/>
418+
{layout === "card" &&
419+
showAtomicDeployments &&
420+
atomicBuilds.includes("prod") &&
421+
onAutoPromoteChange !== undefined && (
422+
<div>
423+
<div className="flex items-center justify-between">
424+
<Label>Auto promotion</Label>
425+
<Switch
426+
variant="small"
427+
checked={autoPromote ?? true}
428+
onCheckedChange={onAutoPromoteChange}
429+
/>
430+
</div>
431+
<Hint className="pr-6">
432+
When enabled, the integration automatically promotes the Vercel deployment after the
433+
Trigger.dev build completes. Turn off to manually promote from your Vercel dashboard —
434+
Trigger.dev will then promote automatically once you do.
435+
</Hint>
387436
</div>
388-
<Hint className="pr-6">
389-
When enabled, the integration automatically promotes the Vercel deployment after the
390-
Trigger.dev build completes. Turn off to manually promote from your Vercel dashboard —
391-
Trigger.dev will then promote automatically once you do.
392-
</Hint>
393-
</div>
394-
)}
437+
)}
395438
</>
396439
);
397440
}
398441

442+
function DeprecatedBadge() {
443+
return (
444+
<SimpleTooltip
445+
asChild
446+
button={
447+
<Badge
448+
variant="extra-small"
449+
className="text-warning system:border-transparent system:bg-warning system:text-white"
450+
>
451+
Deprecated
452+
</Badge>
453+
}
454+
content="Use version skew protection instead"
455+
disableHoverableContent
456+
/>
457+
);
458+
}
459+
399460
function EnvToggleRow({
400461
slug,
401462
checked,

apps/webapp/app/components/integrations/VercelOnboardingModal.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ export function VercelOnboardingModal({
235235
const [pullEnvVarsBeforeBuild, setPullEnvVarsBeforeBuild] = useState<EnvSlug[]>(
236236
() => availableEnvSlugsForOnboardingBuildSettings
237237
);
238-
const [atomicBuilds, setAtomicBuilds] = useState<EnvSlug[]>(() => ["prod"]);
238+
const [atomicBuilds, setAtomicBuilds] = useState<EnvSlug[]>([]);
239239
const [discoverEnvVars, setDiscoverEnvVars] = useState<EnvSlug[]>(
240240
() => availableEnvSlugsForOnboardingBuildSettings
241241
);
@@ -1164,7 +1164,7 @@ export function VercelOnboardingModal({
11641164
<div className="flex flex-col gap-4">
11651165
<Header3>Build Settings</Header3>
11661166
<Paragraph className="text-sm">
1167-
Configure how environment variables are pulled during builds and atomic deployments.
1167+
Configure how environment variables are pulled during builds.
11681168
</Paragraph>
11691169

11701170
<BuildSettingsFields
@@ -1176,6 +1176,7 @@ export function VercelOnboardingModal({
11761176
atomicBuilds={atomicBuilds}
11771177
onAtomicBuildsChange={setAtomicBuilds}
11781178
disabledEnvSlugs={disabledEnvSlugsForBuildSettings}
1179+
showAtomicDeployments={false}
11791180
/>
11801181

11811182
<FormButtons

apps/webapp/app/components/primitives/Badge.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ type BadgeProps = React.HTMLAttributes<HTMLDivElement> & {
1818
variant?: keyof typeof variants;
1919
};
2020

21-
export function Badge({ className, variant = "default", children, ...props }: BadgeProps) {
22-
return (
23-
<div className={cn(variants[variant], className)} {...props}>
21+
export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
22+
({ className, variant = "default", children, ...props }, ref) => (
23+
<div ref={ref} className={cn(variants[variant], className)} {...props}>
2424
<span>{children}</span>
2525
</div>
26-
);
27-
}
26+
)
27+
);
28+
29+
Badge.displayName = "Badge";

0 commit comments

Comments
 (0)