Skip to content

Commit 9ecdf73

Browse files
carderneTrigger.dev RepoOps
authored andcommitted
feat(cli,webapp): make the node runtime track Node.js LTS
Deployments with an omitted `runtime` or `runtime: "node"` now resolve to the current Node.js LTS, currently Node.js 24, across all projects. Explicit versioned runtimes remain pinned, and the previous Node.js 21 update prompts have been removed. Mono-RevId: 98db23c3fb605d546336fdce06ed748d62f1b86a
1 parent c23c9d3 commit 9ecdf73

16 files changed

Lines changed: 64 additions & 401 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
Use the current Node.js LTS for deployments whose `runtime` is unset or set to `"node"`. Set a versioned runtime such as `"node-24"` to keep deployments pinned to that Node.js major.

‎apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,11 @@ export function OrganizationSettingsSideMenu({
5353
buildInfo,
5454
isUsingPlugin,
5555
isSsoUsingPlugin,
56-
hasProjectRuntimeUpdate,
5756
}: {
5857
organization: MatchedOrganization;
5958
buildInfo: BuildInfo;
6059
isUsingPlugin: boolean;
6160
isSsoUsingPlugin: boolean;
62-
hasProjectRuntimeUpdate: boolean;
6361
}) {
6462
const { isManagedCloud } = useFeatures();
6563
const featureFlags = useFeatureFlags();
@@ -140,15 +138,6 @@ export function OrganizationSettingsSideMenu({
140138
inactiveIconColor="text-text-dimmed"
141139
to={organizationProjectsPath(organization)}
142140
data-action="projects"
143-
badge={
144-
hasProjectRuntimeUpdate ? (
145-
<>
146-
{/* mr-1 lifts the right gap to 12px so it matches the dot's 12px top/bottom inset in the h-8 row */}
147-
<span aria-hidden className="mr-1 size-2 shrink-0 rounded-full bg-warning" />
148-
<span className="sr-only">Runtime update available.</span>
149-
</>
150-
) : undefined
151-
}
152141
/>
153142
<SideMenuItem
154143
name="Team"

‎apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.projects/ProjectsPage.tsx‎

Lines changed: 10 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import { NODE_RUNTIME_UPDATE_MAJOR } from "@trigger.dev/core/v3";
21
import { RuntimeIcon } from "~/components/RuntimeIcon";
3-
import { CodeBlock } from "~/components/code/CodeBlock";
4-
import { InlineCode } from "~/components/code/InlineCode";
52
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
63
import { LinkButton } from "~/components/primitives/Buttons";
7-
import { ClipboardField } from "~/components/primitives/ClipboardField";
8-
import { CopyAgentPromptButton } from "~/components/primitives/CopyButton";
94
import { DateTime } from "~/components/primitives/DateTime";
105
import { NavBar, PageTitle } from "~/components/primitives/PageHeader";
116
import { Paragraph } from "~/components/primitives/Paragraph";
@@ -14,45 +9,11 @@ import {
149
SettingsContainer,
1510
SettingsHeader,
1611
SettingsRow,
17-
SettingsRowDescription,
1812
SettingsRowTitle,
1913
SettingsSection,
2014
} from "~/components/primitives/SettingsLayout";
2115
import { v3DeploymentPath, v3ProjectPath } from "~/utils/pathBuilder";
2216

23-
// The source major has a constant in packages/core; the target it moves projects to does not.
24-
const NODE_RUNTIME_TARGET_MAJOR = 24;
25-
26-
const CONFIG_SNIPPET = `export default defineConfig({
27-
project: "<your-project-ref>",
28-
runtime: "node-${NODE_RUNTIME_TARGET_MAJOR}",
29-
});`;
30-
31-
const CLI_COMMAND = "npx trigger.dev@latest projects list --needs-update";
32-
33-
function buildRuntimeUpdatePrompt({
34-
projects,
35-
targetMajor,
36-
}: {
37-
projects: ProjectRuntimeRow[];
38-
targetMajor: number;
39-
}) {
40-
const projectList = projects.map((project) => `- ${project.name} (${project.ref})`).join("\n");
41-
42-
return `Update these Trigger.dev projects to Node.js ${targetMajor}.
43-
44-
They still deploy on Node.js ${NODE_RUNTIME_UPDATE_MAJOR} in production. Moving to Node.js ${targetMajor} is a one-field change in each project's trigger.config.ts.
45-
46-
Projects to update:
47-
${projectList}
48-
49-
How to do it:
50-
1. Find the trigger.config.ts for each project above — the reference in parentheses is its "project" field.
51-
2. In defineConfig, set runtime: "node-${targetMajor}", adding the field if it isn't there. Reference: https://trigger.dev/docs/config/config-file#runtime
52-
3. Deploy each project so the new runtime takes effect.
53-
4. Check that nothing is left by running: ${CLI_COMMAND}`;
54-
}
55-
5617
/** A project and its current Production deployment, or `null` when it has never been deployed. */
5718
export type ProjectRuntimeRow = {
5819
name: string;
@@ -69,143 +30,32 @@ export type ProjectRuntimeRow = {
6930

7031
export function ProjectsPage({
7132
organizationSlug,
72-
needsUpdate,
73-
otherProjects,
33+
projects,
7434
}: {
7535
organizationSlug: string;
76-
needsUpdate: ProjectRuntimeRow[];
77-
otherProjects: ProjectRuntimeRow[];
36+
projects: ProjectRuntimeRow[];
7837
}) {
79-
const count = needsUpdate.length;
80-
// "All projects" lists every project — both the ones needing an update and the rest — sorted by name.
81-
const allProjects = [...needsUpdate, ...otherProjects].sort((a, b) =>
82-
a.name.localeCompare(b.name)
83-
);
84-
8538
return (
8639
<PageContainer>
8740
<NavBar>
8841
<PageTitle title="Projects" />
8942
</NavBar>
9043
<PageBody scrollable>
9144
<SettingsContainer>
92-
{count > 0 ? (
93-
<>
94-
<SettingsSection>
95-
<SettingsHeader
96-
title={
97-
<span className="flex items-center gap-x-2">
98-
<span aria-hidden className="size-2 shrink-0 rounded-full bg-warning" />
99-
Runtime update available
100-
</span>
101-
}
102-
description={`${count} ${
103-
count === 1 ? "project is" : "projects are"
104-
} still running Node.js ${NODE_RUNTIME_UPDATE_MAJOR} in production. Update each one to Node.js ${NODE_RUNTIME_TARGET_MAJOR} and deploy a new version.`}
105-
/>
106-
107-
<SettingsRow
108-
align="start"
109-
title="Update your config"
110-
description={
111-
<>
112-
Set the runtime in{" "}
113-
<InlineCode variant="extra-extra-small" className="whitespace-nowrap">
114-
trigger.config.ts
115-
</InlineCode>
116-
, then deploy. <InlineCode variant="extra-extra-small">node-22</InlineCode>{" "}
117-
and <InlineCode variant="extra-extra-small">node-26</InlineCode> are also
118-
supported.
119-
</>
120-
}
121-
action={
122-
<CodeBlock
123-
code={CONFIG_SNIPPET}
124-
showLineNumbers={false}
125-
showOpenInModal={false}
126-
className="w-fit"
127-
/>
128-
}
129-
/>
130-
131-
<SettingsRow
132-
title="Or check from the CLI"
133-
description="Find every project that needs an update."
134-
action={
135-
<ClipboardField
136-
value={CLI_COMMAND}
137-
variant="secondary/small"
138-
iconButton
139-
className="w-64"
140-
/>
141-
}
142-
/>
143-
144-
<SettingsRow
145-
title="Or let your AI agent do it"
146-
description="Copy a ready-to-paste prompt for Claude Code, Cursor, or any coding agent. It includes every project that needs updating."
147-
action={
148-
<CopyAgentPromptButton
149-
prompt={buildRuntimeUpdatePrompt({
150-
projects: needsUpdate,
151-
targetMajor: NODE_RUNTIME_TARGET_MAJOR,
152-
})}
153-
label="Copy AI agent prompt"
154-
tooltip="Copies an update prompt to paste into Claude Code, Cursor, or any coding agent"
155-
variant="primary/small"
156-
/>
157-
}
158-
/>
159-
</SettingsSection>
160-
161-
<SettingsSection>
162-
<SettingsHeader
163-
title={
164-
<span className="text-warning">
165-
{count} {count === 1 ? "project" : "projects"} to update
166-
</span>
167-
}
168-
/>
169-
{needsUpdate.map((project) => (
170-
<ProjectRow
171-
key={project.ref}
172-
organizationSlug={organizationSlug}
173-
project={project}
174-
/>
175-
))}
176-
</SettingsSection>
177-
</>
178-
) : null}
179-
18045
<SettingsSection>
18146
<SettingsHeader title="All projects" />
182-
{allProjects.length === 0 ? (
47+
{projects.length === 0 ? (
18348
<SettingsBlock>
18449
<Paragraph variant="small">This organization has no projects yet.</Paragraph>
18550
</SettingsBlock>
18651
) : (
187-
<>
188-
{count === 0 ? (
189-
<SettingsBlock>
190-
<div className="space-y-0.5">
191-
<div className="flex items-center gap-x-2">
192-
<span aria-hidden className="size-2 shrink-0 rounded-full bg-success" />
193-
<SettingsRowTitle>All projects are up to date</SettingsRowTitle>
194-
</div>
195-
<SettingsRowDescription>
196-
Every project is running the latest Node.js version in production.
197-
</SettingsRowDescription>
198-
</div>
199-
</SettingsBlock>
200-
) : null}
201-
{allProjects.map((project) => (
202-
<ProjectRow
203-
key={project.ref}
204-
organizationSlug={organizationSlug}
205-
project={project}
206-
/>
207-
))}
208-
</>
52+
projects.map((project) => (
53+
<ProjectRow
54+
key={project.ref}
55+
organizationSlug={organizationSlug}
56+
project={project}
57+
/>
58+
))
20959
)}
21060
</SettingsSection>
21161
</SettingsContainer>
Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { needsNodeRuntimeUpdate } from "@trigger.dev/core/v3";
21
import { typedjson, useTypedLoaderData } from "remix-typedjson";
32
import { resolveOrgIdFromSlugForUser } from "~/models/organization.server";
43
import { listCurrentProductionProjectRuntimes } from "~/services/projectRuntimeUpdates.server";
@@ -34,49 +33,26 @@ export const loader = dashboardLoader(
3433
}
3534

3635
const runtimes = await listCurrentProductionProjectRuntimes({ organizationId });
37-
38-
const needsUpdate: ProjectRuntimeRow[] = [];
39-
const otherProjects: ProjectRuntimeRow[] = [];
40-
41-
for (const { project, environment, deployment } of runtimes) {
42-
const row: ProjectRuntimeRow = {
43-
name: project.name,
44-
ref: project.externalRef,
45-
slug: project.slug,
46-
environmentSlug: environment.slug,
47-
deployment: deployment
48-
? {
49-
runtime: deployment.runtime,
50-
runtimeVersion: deployment.runtimeVersion,
51-
deployedAt: deployment.deployedAt,
52-
shortCode: deployment.shortCode,
53-
}
54-
: null,
55-
};
56-
57-
if (deployment && needsNodeRuntimeUpdate(deployment.runtime, deployment.runtimeVersion)) {
58-
needsUpdate.push(row);
59-
} else {
60-
otherProjects.push(row);
61-
}
62-
}
63-
64-
return typedjson({
65-
organizationSlug: params.organizationSlug,
66-
needsUpdate,
67-
otherProjects,
68-
});
36+
const projects: ProjectRuntimeRow[] = runtimes.map(({ project, environment, deployment }) => ({
37+
name: project.name,
38+
ref: project.externalRef,
39+
slug: project.slug,
40+
environmentSlug: environment.slug,
41+
deployment: deployment
42+
? {
43+
runtime: deployment.runtime,
44+
runtimeVersion: deployment.runtimeVersion,
45+
deployedAt: deployment.deployedAt,
46+
shortCode: deployment.shortCode,
47+
}
48+
: null,
49+
}));
50+
51+
return typedjson({ organizationSlug: params.organizationSlug, projects });
6952
}
7053
);
7154

7255
export default function Page() {
73-
const { organizationSlug, needsUpdate, otherProjects } = useTypedLoaderData<typeof loader>();
74-
75-
return (
76-
<ProjectsPage
77-
organizationSlug={organizationSlug}
78-
needsUpdate={needsUpdate}
79-
otherProjects={otherProjects}
80-
/>
81-
);
56+
const { organizationSlug, projects } = useTypedLoaderData<typeof loader>();
57+
return <ProjectsPage organizationSlug={organizationSlug} projects={projects} />;
8258
}

0 commit comments

Comments
 (0)