Skip to content

Commit b178629

Browse files
committed
fix dev build namespacing
1 parent 775d918 commit b178629

6 files changed

Lines changed: 68 additions & 26 deletions

File tree

packages/cli-v3/src/dev/devSession.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ export async function startDevSession({
5757
dashboardUrl,
5858
keepTmpFiles,
5959
}: DevSessionOptions): Promise<DevSessionInstance> {
60-
clearTmpDirs(rawConfig.workingDir);
61-
const destination = getTmpDir(rawConfig.workingDir, "build", keepTmpFiles);
60+
clearTmpDirs(rawConfig.workingDir, branch);
61+
const destination = getTmpDir(rawConfig.workingDir, "build", keepTmpFiles, branch);
6262
// Create shared store directory for deduplicating chunk files across rebuilds
63-
const storeDir = getStoreDir(rawConfig.workingDir, keepTmpFiles);
63+
const storeDir = getStoreDir(rawConfig.workingDir, keepTmpFiles, branch);
6464

6565
const runtime = await startWorkerRuntime({
6666
name,
67+
branch,
6768
config: rawConfig,
6869
args: rawArgs,
6970
client,
@@ -190,7 +191,7 @@ export async function startDevSession({
190191
return;
191192
}
192193

193-
const workerDir = getTmpDir(rawConfig.workingDir, "build", keepTmpFiles);
194+
const workerDir = getTmpDir(rawConfig.workingDir, "build", keepTmpFiles, branch);
194195
await updateBuild(result, workerDir);
195196
});
196197
},

packages/cli-v3/src/dev/devSupervisor.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ import { resolveLocalEnvVars } from "../utilities/localEnvVars.js";
3131
import type { Metafile } from "esbuild";
3232
import { TaskRunProcessPool } from "./taskRunProcessPool.js";
3333
import { tryCatch } from "@trigger.dev/core/utils";
34+
import { devBranchPathSegment } from "../utilities/devBranch.js";
35+
import { getTmpRoot } from "../utilities/tempDirectories.js";
3436

3537
export type WorkerRuntimeOptions = {
3638
name: string | undefined;
39+
branch?: string;
3740
config: ResolvedConfig;
3841
args: DevCommandOptions;
3942
client: CliApiClient;
@@ -206,8 +209,14 @@ class DevSupervisor implements WorkerRuntime {
206209
mkdirSync(triggerDir, { recursive: true });
207210
}
208211

209-
this.activeRunsPath = join(triggerDir, "active-runs.json");
210-
this.watchdogPidPath = join(triggerDir, "watchdog.pid");
212+
// Namespace watchdog state per branch so concurrent dev sessions on
213+
// different branches don't share a single watchdog instance (the
214+
// single-instance guard would otherwise kill the other branch's watchdog).
215+
const safeBranch = devBranchPathSegment(this.options.branch);
216+
const suffix = safeBranch ? `-${safeBranch}` : "";
217+
218+
this.activeRunsPath = join(triggerDir, `active-runs${suffix}.json`);
219+
this.watchdogPidPath = join(triggerDir, `watchdog${suffix}.pid`);
211220

212221
// Write empty active-runs file
213222
this.#updateActiveRunsFile();
@@ -232,7 +241,7 @@ class DevSupervisor implements WorkerRuntime {
232241
WATCHDOG_API_KEY: this.options.client.accessToken ?? "",
233242
WATCHDOG_ACTIVE_RUNS: this.activeRunsPath,
234243
WATCHDOG_PID_FILE: this.watchdogPidPath,
235-
WATCHDOG_TMP_DIR: join(triggerDir, "tmp"),
244+
WATCHDOG_TMP_DIR: getTmpRoot(this.options.config.workingDir, this.options.branch),
236245
},
237246
});
238247

packages/cli-v3/src/dev/lock.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path from "node:path";
22
import { readFile } from "../utilities/fileSystem.js";
33
import { tryCatch } from "@trigger.dev/core/utils";
4-
import { isDefaultDevBranch } from "@trigger.dev/core/v3/utils/gitBranch";
4+
import { devBranchPathSegment } from "../utilities/devBranch.js";
55
import { logger } from "../utilities/logger.js";
66
import { mkdir, writeFile } from "node:fs/promises";
77
import { existsSync, unlinkSync } from "node:fs";
@@ -16,13 +16,8 @@ const LOCK_FILE_NAME = "dev.lock";
1616
* branches don't kill each other.
1717
*/
1818
function lockFileName(branch?: string) {
19-
if (!branch || isDefaultDevBranch(branch)) {
20-
return LOCK_FILE_NAME;
21-
}
22-
23-
// Branch names can contain filesystem-unsafe characters (e.g. "/"), so sanitize.
24-
const safeBranch = branch.replace(/[^a-zA-Z0-9-_]/g, "-");
25-
return `dev.${safeBranch}.lock`;
19+
const safeBranch = devBranchPathSegment(branch);
20+
return safeBranch ? `dev.${safeBranch}.lock` : LOCK_FILE_NAME;
2621
}
2722

