buncargo 1.0.13 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin.ts +17 -1
- package/cli.ts +4 -10
- package/core/process.ts +36 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +22 -8
- package/dist/cli.js +3 -3
- package/dist/core/index.js +4 -2
- package/dist/core/process.d.ts +7 -0
- package/dist/core/process.js +3 -1
- package/dist/core/watchdog.js +2 -2
- package/dist/environment.js +3 -3
- package/dist/index-1yvbwj4k.js +274 -0
- package/dist/index-2f47khe5.js +390 -0
- package/dist/index-8hbbj1mp.js +136 -0
- package/dist/index-g6eb5wdw.js +132 -0
- package/dist/index-ggq3yryx.js +120 -0
- package/dist/index-kf3dhser.js +160 -0
- package/dist/index-qw4093g2.js +58 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -6
- package/dist/loader.js +4 -4
- package/index.ts +1 -0
- package/package.json +140 -140
- package/readme.md +3 -1
package/bin.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* bunx buncargo dev # Start containers + dev servers
|
|
8
8
|
* bunx buncargo dev --down # Stop containers
|
|
9
9
|
* bunx buncargo dev --reset # Stop + remove volumes
|
|
10
|
+
* bunx buncargo typecheck # Run TypeScript typecheck
|
|
10
11
|
* bunx buncargo prisma ... # Run prisma commands
|
|
11
12
|
* bunx buncargo help # Show help
|
|
12
13
|
*/
|
|
@@ -82,6 +83,16 @@ async function handleEnv(): Promise<void> {
|
|
|
82
83
|
);
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
async function handleTypecheck(): Promise<void> {
|
|
87
|
+
const env = await loadEnv();
|
|
88
|
+
const { runWorkspaceTypecheck } = await import("./lint");
|
|
89
|
+
const result = await runWorkspaceTypecheck({
|
|
90
|
+
root: env.root,
|
|
91
|
+
verbose: true,
|
|
92
|
+
});
|
|
93
|
+
process.exit(result.success ? 0 : 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
function showHelp(): void {
|
|
86
97
|
console.log(`
|
|
87
98
|
buncargo - Development environment CLI
|
|
@@ -91,6 +102,7 @@ USAGE:
|
|
|
91
102
|
|
|
92
103
|
COMMANDS:
|
|
93
104
|
dev Start the development environment
|
|
105
|
+
typecheck Run TypeScript typecheck across workspaces
|
|
94
106
|
prisma <args> Run Prisma CLI with correct DATABASE_URL
|
|
95
107
|
env Print environment info as JSON
|
|
96
108
|
help Show this help message
|
|
@@ -102,11 +114,11 @@ DEV OPTIONS:
|
|
|
102
114
|
--reset Stop containers and remove volumes
|
|
103
115
|
--migrate Run migrations only
|
|
104
116
|
--seed Run seeders
|
|
105
|
-
--lint Run typecheck (no Docker required)
|
|
106
117
|
|
|
107
118
|
EXAMPLES:
|
|
108
119
|
bunx buncargo dev # Start everything
|
|
109
120
|
bunx buncargo dev --down # Stop containers
|
|
121
|
+
bunx buncargo typecheck # Run typecheck
|
|
110
122
|
bunx buncargo prisma studio # Open Prisma Studio
|
|
111
123
|
bunx buncargo env # Get ports/urls as JSON
|
|
112
124
|
|
|
@@ -157,6 +169,10 @@ async function main(): Promise<void> {
|
|
|
157
169
|
await handleDev(commandArgs);
|
|
158
170
|
break;
|
|
159
171
|
|
|
172
|
+
case "typecheck":
|
|
173
|
+
await handleTypecheck();
|
|
174
|
+
break;
|
|
175
|
+
|
|
160
176
|
case "prisma":
|
|
161
177
|
await handlePrisma(commandArgs);
|
|
162
178
|
break;
|
package/cli.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { killProcessesOnAppPorts } from "./core/process";
|
|
2
3
|
import { spawnWatchdog, startHeartbeat, stopHeartbeat } from "./core/watchdog";
|
|
3
4
|
import type {
|
|
4
5
|
AppConfig,
|
|
@@ -40,16 +41,6 @@ export async function runCli<
|
|
|
40
41
|
// Log environment info
|
|
41
42
|
env.logInfo();
|
|
42
43
|
|
|
43
|
-
// Handle --lint (no Docker required)
|
|
44
|
-
if (args.includes("--lint")) {
|
|
45
|
-
const { runWorkspaceTypecheck } = await import("./lint");
|
|
46
|
-
const result = await runWorkspaceTypecheck({
|
|
47
|
-
root: env.root,
|
|
48
|
-
verbose: true,
|
|
49
|
-
});
|
|
50
|
-
process.exit(result.success ? 0 : 1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
44
|
// Handle --down
|
|
54
45
|
if (args.includes("--down")) {
|
|
55
46
|
await env.stop();
|
|
@@ -119,6 +110,9 @@ export async function runCli<
|
|
|
119
110
|
return;
|
|
120
111
|
}
|
|
121
112
|
|
|
113
|
+
// Kill any existing processes on app ports before starting
|
|
114
|
+
await killProcessesOnAppPorts(env.apps, env.ports);
|
|
115
|
+
|
|
122
116
|
// Start dev servers interactively
|
|
123
117
|
console.log("");
|
|
124
118
|
console.log("🔧 Starting dev servers...");
|
package/core/process.ts
CHANGED
|
@@ -369,6 +369,42 @@ export async function killProcessOnPortAndWait(
|
|
|
369
369
|
return released;
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Kill any existing processes using the specified app ports.
|
|
374
|
+
* Used before starting dev servers to ensure ports are available.
|
|
375
|
+
*/
|
|
376
|
+
export async function killProcessesOnAppPorts(
|
|
377
|
+
apps: Record<string, AppConfig>,
|
|
378
|
+
ports: Record<string, number>,
|
|
379
|
+
options: { verbose?: boolean } = {},
|
|
380
|
+
): Promise<void> {
|
|
381
|
+
const { verbose = true } = options;
|
|
382
|
+
const appNames = Object.keys(apps);
|
|
383
|
+
if (appNames.length === 0) return;
|
|
384
|
+
|
|
385
|
+
let killedAny = false;
|
|
386
|
+
|
|
387
|
+
for (const name of appNames) {
|
|
388
|
+
const port = ports[name];
|
|
389
|
+
if (port === undefined) continue;
|
|
390
|
+
|
|
391
|
+
const existingPid = getProcessOnPort(port);
|
|
392
|
+
if (existingPid !== null) {
|
|
393
|
+
if (!killedAny && verbose) {
|
|
394
|
+
console.log("");
|
|
395
|
+
killedAny = true;
|
|
396
|
+
}
|
|
397
|
+
if (verbose) {
|
|
398
|
+
console.log(`⚠️ Port ${port} (${name}) is in use by process ${existingPid}`);
|
|
399
|
+
}
|
|
400
|
+
const killed = await killProcessOnPortAndWait(port, { verbose });
|
|
401
|
+
if (!killed && verbose) {
|
|
402
|
+
console.log(` ⚠️ Could not kill process on port ${port}, server may fail to start`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
372
408
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
373
409
|
// Process Management
|
|
374
410
|
// ═══════════════════════════════════════════════════════════════════════════
|
package/dist/bin.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* bunx buncargo dev # Start containers + dev servers
|
|
7
7
|
* bunx buncargo dev --down # Stop containers
|
|
8
8
|
* bunx buncargo dev --reset # Stop + remove volumes
|
|
9
|
+
* bunx buncargo typecheck # Run TypeScript typecheck
|
|
9
10
|
* bunx buncargo prisma ... # Run prisma commands
|
|
10
11
|
* bunx buncargo help # Show help
|
|
11
12
|
*/
|
package/dist/bin.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import {
|
|
3
3
|
runCli
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-8hbbj1mp.js";
|
|
5
5
|
import {
|
|
6
6
|
loadDevEnv
|
|
7
|
-
} from "./index-
|
|
8
|
-
import"./index-
|
|
9
|
-
import"./index-
|
|
10
|
-
import"./index-
|
|
7
|
+
} from "./index-qw4093g2.js";
|
|
8
|
+
import"./index-2f47khe5.js";
|
|
9
|
+
import"./index-ggq3yryx.js";
|
|
10
|
+
import"./index-1yvbwj4k.js";
|
|
11
11
|
import"./index-tjqw9vtj.js";
|
|
12
12
|
import"./index-5hka0tff.js";
|
|
13
13
|
import"./index-2fr3g85b.js";
|
|
14
14
|
import"./index-6fm7mvwj.js";
|
|
15
15
|
import"./index-08wa79cs.js";
|
|
16
16
|
import {
|
|
17
|
-
__commonJS
|
|
17
|
+
__commonJS,
|
|
18
|
+
__require
|
|
18
19
|
} from "./index-qnx9j3qa.js";
|
|
19
20
|
|
|
20
21
|
// package.json
|
|
21
22
|
var require_package = __commonJS((exports, module) => {
|
|
22
23
|
module.exports = {
|
|
23
24
|
name: "buncargo",
|
|
24
|
-
version: "1.0.
|
|
25
|
+
version: "1.0.17",
|
|
25
26
|
description: "A Bun-powered development environment CLI for managing Docker Compose services, dev servers, and environment variables",
|
|
26
27
|
type: "module",
|
|
27
28
|
module: "./dist/index.js",
|
|
@@ -211,6 +212,15 @@ async function handleEnv() {
|
|
|
211
212
|
root: env.root
|
|
212
213
|
}, null, 2));
|
|
213
214
|
}
|
|
215
|
+
async function handleTypecheck() {
|
|
216
|
+
const env = await loadEnv();
|
|
217
|
+
const { runWorkspaceTypecheck } = await import("./lint.js");
|
|
218
|
+
const result = await runWorkspaceTypecheck({
|
|
219
|
+
root: env.root,
|
|
220
|
+
verbose: true
|
|
221
|
+
});
|
|
222
|
+
process.exit(result.success ? 0 : 1);
|
|
223
|
+
}
|
|
214
224
|
function showHelp() {
|
|
215
225
|
console.log(`
|
|
216
226
|
buncargo - Development environment CLI
|
|
@@ -220,6 +230,7 @@ USAGE:
|
|
|
220
230
|
|
|
221
231
|
COMMANDS:
|
|
222
232
|
dev Start the development environment
|
|
233
|
+
typecheck Run TypeScript typecheck across workspaces
|
|
223
234
|
prisma <args> Run Prisma CLI with correct DATABASE_URL
|
|
224
235
|
env Print environment info as JSON
|
|
225
236
|
help Show this help message
|
|
@@ -231,11 +242,11 @@ DEV OPTIONS:
|
|
|
231
242
|
--reset Stop containers and remove volumes
|
|
232
243
|
--migrate Run migrations only
|
|
233
244
|
--seed Run seeders
|
|
234
|
-
--lint Run typecheck (no Docker required)
|
|
235
245
|
|
|
236
246
|
EXAMPLES:
|
|
237
247
|
bunx buncargo dev # Start everything
|
|
238
248
|
bunx buncargo dev --down # Stop containers
|
|
249
|
+
bunx buncargo typecheck # Run typecheck
|
|
239
250
|
bunx buncargo prisma studio # Open Prisma Studio
|
|
240
251
|
bunx buncargo env # Get ports/urls as JSON
|
|
241
252
|
|
|
@@ -271,6 +282,9 @@ async function main() {
|
|
|
271
282
|
case "dev":
|
|
272
283
|
await handleDev(commandArgs);
|
|
273
284
|
break;
|
|
285
|
+
case "typecheck":
|
|
286
|
+
await handleTypecheck();
|
|
287
|
+
break;
|
|
274
288
|
case "prisma":
|
|
275
289
|
await handlePrisma(commandArgs);
|
|
276
290
|
break;
|
package/dist/cli.js
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
getFlagValue,
|
|
3
3
|
hasFlag,
|
|
4
4
|
runCli
|
|
5
|
-
} from "./index-
|
|
6
|
-
import"./index-
|
|
7
|
-
import"./index-
|
|
5
|
+
} from "./index-8hbbj1mp.js";
|
|
6
|
+
import"./index-ggq3yryx.js";
|
|
7
|
+
import"./index-1yvbwj4k.js";
|
|
8
8
|
import"./index-qnx9j3qa.js";
|
|
9
9
|
export {
|
|
10
10
|
runCli,
|
package/dist/core/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
startHeartbeat,
|
|
10
10
|
stopHeartbeat,
|
|
11
11
|
stopWatchdog
|
|
12
|
-
} from "../index-
|
|
12
|
+
} from "../index-ggq3yryx.js";
|
|
13
13
|
import {
|
|
14
14
|
buildApps,
|
|
15
15
|
exec,
|
|
@@ -19,11 +19,12 @@ import {
|
|
|
19
19
|
isProcessAlive,
|
|
20
20
|
killProcessOnPort,
|
|
21
21
|
killProcessOnPortAndWait,
|
|
22
|
+
killProcessesOnAppPorts,
|
|
22
23
|
spawnDevServer,
|
|
23
24
|
startDevServers,
|
|
24
25
|
stopAllProcesses,
|
|
25
26
|
stopProcess
|
|
26
|
-
} from "../index-
|
|
27
|
+
} from "../index-1yvbwj4k.js";
|
|
27
28
|
import {
|
|
28
29
|
MAX_ATTEMPTS,
|
|
29
30
|
POLL_INTERVAL,
|
|
@@ -82,6 +83,7 @@ export {
|
|
|
82
83
|
logFrontendPort,
|
|
83
84
|
logExpoApiUrl,
|
|
84
85
|
logApiUrl,
|
|
86
|
+
killProcessesOnAppPorts,
|
|
85
87
|
killProcessOnPortAndWait,
|
|
86
88
|
killProcessOnPort,
|
|
87
89
|
isWorktree,
|
package/dist/core/process.d.ts
CHANGED
|
@@ -64,6 +64,13 @@ export declare function killProcessOnPortAndWait(port: number, options?: {
|
|
|
64
64
|
verbose?: boolean;
|
|
65
65
|
timeout?: number;
|
|
66
66
|
}): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Kill any existing processes using the specified app ports.
|
|
69
|
+
* Used before starting dev servers to ensure ports are available.
|
|
70
|
+
*/
|
|
71
|
+
export declare function killProcessesOnAppPorts(apps: Record<string, AppConfig>, ports: Record<string, number>, options?: {
|
|
72
|
+
verbose?: boolean;
|
|
73
|
+
}): Promise<void>;
|
|
67
74
|
/**
|
|
68
75
|
* Stop a process by PID.
|
|
69
76
|
*/
|
package/dist/core/process.js
CHANGED
|
@@ -7,17 +7,19 @@ import {
|
|
|
7
7
|
isProcessAlive,
|
|
8
8
|
killProcessOnPort,
|
|
9
9
|
killProcessOnPortAndWait,
|
|
10
|
+
killProcessesOnAppPorts,
|
|
10
11
|
spawnDevServer,
|
|
11
12
|
startDevServers,
|
|
12
13
|
stopAllProcesses,
|
|
13
14
|
stopProcess
|
|
14
|
-
} from "../index-
|
|
15
|
+
} from "../index-1yvbwj4k.js";
|
|
15
16
|
import"../index-qnx9j3qa.js";
|
|
16
17
|
export {
|
|
17
18
|
stopProcess,
|
|
18
19
|
stopAllProcesses,
|
|
19
20
|
startDevServers,
|
|
20
21
|
spawnDevServer,
|
|
22
|
+
killProcessesOnAppPorts,
|
|
21
23
|
killProcessOnPortAndWait,
|
|
22
24
|
killProcessOnPort,
|
|
23
25
|
isProcessAlive,
|
package/dist/core/watchdog.js
CHANGED
package/dist/environment.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createDevEnvironment
|
|
3
|
-
} from "./index-
|
|
4
|
-
import"./index-
|
|
5
|
-
import"./index-
|
|
3
|
+
} from "./index-2f47khe5.js";
|
|
4
|
+
import"./index-ggq3yryx.js";
|
|
5
|
+
import"./index-1yvbwj4k.js";
|
|
6
6
|
import"./index-tjqw9vtj.js";
|
|
7
7
|
import"./index-5hka0tff.js";
|
|
8
8
|
import"./index-2fr3g85b.js";
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// core/process.ts
|
|
2
|
+
import {
|
|
3
|
+
execSync,
|
|
4
|
+
spawn
|
|
5
|
+
} from "node:child_process";
|
|
6
|
+
import { platform } from "node:os";
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
function exec(cmd, root, envVars, options = {}) {
|
|
9
|
+
const { cwd, verbose = false, env = {}, throwOnError = true } = options;
|
|
10
|
+
const workingDir = cwd ? resolve(root, cwd) : root;
|
|
11
|
+
const fullEnv = { ...process.env, ...envVars, ...env };
|
|
12
|
+
try {
|
|
13
|
+
const stdout = execSync(cmd, {
|
|
14
|
+
cwd: workingDir,
|
|
15
|
+
env: fullEnv,
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: verbose ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
exitCode: 0,
|
|
21
|
+
stdout: typeof stdout === "string" ? stdout : "",
|
|
22
|
+
stderr: ""
|
|
23
|
+
};
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const execError = error;
|
|
26
|
+
const result = {
|
|
27
|
+
exitCode: execError.status ?? 1,
|
|
28
|
+
stdout: execError.stdout ?? "",
|
|
29
|
+
stderr: execError.stderr ?? ""
|
|
30
|
+
};
|
|
31
|
+
if (throwOnError) {
|
|
32
|
+
throw new Error(`Command failed with exit code ${result.exitCode}: ${cmd}
|
|
33
|
+
${result.stderr}`);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function execAsync(cmd, root, envVars, options = {}) {
|
|
39
|
+
return new Promise((resolve2) => {
|
|
40
|
+
const result = exec(cmd, root, envVars, {
|
|
41
|
+
...options,
|
|
42
|
+
throwOnError: false
|
|
43
|
+
});
|
|
44
|
+
resolve2(result);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function spawnDevServer(command, root, appCwd, envVars, options = {}) {
|
|
48
|
+
const {
|
|
49
|
+
verbose = false,
|
|
50
|
+
detached = true,
|
|
51
|
+
isCI = false,
|
|
52
|
+
killExisting = true,
|
|
53
|
+
port
|
|
54
|
+
} = options;
|
|
55
|
+
if (killExisting && port !== undefined) {
|
|
56
|
+
const existingPid = getProcessOnPort(port);
|
|
57
|
+
if (existingPid !== null) {
|
|
58
|
+
if (verbose) {
|
|
59
|
+
console.log(` ⚠️ Port ${port} is in use by process ${existingPid}`);
|
|
60
|
+
}
|
|
61
|
+
await killProcessOnPortAndWait(port, { verbose });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const parts = command.split(" ");
|
|
65
|
+
const cmd = parts[0];
|
|
66
|
+
const args = parts.slice(1);
|
|
67
|
+
if (!cmd) {
|
|
68
|
+
throw new Error("Command cannot be empty");
|
|
69
|
+
}
|
|
70
|
+
const workingDir = appCwd ? resolve(root, appCwd) : root;
|
|
71
|
+
const spawnOptions = {
|
|
72
|
+
cwd: workingDir,
|
|
73
|
+
env: { ...process.env, ...envVars },
|
|
74
|
+
detached,
|
|
75
|
+
stdio: isCI || verbose ? "inherit" : "ignore"
|
|
76
|
+
};
|
|
77
|
+
const proc = spawn(cmd, args, spawnOptions);
|
|
78
|
+
if (detached && proc.unref) {
|
|
79
|
+
proc.unref();
|
|
80
|
+
}
|
|
81
|
+
return proc;
|
|
82
|
+
}
|
|
83
|
+
async function startDevServers(apps, root, envVars, ports, options = {}) {
|
|
84
|
+
const {
|
|
85
|
+
verbose = true,
|
|
86
|
+
productionBuild = false,
|
|
87
|
+
isCI = false,
|
|
88
|
+
killExisting = true
|
|
89
|
+
} = options;
|
|
90
|
+
const pids = {};
|
|
91
|
+
if (verbose) {
|
|
92
|
+
console.log(productionBuild ? "\uD83D\uDE80 Starting production servers..." : "\uD83D\uDD27 Starting dev servers...");
|
|
93
|
+
}
|
|
94
|
+
for (const [name, config] of Object.entries(apps)) {
|
|
95
|
+
const command = productionBuild ? config.prodCommand ?? config.devCommand : config.devCommand;
|
|
96
|
+
const port = ports[name];
|
|
97
|
+
const proc = await spawnDevServer(command, root, config.cwd, envVars, {
|
|
98
|
+
verbose,
|
|
99
|
+
isCI,
|
|
100
|
+
killExisting,
|
|
101
|
+
port
|
|
102
|
+
});
|
|
103
|
+
if (proc.pid) {
|
|
104
|
+
pids[name] = proc.pid;
|
|
105
|
+
if (verbose) {
|
|
106
|
+
console.log(` ${name} PID: ${proc.pid}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return pids;
|
|
111
|
+
}
|
|
112
|
+
function getProcessOnPort(port) {
|
|
113
|
+
try {
|
|
114
|
+
const os = platform();
|
|
115
|
+
let output;
|
|
116
|
+
if (os === "win32") {
|
|
117
|
+
output = execSync(`netstat -ano | findstr :${port}`, {
|
|
118
|
+
encoding: "utf-8",
|
|
119
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
120
|
+
});
|
|
121
|
+
const lines = output.trim().split(`
|
|
122
|
+
`);
|
|
123
|
+
for (const line of lines) {
|
|
124
|
+
if (line.includes("LISTENING")) {
|
|
125
|
+
const parts = line.trim().split(/\s+/);
|
|
126
|
+
const pid = Number.parseInt(parts[parts.length - 1], 10);
|
|
127
|
+
if (!Number.isNaN(pid) && pid > 0) {
|
|
128
|
+
return pid;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
output = execSync(`lsof -ti :${port}`, {
|
|
134
|
+
encoding: "utf-8",
|
|
135
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
136
|
+
});
|
|
137
|
+
const pid = Number.parseInt(output.trim().split(`
|
|
138
|
+
`)[0], 10);
|
|
139
|
+
if (!Number.isNaN(pid) && pid > 0) {
|
|
140
|
+
return pid;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function isPortInUse(port) {
|
|
149
|
+
return getProcessOnPort(port) !== null;
|
|
150
|
+
}
|
|
151
|
+
function killProcessOnPort(port, options = {}) {
|
|
152
|
+
const { verbose = false, signal = "SIGTERM" } = options;
|
|
153
|
+
const pid = getProcessOnPort(port);
|
|
154
|
+
if (pid === null) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
if (verbose) {
|
|
159
|
+
console.log(` Killing process ${pid} on port ${port}`);
|
|
160
|
+
}
|
|
161
|
+
process.kill(pid, signal);
|
|
162
|
+
return true;
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function killProcessOnPortAndWait(port, options = {}) {
|
|
168
|
+
const { verbose = false, timeout = 5000 } = options;
|
|
169
|
+
const pid = getProcessOnPort(port);
|
|
170
|
+
if (pid === null) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (verbose) {
|
|
174
|
+
console.log(` Killing process ${pid} on port ${port}...`);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
process.kill(pid, "SIGTERM");
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const startTime = Date.now();
|
|
182
|
+
const checkInterval = 100;
|
|
183
|
+
while (Date.now() - startTime < timeout) {
|
|
184
|
+
await new Promise((resolve2) => setTimeout(resolve2, checkInterval));
|
|
185
|
+
if (!isPortInUse(port)) {
|
|
186
|
+
if (verbose) {
|
|
187
|
+
console.log(` ✓ Port ${port} released`);
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (verbose) {
|
|
193
|
+
console.log(` Process ${pid} didn't exit, sending SIGKILL...`);
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
process.kill(pid, "SIGKILL");
|
|
197
|
+
} catch {}
|
|
198
|
+
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
199
|
+
const released = !isPortInUse(port);
|
|
200
|
+
if (verbose) {
|
|
201
|
+
if (released) {
|
|
202
|
+
console.log(` ✓ Port ${port} released after SIGKILL`);
|
|
203
|
+
} else {
|
|
204
|
+
console.log(` ⚠ Port ${port} still in use`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return released;
|
|
208
|
+
}
|
|
209
|
+
async function killProcessesOnAppPorts(apps, ports, options = {}) {
|
|
210
|
+
const { verbose = true } = options;
|
|
211
|
+
const appNames = Object.keys(apps);
|
|
212
|
+
if (appNames.length === 0)
|
|
213
|
+
return;
|
|
214
|
+
let killedAny = false;
|
|
215
|
+
for (const name of appNames) {
|
|
216
|
+
const port = ports[name];
|
|
217
|
+
if (port === undefined)
|
|
218
|
+
continue;
|
|
219
|
+
const existingPid = getProcessOnPort(port);
|
|
220
|
+
if (existingPid !== null) {
|
|
221
|
+
if (!killedAny && verbose) {
|
|
222
|
+
console.log("");
|
|
223
|
+
killedAny = true;
|
|
224
|
+
}
|
|
225
|
+
if (verbose) {
|
|
226
|
+
console.log(`⚠️ Port ${port} (${name}) is in use by process ${existingPid}`);
|
|
227
|
+
}
|
|
228
|
+
const killed = await killProcessOnPortAndWait(port, { verbose });
|
|
229
|
+
if (!killed && verbose) {
|
|
230
|
+
console.log(` ⚠️ Could not kill process on port ${port}, server may fail to start`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function stopProcess(pid) {
|
|
236
|
+
try {
|
|
237
|
+
process.kill(pid, "SIGTERM");
|
|
238
|
+
} catch {}
|
|
239
|
+
}
|
|
240
|
+
function stopAllProcesses(pids, options = {}) {
|
|
241
|
+
const { verbose = true } = options;
|
|
242
|
+
for (const [name, pid] of Object.entries(pids)) {
|
|
243
|
+
if (pid) {
|
|
244
|
+
if (verbose)
|
|
245
|
+
console.log(` Stopping ${name} (PID: ${pid})`);
|
|
246
|
+
stopProcess(pid);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function isProcessAlive(pid) {
|
|
251
|
+
try {
|
|
252
|
+
process.kill(pid, 0);
|
|
253
|
+
return true;
|
|
254
|
+
} catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function buildApps(apps, root, envVars, options = {}) {
|
|
259
|
+
const { verbose = true } = options;
|
|
260
|
+
for (const [name, config] of Object.entries(apps)) {
|
|
261
|
+
if (config.buildCommand) {
|
|
262
|
+
if (verbose)
|
|
263
|
+
console.log(`\uD83D\uDD28 Building ${name}...`);
|
|
264
|
+
exec(config.buildCommand, root, envVars, {
|
|
265
|
+
cwd: config.cwd,
|
|
266
|
+
verbose
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (verbose)
|
|
271
|
+
console.log("✓ Build complete");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export { exec, execAsync, spawnDevServer, startDevServers, getProcessOnPort, isPortInUse, killProcessOnPort, killProcessOnPortAndWait, killProcessesOnAppPorts, stopProcess, stopAllProcesses, isProcessAlive, buildApps };
|