cli: add dev lock file to prevent 2 dev processes running at the same time in the same dir (#1854)
* cli: add dev lock file to prevent 2 dev processes running at the same time in the same dir * Make sure the .trigger dir exists before creating the dev.lock file
This commit is contained in:
@@ -11,6 +11,7 @@ import { runtimeChecks } from "../utilities/runtimeCheck.js";
|
||||
import { getProjectClient, LoginResultOk } from "../utilities/session.js";
|
||||
import { login } from "./login.js";
|
||||
import { updateTriggerPackages } from "./update.js";
|
||||
import { createLockFile } from "../dev/lock.js";
|
||||
|
||||
const DevCommandOptions = CommonCommandOptions.extend({
|
||||
debugOtel: z.boolean().default(false),
|
||||
@@ -120,6 +121,8 @@ async function startDev(options: StartDevOptions) {
|
||||
displayedUpdateMessage = await updateTriggerPackages(options.cwd, { ...options }, true, true);
|
||||
}
|
||||
|
||||
const removeLockFile = await createLockFile(options.cwd);
|
||||
|
||||
let devInstance: DevSessionInstance | undefined;
|
||||
|
||||
printDevBanner(displayedUpdateMessage);
|
||||
@@ -178,6 +181,7 @@ async function startDev(options: StartDevOptions) {
|
||||
stop: async () => {
|
||||
devInstance?.stop();
|
||||
await watcher?.stop();
|
||||
removeLockFile();
|
||||
},
|
||||
waitUntilExit,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import path from "node:path";
|
||||
import { readFile } from "../utilities/fileSystem.js";
|
||||
import { tryCatch } from "@trigger.dev/core/utils";
|
||||
import { logger } from "../utilities/logger.js";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { existsSync, unlinkSync } from "node:fs";
|
||||
import { onExit } from "signal-exit";
|
||||
|
||||
const LOCK_FILE_NAME = "dev.lock";
|
||||
|
||||
export async function createLockFile(cwd: string) {
|
||||
const currentPid = process.pid;
|
||||
const lockFilePath = path.join(cwd, ".trigger", LOCK_FILE_NAME);
|
||||
|
||||
logger.debug("Checking for lockfile", { lockFilePath, currentPid });
|
||||
|
||||
const removeLockFile = () => {
|
||||
try {
|
||||
logger.debug("Removing lockfile", { lockFilePath });
|
||||
return unlinkSync(lockFilePath);
|
||||
} catch (e) {
|
||||
// This sometimes fails on Windows with EBUSY
|
||||
}
|
||||
};
|
||||
const removeExitListener = onExit(removeLockFile);
|
||||
|
||||
const [, existingLockfileContents] = await tryCatch(readFile(lockFilePath));
|
||||
|
||||
if (existingLockfileContents) {
|
||||
// Read the pid number from the lockfile
|
||||
const existingPid = Number(existingLockfileContents);
|
||||
|
||||
logger.debug("Lockfile exists", { lockFilePath, existingPid, currentPid });
|
||||
|
||||
if (existingPid === currentPid) {
|
||||
logger.debug("Lockfile exists and is owned by current process", {
|
||||
lockFilePath,
|
||||
existingPid,
|
||||
currentPid,
|
||||
});
|
||||
|
||||
return () => {
|
||||
removeExitListener();
|
||||
removeLockFile();
|
||||
};
|
||||
}
|
||||
|
||||
// If the pid is different, try and kill the existing pid
|
||||
logger.debug("Lockfile exists and is owned by another process, killing it", {
|
||||
lockFilePath,
|
||||
existingPid,
|
||||
currentPid,
|
||||
});
|
||||
|
||||
try {
|
||||
process.kill(existingPid);
|
||||
// If it did kill the process, it will have exited, deleting the lockfile, so let's wait for that to happen
|
||||
// But let's not wait forever
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
reject(new Error("Timed out waiting for lockfile to be deleted"));
|
||||
}, 5000);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (!existsSync(lockFilePath)) {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timeout);
|
||||
resolve(true);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
} catch (error) {
|
||||
logger.debug("Failed to kill existing process, lets assume it's not running", { error });
|
||||
}
|
||||
}
|
||||
|
||||
// Now write the current pid to the lockfile
|
||||
await writeFileAndEnsureDirExists(lockFilePath, currentPid.toString());
|
||||
|
||||
logger.debug("Lockfile created", { lockFilePath, currentPid });
|
||||
|
||||
return () => {
|
||||
removeExitListener();
|
||||
removeLockFile();
|
||||
};
|
||||
}
|
||||
|
||||
async function writeFileAndEnsureDirExists(filePath: string, data: string) {
|
||||
const dir = path.dirname(filePath);
|
||||
await mkdir(dir, { recursive: true });
|
||||
await writeFile(filePath, data);
|
||||
}
|
||||
Reference in New Issue
Block a user