2823
export async function createLockFile(cwd: string, branch?: string) {

packages/cli-v3/src/utilities/analyze.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,9 @@ function formatSize(bytes: number): string {
207207
}
208208

209209
function normalizePath(path: string): string {
210-
// Remove .trigger/tmp/build-<hash>/ prefix
211-
return path.replace(/(^|\/).trigger\/tmp\/build-[^/]+\//, "");
210+
// Remove .trigger/tmp/build-<hash>/ prefix (tmp root may be branch-scoped,
211+
// e.g. .trigger/tmp-feature-foo/build-<hash>/)
212+
return path.replace(/(^|\/)\.trigger\/tmp(-[^/]+)?\/build-[^/]+\//, "");
212213
}
213214

214215
interface BundleTreeData {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { isDefaultDevBranch } from "@trigger.dev/core/v3/utils/gitBranch";
2+
3+
/**
4+
* Derives a filesystem-safe path segment for a dev branch, used to namespace
5+
* on-disk artifacts (lock files, the `.trigger/tmp` build tree, watchdog state)
6+
* so concurrent `trigger dev` sessions on different branches in the same project
7+
* don't clobber each other.
8+
*
9+
* Returns `undefined` for the default branch (or no branch) so callers keep
10+
* their original, branch-less paths for backwards compatibility.
11+
*/
12+
export function devBranchPathSegment(branch?: string): string | undefined {
13+
if (!branch || isDefaultDevBranch(branch)) {
14+
return undefined;
15+
}
16+
17+
// Branch names can contain filesystem-unsafe characters (e.g. "/"), so sanitize.
18+
return branch.replace(/[^a-zA-Z0-9-_]/g, "-");
19+
}

packages/cli-v3/src/utilities/tempDirectories.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { onExit } from "signal-exit";
4+
import { devBranchPathSegment } from "./devBranch.js";
5+
6+
/**
7+
* Resolves the `.trigger/tmp` root for a dev session, scoped to the branch so
8+
* concurrent sessions on different branches don't share (and clobber) a build
9+
* tree. The default branch keeps the original `.trigger/tmp` path; branches get
10+
* a sibling root (e.g. `.trigger/tmp-feature-foo`) so a default-branch
11+
* `clearTmpDirs` can't reach into a branch's tree, and vice versa.
12+
*/
13+
export function getTmpRoot(projectRoot: string | undefined, branch?: string): string {
14+
projectRoot ??= process.cwd();
15+
const safeBranch = devBranchPathSegment(branch);
16+
const tmpDirName = safeBranch ? `tmp-${safeBranch}` : "tmp";
17+
return path.join(projectRoot, ".trigger", tmpDirName);
18+
}
419

520
/**
621
* A short-lived directory. Automatically removed when the process exits, but
@@ -21,10 +36,10 @@ export interface EphemeralDirectory {
2136
export function getTmpDir(
2237
projectRoot: string | undefined,
2338
prefix: string,
24-
keep: boolean = false
39+
keep: boolean = false,
40+
branch?: string
2541
): EphemeralDirectory {
26-
projectRoot ??= process.cwd();
27-
const tmpRoot = path.join(projectRoot, ".trigger", "tmp");
42+
const tmpRoot = getTmpRoot(projectRoot, branch);
2843
fs.mkdirSync(tmpRoot, { recursive: true });
2944

3045
const tmpPrefix = path.join(tmpRoot, `${prefix}-`);
@@ -48,9 +63,8 @@ export function getTmpDir(
4863
};
4964
}
5065

51-
export function clearTmpDirs(projectRoot: string | undefined) {
52-
projectRoot ??= process.cwd();
53-
const tmpRoot = path.join(projectRoot, ".trigger", "tmp");
66+
export function clearTmpDirs(projectRoot: string | undefined, branch?: string) {
67+
const tmpRoot = getTmpRoot(projectRoot, branch);
5468

5569
try {
5670
fs.rmSync(tmpRoot, { recursive: true, force: true });
@@ -65,9 +79,12 @@ export function clearTmpDirs(projectRoot: string | undefined) {
6579
* identical chunk files between build versions.
6680
* Automatically cleaned up when the process exits.
6781
*/
68-
export function getStoreDir(projectRoot: string | undefined, keep: boolean = false): string {
69-
projectRoot ??= process.cwd();
70-
const storeDir = path.join(projectRoot, ".trigger", "tmp", "store");
82+
export function getStoreDir(
83+
projectRoot: string | undefined,
84+
keep: boolean = false,
85+
branch?: string
86+
): string {
87+
const storeDir = path.join(getTmpRoot(projectRoot, branch), "store");
7188
fs.mkdirSync(storeDir, { recursive: true });
7289

7390
// Register exit handler to clean up the store directory

0 commit comments

Comments
 (0)