Skip to content

Commit b32c1d1

Browse files
myftijaTrigger.dev RepoOps
authored andcommitted
fix(cli): stop percent-encoding Windows output paths in the build manifest
Fix Windows deploys failing at indexing with `Cannot find module` on a percent-encoded path when the project directory contains spaces or non-ASCII characters. Mono-RevId: 570c78ee09bc121ae8066a7e70848ae4509d74e3
1 parent 8fe554a commit b32c1d1

5 files changed

Lines changed: 62 additions & 16 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+
Fix Windows deploys failing at indexing with `Cannot find module` on a percent-encoded path when the project directory contains spaces or non-ASCII characters.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { join, posix, sep, win32 } from "node:path";
2+
import { describe, expect, it } from "vitest";
3+
import { rewriteOutputPath } from "./buildWorker.js";
4+
5+
describe("rewriteOutputPath", () => {
6+
it("defaults to the host platform's path semantics", () => {
7+
const out = join(sep, "tmp", "out");
8+
expect(rewriteOutputPath(out, join(out, "src", "a b.mjs"))).toBe("/app/src/a b.mjs");
9+
});
10+
11+
it("maps a bundled file to its /app path on posix", () => {
12+
expect(rewriteOutputPath("/tmp/out", "/tmp/out/src/trigger/a.mjs", posix)).toBe(
13+
"/app/src/trigger/a.mjs"
14+
);
15+
});
16+
17+
it("uses forward slashes and keeps spaces and non-ASCII characters on windows", () => {
18+
const out = "C:\\Users\\me\\AppData\\Local\\Temp\\trigger-build";
19+
const file = `${out}\\Documents\\DATEN VORBEREITUNG FÜR AUKTIONEN\\trigger.config.mjs`;
20+
21+
expect(rewriteOutputPath(out, file, win32)).toBe(
22+
"/app/Documents/DATEN VORBEREITUNG FÜR AUKTIONEN/trigger.config.mjs"
23+
);
24+
});
25+
26+
it("tolerates mixed separators and drive letter case on windows", () => {
27+
expect(
28+
rewriteOutputPath("c:\\Users\\me\\out", "C:/Users/me/out/Trigger Demo/index.mjs", win32)
29+
).toBe("/app/Trigger Demo/index.mjs");
30+
});
31+
});

‎packages/cli-v3/src/build/buildWorker.ts‎

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,13 @@ import {
2222
import { createExternalsBuildExtension } from "./externals.js";
2323
import { tmpdir } from "node:os";
2424
import { mkdtemp, rm } from "node:fs/promises";
25-
import { join, relative, sep } from "node:path";
25+
import path, { join } from "node:path";
2626
import { generateContainerfile } from "../deploy/buildImage.js";
2727
import { writeFile } from "node:fs/promises";
2828
import { buildManifestToJSON } from "../utilities/buildManifest.js";
2929
import { logger } from "../utilities/logger.js";
3030
import { readPackageJSON } from "pkg-types";
3131
import { writeJSONFile } from "../utilities/fileSystem.js";
32-
import { isWindows } from "std-env";
33-
import { pathToFileURL } from "node:url";
3432
import { logBuildWorkerStart } from "./buildWorkerLogging.js";
3533
import { SdkVersionExtractor } from "./plugins.js";
3634
import { spinner } from "../utilities/windows.js";
@@ -215,17 +213,12 @@ function cleanEntryPath(entry: string): string {
215213
return entry.split("?")[0]!;
216214
}
217215

218-
function rewriteOutputPath(destinationDir: string, filePath: string) {
219-
if (isWindows) {
220-
return `/app/${relative(
221-
pathToFileURL(destinationDir).pathname,
222-
pathToFileURL(filePath).pathname
223-
)
224-
.split(sep)
225-
.join("/")}`;
226-
} else {
227-
return `/app/${relative(destinationDir, filePath)}`;
228-
}
216+
export function rewriteOutputPath(
217+
destinationDir: string,
218+
filePath: string,
219+
pathApi: Pick<typeof path, "relative" | "sep"> = path
220+
) {
221+
return `/app/${pathApi.relative(destinationDir, filePath).split(pathApi.sep).join("/")}`;
229222
}
230223

231224
async function writeDeployFiles({

‎packages/cli-v3/src/deploy/buildImage.test.ts‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ describe("generateContainerfile", () => {
6262
expect(containerfile).toContain(`FROM ${build} AS build`);
6363
});
6464

65+
it.each(["node", "bun"] as BuildRuntime[])(
66+
"runs the indexer in exec form so entry paths with spaces survive on %s",
67+
async (runtime) => {
68+
const indexScript = "/app/Documents/Trigger Demo/index.mjs";
69+
const containerfile = await generateContainerfile({
70+
runtime,
71+
build: {},
72+
image: undefined,
73+
indexScript,
74+
entrypoint: "entrypoint.js",
75+
});
76+
77+
expect(containerfile).toContain(`"${indexScript}" ]`);
78+
expect(containerfile).not.toMatch(/^RUN (node|bun run) \//m);
79+
}
80+
);
81+
6582
it.each(["node", "bun"] as BuildRuntime[])(
6683
"runs no package installation for uncustomized projects on %s",
6784
async (runtime) => {

‎packages/cli-v3/src/deploy/buildImage.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ ARG BUILDPLATFORM
892892
ENV BUILDPLATFORM=$BUILDPLATFORM TARGETPLATFORM=$TARGETPLATFORM
893893
894894
# Run the indexer
895-
RUN bun run ${options.indexScript}
895+
RUN [ "bun", "run", "${options.indexScript}" ]
896896
897897
# Development or production stage builds upon the base stage
898898
FROM base AS final
@@ -1001,7 +1001,7 @@ ARG BUILDPLATFORM
10011001
ENV BUILDPLATFORM=$BUILDPLATFORM TARGETPLATFORM=$TARGETPLATFORM
10021002
10031003
# Run the indexer
1004-
RUN node ${options.indexScript}
1004+
RUN [ "node", "${options.indexScript}" ]
10051005
10061006
# Development or production stage builds upon the base stage
10071007
FROM base AS final

0 commit comments

Comments
 (0)