Skip to content

Commit d639a72

Browse files
claude[bot]Trigger.dev RepoOps
authored andcommitted
fix(webapp): read deployment runtime from the indexed worker
Deployments now show the runtime their worker actually reported. Bun deployments were being labelled "Node.js" because the runtime recorded against the deployment was usually empty and the dashboard filled the gap with a default. A deployment with no recorded runtime now shows a dash instead of a guess. Fixes #3105. Mono-RevId: b68e8835841d7a47c44ed9bfd6a0927ab6ca0edf
1 parent 7e56062 commit d639a72

6 files changed

Lines changed: 135 additions & 19 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Deployments now show the runtime their worker actually reported, so Bun deployments are no longer labelled Node.js; a deployment with no recorded runtime shows a dash instead of a guess.

apps/webapp/app/components/RuntimeIcon.tsx

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,12 @@ export function RuntimeIcon({
2929
}: RuntimeIconProps) {
3030
const parsedRuntime = parseRuntime(runtime);
3131

32-
// Default to Node.js if no runtime is specified
33-
const effectiveRuntime = parsedRuntime || {
34-
runtime: "node" as const,
35-
originalRuntime: "node",
36-
displayName: "Node.js",
37-
};
32+
if (!parsedRuntime) {
33+
return <span className="text-text-dimmed"></span>;
34+
}
3835

39-
const icon = getIcon(effectiveRuntime.runtime, className);
40-
const formattedText = formatRuntimeWithVersion(effectiveRuntime.originalRuntime, runtimeVersion);
36+
const icon = getIcon(parsedRuntime.runtime, className);
37+
const formattedText = formatRuntimeWithVersion(parsedRuntime.originalRuntime, runtimeVersion);
4138

4239
if (withLabel) {
4340
return (
@@ -48,11 +45,5 @@ export function RuntimeIcon({
4845
);
4946
}
5047

51-
if (typeof icon === "object" && "type" in icon) {
52-
return (
53-
<SimpleTooltip button={icon} content={formattedText} side="top" disableHoverableContent />
54-
);
55-
}
56-
57-
return icon;
48+
return <SimpleTooltip button={icon} content={formattedText} side="top" disableHoverableContent />;
5849
}

apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ export class DeploymentListPresenter {
162162
wd."id",
163163
wd."shortCode",
164164
wd."version",
165-
wd."runtime",
166-
wd."runtimeVersion",
165+
COALESCE(bw."runtime", wd."runtime") AS "runtime",
166+
COALESCE(bw."runtimeVersion", wd."runtimeVersion") AS "runtimeVersion",
167167
(SELECT COUNT(*) FROM ${sqlDatabaseSchema}."BackgroundWorkerTask" WHERE "BackgroundWorkerTask"."workerId" = wd."workerId") AS "tasksCount",
168168
wd."environmentId",
169169
wd."status",
@@ -181,6 +181,8 @@ FROM
181181
${sqlDatabaseSchema}."WorkerDeployment" as wd
182182
LEFT JOIN
183183
${sqlDatabaseSchema}."User" as u ON wd."triggeredById" = u."id"
184+
LEFT JOIN
185+
${sqlDatabaseSchema}."BackgroundWorker" as bw ON wd."workerId" = bw."id"
184186
${vercelJoin}
185187
WHERE
186188
wd."projectId" = ${project.id}

apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ export class DeploymentPresenter {
147147
},
148148
sdkVersion: true,
149149
cliVersion: true,
150+
runtime: true,
151+
runtimeVersion: true,
150152
},
151153
},
152154
triggeredBy: {
@@ -262,8 +264,8 @@ export class DeploymentPresenter {
262264
deployedBy: deployment.triggeredBy,
263265
sdkVersion: deployment.worker?.sdkVersion,
264266
cliVersion: deployment.worker?.cliVersion,
265-
runtime: deployment.runtime,
266-
runtimeVersion: deployment.runtimeVersion,
267+
runtime: deployment.worker?.runtime ?? deployment.runtime,
268+
runtimeVersion: deployment.worker?.runtimeVersion ?? deployment.runtimeVersion,
267269
imageReference: deployment.imageReference,
268270
imagePlatform: deployment.imagePlatform,
269271
externalBuildData:

apps/webapp/test/runtime.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, expect, it } from "vitest";
2+
import { formatRuntimeWithVersion, parseRuntime } from "~/utils/runtime";
3+
4+
describe("parseRuntime", () => {
5+
it("normalizes bun runtimes", () => {
6+
expect(parseRuntime("bun")).toEqual({
7+
runtime: "bun",
8+
originalRuntime: "bun",
9+
displayName: "Bun",
10+
});
11+
expect(parseRuntime("bun-1.2")).toEqual({
12+
runtime: "bun",
13+
originalRuntime: "bun-1.2",
14+
displayName: "Bun",
15+
});
16+
});
17+
18+
it("normalizes node runtimes", () => {
19+
expect(parseRuntime("node")).toEqual({
20+
runtime: "node",
21+
originalRuntime: "node",
22+
displayName: "Node.js",
23+
});
24+
expect(parseRuntime("node-24")).toEqual({
25+
runtime: "node",
26+
originalRuntime: "node-24",
27+
displayName: "Node.js",
28+
});
29+
});
30+
31+
it("returns null for a missing runtime instead of assuming node", () => {
32+
expect(parseRuntime(null)).toBeNull();
33+
expect(parseRuntime(undefined)).toBeNull();
34+
expect(parseRuntime("")).toBeNull();
35+
});
36+
37+
it("returns null for an unrecognized runtime", () => {
38+
expect(parseRuntime("deno")).toBeNull();
39+
expect(parseRuntime("python3.12")).toBeNull();
40+
expect(parseRuntime("Node")).toBeNull();
41+
});
42+
});
43+
44+
describe("formatRuntimeWithVersion", () => {
45+
it("appends the version when there is one", () => {
46+
expect(formatRuntimeWithVersion("bun", "1.2.23")).toBe("Bun v1.2.23");
47+
expect(formatRuntimeWithVersion("node-24", "24.9.0")).toBe("Node.js v24.9.0");
48+
});
49+
50+
it("falls back to the display name without a version", () => {
51+
expect(formatRuntimeWithVersion("bun", null)).toBe("Bun");
52+
expect(formatRuntimeWithVersion("node-24", undefined)).toBe("Node.js");
53+
expect(formatRuntimeWithVersion("node", "")).toBe("Node.js");
54+
});
55+
56+
it("reports an unknown runtime rather than a default", () => {
57+
expect(formatRuntimeWithVersion(null, null)).toBe("Unknown runtime");
58+
expect(formatRuntimeWithVersion(undefined, "24.9.0")).toBe("Unknown runtime");
59+
expect(formatRuntimeWithVersion("deno", "2.1.0")).toBe("Unknown runtime");
60+
});
61+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// @vitest-environment jsdom
2+
import { createElement } from "react";
3+
import { createRoot, type Root } from "react-dom/client";
4+
import { act } from "react-dom/test-utils";
5+
import { afterEach, describe, expect, it } from "vitest";
6+
import { RuntimeIcon } from "~/components/RuntimeIcon";
7+
8+
let container: HTMLDivElement | undefined;
9+
let root: Root | undefined;
10+
11+
afterEach(() => {
12+
if (root) {
13+
act(() => root!.unmount());
14+
}
15+
container?.remove();
16+
container = undefined;
17+
root = undefined;
18+
});
19+
20+
function render(props: { runtime?: string | null; runtimeVersion?: string | null }) {
21+
container = document.createElement("div");
22+
document.body.appendChild(container);
23+
root = createRoot(container);
24+
act(() => {
25+
root!.render(createElement(RuntimeIcon, { ...props, withLabel: true }));
26+
});
27+
return container.textContent ?? "";
28+
}
29+
30+
describe("RuntimeIcon", () => {
31+
const unknownRuntimes: (string | null | undefined)[] = [null, undefined, "", "deno"];
32+
33+
it.each(unknownRuntimes)("does not claim Node.js for runtime %o", (runtime) => {
34+
const text = render({ runtime, runtimeVersion: null });
35+
36+
expect(text).not.toContain("Node.js");
37+
expect(text).toContain("–");
38+
});
39+
40+
it("does not claim Node.js when only a version is recorded", () => {
41+
const text = render({ runtime: null, runtimeVersion: "24.9.0" });
42+
43+
expect(text).not.toContain("Node.js");
44+
expect(text).not.toContain("24.9.0");
45+
});
46+
47+
it("labels a bun runtime as Bun", () => {
48+
expect(render({ runtime: "bun", runtimeVersion: "1.2.23" })).toContain("Bun v1.2.23");
49+
});
50+
51+
it("labels a node runtime as Node.js", () => {
52+
expect(render({ runtime: "node-24", runtimeVersion: "24.9.0" })).toContain("Node.js v24.9.0");
53+
});
54+
});

0 commit comments

Comments
 (0)