@take-out/scripts 0.1.38-1772433507984 → 0.1.38-1772439111798
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/package.json +2 -3
- package/src/helpers/run.ts +6 -79
- package/src/run-pty.mjs +24 -4
- package/src/run.ts +226 -161
- package/bin/run-group +0 -0
- package/src/run-group/.zig-cache/z/31f4371889dc4e502f87d8ad03235caa +0 -0
- package/src/run-group/build.zig +0 -24
- package/src/run-group/main.zig +0 -327
- package/src/run-group/run-group +0 -0
- package/src/run-group/run-group.c +0 -497
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@take-out/scripts",
|
|
3
|
-
"version": "0.1.38-
|
|
3
|
+
"version": "0.1.38-1772439111798",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/run.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"postinstall": "cd src/run-group && (clang -O2 -o run-group run-group.c 2>/dev/null || gcc -O2 -o run-group run-group.c 2>/dev/null || true)",
|
|
24
23
|
"lint": "oxlint src",
|
|
25
24
|
"lint:fix": "oxfmt src && oxlint --fix --fix-suggestions src",
|
|
26
25
|
"typecheck": "tko run typecheck"
|
|
@@ -31,7 +30,7 @@
|
|
|
31
30
|
"dependencies": {
|
|
32
31
|
"@clack/prompts": "^0.8.2",
|
|
33
32
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
34
|
-
"@take-out/helpers": "0.1.38-
|
|
33
|
+
"@take-out/helpers": "0.1.38-1772439111798",
|
|
35
34
|
"picocolors": "^1.1.1"
|
|
36
35
|
},
|
|
37
36
|
"peerDependencies": {
|
package/src/helpers/run.ts
CHANGED
|
@@ -1,39 +1,8 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from 'node:child_process'
|
|
2
|
-
import { existsSync } from 'node:fs'
|
|
3
2
|
import { cpus } from 'node:os'
|
|
4
|
-
import { dirname, resolve } from 'node:path'
|
|
5
|
-
import { fileURLToPath } from 'node:url'
|
|
6
3
|
|
|
7
4
|
import type { Timer } from '@take-out/helpers'
|
|
8
5
|
|
|
9
|
-
// find run-group binary for proper process group management
|
|
10
|
-
let _runGroupBin: string | null | undefined
|
|
11
|
-
function findRunGroupBinary(): string | null {
|
|
12
|
-
if (_runGroupBin !== undefined) return _runGroupBin
|
|
13
|
-
|
|
14
|
-
// check relative to this file (in packages/scripts/bin/)
|
|
15
|
-
try {
|
|
16
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
17
|
-
const localBin = resolve(__dirname, '..', 'bin', 'run-group')
|
|
18
|
-
if (existsSync(localBin)) {
|
|
19
|
-
_runGroupBin = localBin
|
|
20
|
-
return _runGroupBin
|
|
21
|
-
}
|
|
22
|
-
} catch {}
|
|
23
|
-
|
|
24
|
-
// check in node_modules
|
|
25
|
-
try {
|
|
26
|
-
const nmBin = resolve(process.cwd(), 'node_modules', '@take-out', 'scripts', 'bin', 'run-group')
|
|
27
|
-
if (existsSync(nmBin)) {
|
|
28
|
-
_runGroupBin = nmBin
|
|
29
|
-
return _runGroupBin
|
|
30
|
-
}
|
|
31
|
-
} catch {}
|
|
32
|
-
|
|
33
|
-
_runGroupBin = null
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
|
|
37
6
|
export type ProcessType = ChildProcess
|
|
38
7
|
export type ProcessHandler = (process: ProcessType) => void
|
|
39
8
|
|
|
@@ -50,39 +19,6 @@ export function getIsExiting() {
|
|
|
50
19
|
|
|
51
20
|
const processHandlers = new Set<ProcessHandler>()
|
|
52
21
|
|
|
53
|
-
// track all spawned child processes for cleanup on exit
|
|
54
|
-
const activeProcesses = new Set<ChildProcess>()
|
|
55
|
-
|
|
56
|
-
// install cleanup handlers once
|
|
57
|
-
let cleanupInstalled = false
|
|
58
|
-
function installCleanupHandlers() {
|
|
59
|
-
if (cleanupInstalled) return
|
|
60
|
-
cleanupInstalled = true
|
|
61
|
-
|
|
62
|
-
const cleanup = () => {
|
|
63
|
-
for (const proc of activeProcesses) {
|
|
64
|
-
try {
|
|
65
|
-
// kill the process group to ensure child processes are also killed
|
|
66
|
-
if (proc.pid) {
|
|
67
|
-
process.kill(-proc.pid, 'SIGTERM')
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
// process may already be dead
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
process.on('exit', cleanup)
|
|
76
|
-
process.on('SIGINT', () => {
|
|
77
|
-
cleanup()
|
|
78
|
-
process.exit(130)
|
|
79
|
-
})
|
|
80
|
-
process.on('SIGTERM', () => {
|
|
81
|
-
cleanup()
|
|
82
|
-
process.exit(143)
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
22
|
const colors = [
|
|
87
23
|
'\x1b[36m', // cyan
|
|
88
24
|
'\x1b[35m', // magenta
|
|
@@ -157,22 +93,13 @@ export async function run(
|
|
|
157
93
|
let timeoutId: Timer | undefined
|
|
158
94
|
let didTimeOut = false
|
|
159
95
|
|
|
160
|
-
// find run-group binary (handles process group cleanup properly)
|
|
161
|
-
const runGroupBin = findRunGroupBinary()
|
|
162
|
-
|
|
163
96
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
})
|
|
171
|
-
: spawn('bash', ['-c', command], {
|
|
172
|
-
env: { ...process.env, ...env },
|
|
173
|
-
cwd,
|
|
174
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
175
|
-
})
|
|
97
|
+
const shell = spawn('bash', ['-c', command], {
|
|
98
|
+
env: { ...process.env, ...env },
|
|
99
|
+
cwd,
|
|
100
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
101
|
+
detached: detached ?? false,
|
|
102
|
+
})
|
|
176
103
|
|
|
177
104
|
if (detached) {
|
|
178
105
|
shell.unref()
|
package/src/run-pty.mjs
CHANGED
|
@@ -258,7 +258,7 @@ function handleInput(data) {
|
|
|
258
258
|
pendingAction = null
|
|
259
259
|
console.log(match.shortcut)
|
|
260
260
|
match.killed = true
|
|
261
|
-
match.terminal.
|
|
261
|
+
killProcessTree(match.terminal.pid)
|
|
262
262
|
setTimeout(() => {
|
|
263
263
|
spawnScript(match.name, match.cwd, match.label, match.extraArgs, match.index)
|
|
264
264
|
console.log(`${getPrefix(match.index)} restarted`)
|
|
@@ -268,7 +268,7 @@ function handleInput(data) {
|
|
|
268
268
|
console.log(match.shortcut)
|
|
269
269
|
if (!match.killed) {
|
|
270
270
|
match.killed = true
|
|
271
|
-
match.terminal.
|
|
271
|
+
killProcessTree(match.terminal.pid)
|
|
272
272
|
console.log(`${getPrefix(match.index)} killed`)
|
|
273
273
|
}
|
|
274
274
|
} else {
|
|
@@ -286,6 +286,24 @@ function handleInput(data) {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
// kill entire process group to prevent orphans
|
|
290
|
+
function killProcessTree(pid) {
|
|
291
|
+
if (!pid) return
|
|
292
|
+
// try killing process group first (negative pid)
|
|
293
|
+
try {
|
|
294
|
+
process.kill(-pid, 'SIGTERM')
|
|
295
|
+
} catch {}
|
|
296
|
+
// also kill direct process
|
|
297
|
+
try {
|
|
298
|
+
process.kill(pid, 'SIGTERM')
|
|
299
|
+
} catch {}
|
|
300
|
+
// schedule force kill
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
try { process.kill(-pid, 'SIGKILL') } catch {}
|
|
303
|
+
try { process.kill(pid, 'SIGKILL') } catch {}
|
|
304
|
+
}, 100)
|
|
305
|
+
}
|
|
306
|
+
|
|
289
307
|
function cleanup() {
|
|
290
308
|
// restore terminal to cooked mode before exiting
|
|
291
309
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
@@ -299,10 +317,12 @@ function cleanup() {
|
|
|
299
317
|
for (const p of processes) {
|
|
300
318
|
if (!p.killed) {
|
|
301
319
|
p.killed = true
|
|
302
|
-
p.terminal.
|
|
320
|
+
killProcessTree(p.terminal.pid)
|
|
303
321
|
}
|
|
304
322
|
}
|
|
305
|
-
|
|
323
|
+
|
|
324
|
+
// wait for kills to complete before exit
|
|
325
|
+
setTimeout(() => process.exit(0), 150)
|
|
306
326
|
}
|
|
307
327
|
|
|
308
328
|
async function main() {
|
package/src/run.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @description Run multiple scripts in parallel
|
|
4
|
+
* @description Run multiple scripts in parallel
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { spawn } from 'node:child_process'
|
|
7
8
|
import fs from 'node:fs'
|
|
8
|
-
import {
|
|
9
|
-
import { fileURLToPath } from 'node:url'
|
|
9
|
+
import { join, relative, resolve } from 'node:path'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import { handleProcessExit } from '@take-out/scripts/helpers/handleProcessExit'
|
|
12
|
+
|
|
13
|
+
import { getIsExiting } from './helpers/run'
|
|
14
|
+
import { checkNodeVersion } from './node-version-check'
|
|
15
|
+
|
|
16
|
+
const colors = [
|
|
17
|
+
'\x1b[38;5;245m',
|
|
18
|
+
'\x1b[38;5;240m',
|
|
19
|
+
'\x1b[38;5;250m',
|
|
20
|
+
'\x1b[38;5;243m',
|
|
21
|
+
'\x1b[38;5;248m',
|
|
22
|
+
'\x1b[38;5;238m',
|
|
23
|
+
'\x1b[38;5;252m',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const reset = '\x1b[0m'
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line no-control-regex
|
|
29
|
+
const ansiPattern = /\x1b\[[0-9;]*m/g
|
|
12
30
|
|
|
13
31
|
const args = process.argv.slice(2)
|
|
14
32
|
const ownFlags = ['--no-root', '--bun', '--watch', '--flags=last']
|
|
@@ -32,22 +50,40 @@ for (let i = 0; i < args.length; i++) {
|
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
const noRoot = args.includes('--no-root')
|
|
53
|
+
const runBun = args.includes('--bun')
|
|
54
|
+
const watch = args.includes('--watch')
|
|
35
55
|
const flagsLast = args.includes('--flags=last')
|
|
36
56
|
|
|
57
|
+
const MAX_RESTARTS = 3
|
|
58
|
+
|
|
37
59
|
const parentRunningScripts = process.env.BUN_RUN_SCRIPTS
|
|
38
60
|
? process.env.BUN_RUN_SCRIPTS.split(',')
|
|
39
61
|
: []
|
|
40
62
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
process.exit(1)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface ResolvedCommand {
|
|
63
|
+
interface ManagedProcess {
|
|
64
|
+
proc: ReturnType<typeof spawn>
|
|
47
65
|
name: string
|
|
48
|
-
command: string
|
|
49
66
|
cwd: string
|
|
50
|
-
|
|
67
|
+
prefixLabel: string
|
|
68
|
+
extraArgs: string[]
|
|
69
|
+
index: number
|
|
70
|
+
shortcut: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const managedProcesses: ManagedProcess[] = []
|
|
74
|
+
const { addChildProcess, exit } = handleProcessExit()
|
|
75
|
+
|
|
76
|
+
function getPrefix(index: number): string {
|
|
77
|
+
const managed = managedProcesses[index]
|
|
78
|
+
if (!managed) return ''
|
|
79
|
+
const color = colors[index % colors.length]
|
|
80
|
+
const sc = managed.shortcut || String(index + 1)
|
|
81
|
+
return `${color}${sc} ${managed.prefixLabel}${reset}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (runCommands.length === 0) {
|
|
85
|
+
console.error('Please provide at least one script name to run')
|
|
86
|
+
exit(1)
|
|
51
87
|
}
|
|
52
88
|
|
|
53
89
|
async function readPackageJson(directoryPath: string) {
|
|
@@ -143,15 +179,6 @@ async function findWorkspaceDirectories(): Promise<string[]> {
|
|
|
143
179
|
})
|
|
144
180
|
}
|
|
145
181
|
|
|
146
|
-
async function getScriptCommand(
|
|
147
|
-
directoryPath: string,
|
|
148
|
-
scriptName: string
|
|
149
|
-
): Promise<string | null> {
|
|
150
|
-
const packageJson = await readPackageJson(directoryPath)
|
|
151
|
-
if (!packageJson?.scripts?.[scriptName]) return null
|
|
152
|
-
return packageJson.scripts[scriptName]
|
|
153
|
-
}
|
|
154
|
-
|
|
155
182
|
async function findAvailableScripts(
|
|
156
183
|
directoryPath: string,
|
|
157
184
|
scriptNames: string[]
|
|
@@ -167,180 +194,218 @@ async function findAvailableScripts(
|
|
|
167
194
|
)
|
|
168
195
|
}
|
|
169
196
|
|
|
170
|
-
async function
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// root scripts
|
|
175
|
-
if (!noRoot) {
|
|
176
|
-
const filteredCommands = runCommands.filter(
|
|
177
|
-
(name) => !parentRunningScripts.includes(name)
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
for (const name of filteredCommands) {
|
|
181
|
-
const command = await getScriptCommand('.', name)
|
|
182
|
-
if (command) {
|
|
183
|
-
const scriptArgs = !flagsLast || name === lastScript ? forwardArgs : []
|
|
184
|
-
commands.push({
|
|
185
|
-
name,
|
|
186
|
-
command,
|
|
187
|
-
cwd: '.',
|
|
188
|
-
args: scriptArgs,
|
|
189
|
-
})
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// workspace scripts
|
|
197
|
+
async function mapWorkspacesToScripts(
|
|
198
|
+
scriptNames: string[]
|
|
199
|
+
): Promise<Map<string, { scripts: string[]; packageName: string }>> {
|
|
195
200
|
const workspaceDirs = await findWorkspaceDirectories()
|
|
201
|
+
const workspaceScriptMap = new Map<string, { scripts: string[]; packageName: string }>()
|
|
196
202
|
|
|
197
203
|
for (const dir of workspaceDirs) {
|
|
198
|
-
const availableScripts = await findAvailableScripts(dir,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const packageName = packageJson?.name || dir
|
|
208
|
-
const scriptArgs = !flagsLast || scriptName === lastScript ? forwardArgs : []
|
|
209
|
-
|
|
210
|
-
commands.push({
|
|
211
|
-
name: `${packageName} ${scriptName}`,
|
|
212
|
-
command,
|
|
213
|
-
cwd: dir,
|
|
214
|
-
args: scriptArgs,
|
|
215
|
-
})
|
|
216
|
-
}
|
|
204
|
+
const availableScripts = await findAvailableScripts(dir, scriptNames)
|
|
205
|
+
|
|
206
|
+
if (availableScripts.length > 0) {
|
|
207
|
+
const packageJson = await readPackageJson(dir)
|
|
208
|
+
const packageName = packageJson?.name || dir
|
|
209
|
+
workspaceScriptMap.set(dir, {
|
|
210
|
+
scripts: availableScripts,
|
|
211
|
+
packageName,
|
|
212
|
+
})
|
|
217
213
|
}
|
|
218
214
|
}
|
|
219
215
|
|
|
220
|
-
return
|
|
216
|
+
return workspaceScriptMap
|
|
221
217
|
}
|
|
222
218
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
219
|
+
const runScript = async (
|
|
220
|
+
name: string,
|
|
221
|
+
cwd = '.',
|
|
222
|
+
prefixLabel: string = name,
|
|
223
|
+
restarts = 0,
|
|
224
|
+
extraArgs: string[] = [],
|
|
225
|
+
managedIndex?: number
|
|
226
|
+
) => {
|
|
227
|
+
const index = managedIndex ?? managedProcesses.length
|
|
228
|
+
|
|
229
|
+
const runArgs = ['run', '--silent', runBun ? '--bun' : '', name, ...extraArgs].filter(
|
|
230
|
+
Boolean
|
|
231
|
+
)
|
|
227
232
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
const allRunningScripts = [...parentRunningScripts, ...runCommands].join(',')
|
|
234
|
+
|
|
235
|
+
const proc = spawn('bun', runArgs, {
|
|
236
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
237
|
+
shell: false,
|
|
238
|
+
detached: true,
|
|
239
|
+
env: {
|
|
240
|
+
...process.env,
|
|
241
|
+
FORCE_COLOR: '3',
|
|
242
|
+
BUN_RUN_PARENT_SCRIPT: name,
|
|
243
|
+
BUN_RUN_SCRIPTS: allRunningScripts,
|
|
244
|
+
TKO_SILENT: '1',
|
|
245
|
+
} as any,
|
|
246
|
+
cwd: resolve(cwd),
|
|
247
|
+
})
|
|
231
248
|
|
|
232
|
-
|
|
233
|
-
|
|
249
|
+
const managed: ManagedProcess = {
|
|
250
|
+
proc,
|
|
251
|
+
name,
|
|
252
|
+
cwd,
|
|
253
|
+
prefixLabel,
|
|
254
|
+
extraArgs,
|
|
255
|
+
index,
|
|
256
|
+
shortcut: '',
|
|
257
|
+
}
|
|
234
258
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const colors = [
|
|
241
|
-
'\x1b[36m', '\x1b[35m', '\x1b[32m', '\x1b[33m', '\x1b[34m', '\x1b[31m',
|
|
242
|
-
]
|
|
243
|
-
const reset = '\x1b[0m'
|
|
244
|
-
|
|
245
|
-
const { addChildProcess } = handleProcessExit()
|
|
246
|
-
let exitCode = 0
|
|
247
|
-
|
|
248
|
-
const procs = commands.map((cmd, i) => {
|
|
249
|
-
const color = colors[i % colors.length]
|
|
250
|
-
const prefix = `${color}[${cmd.name}]${reset}`
|
|
251
|
-
const fullCwd = resolve(cmd.cwd)
|
|
252
|
-
const fullCommand = cmd.args.length > 0
|
|
253
|
-
? `${cmd.command} ${cmd.args.join(' ')}`
|
|
254
|
-
: cmd.command
|
|
255
|
-
|
|
256
|
-
const proc = spawn('sh', ['-c', fullCommand], {
|
|
257
|
-
cwd: fullCwd,
|
|
258
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
259
|
-
env: {
|
|
260
|
-
...process.env,
|
|
261
|
-
FORCE_COLOR: '3',
|
|
262
|
-
TKO_SILENT: '1',
|
|
263
|
-
BUN_RUN_SCRIPTS: runCommands.join(','),
|
|
264
|
-
},
|
|
265
|
-
})
|
|
259
|
+
if (managedIndex !== undefined) {
|
|
260
|
+
managedProcesses[managedIndex] = managed
|
|
261
|
+
} else {
|
|
262
|
+
managedProcesses.push(managed)
|
|
263
|
+
}
|
|
266
264
|
|
|
267
|
-
|
|
265
|
+
addChildProcess(proc)
|
|
268
266
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
proc.stdout!.on('data', (data) => {
|
|
268
|
+
if (getIsExiting()) return
|
|
269
|
+
const lines = data.toString().split('\n')
|
|
270
|
+
for (const line of lines) {
|
|
271
|
+
const stripped = line.replace(ansiPattern, '')
|
|
272
|
+
if (stripped.startsWith('$ ')) continue
|
|
273
|
+
if (line) console.info(`${getPrefix(index)} ${line}`)
|
|
274
|
+
}
|
|
275
|
+
})
|
|
274
276
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
proc.stderr!.on('data', (data) => {
|
|
278
|
+
if (getIsExiting()) return
|
|
279
|
+
const lines = data.toString().split('\n')
|
|
280
|
+
for (const line of lines) {
|
|
281
|
+
const stripped = line.replace(ansiPattern, '')
|
|
282
|
+
if (stripped.startsWith('$ ')) continue
|
|
283
|
+
if (line) console.error(`${getPrefix(index)} ${line}`)
|
|
284
|
+
}
|
|
285
|
+
})
|
|
280
286
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
})
|
|
287
|
+
proc.on('error', (error) => {
|
|
288
|
+
console.error(`${getPrefix(index)} Failed to start: ${error.message}`)
|
|
284
289
|
})
|
|
285
290
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
process.exit(exitCode)
|
|
289
|
-
}
|
|
291
|
+
proc.on('close', (code) => {
|
|
292
|
+
if (getIsExiting()) return
|
|
290
293
|
|
|
291
|
-
|
|
292
|
-
|
|
294
|
+
if (code && code !== 0) {
|
|
295
|
+
console.error(`${getPrefix(index)} Process exited with code ${code}`)
|
|
293
296
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
if (watch && restarts < MAX_RESTARTS) {
|
|
298
|
+
const newRestarts = restarts + 1
|
|
299
|
+
console.info(`Restarting process ${name} (${newRestarts}/${MAX_RESTARTS} times)`)
|
|
300
|
+
runScript(name, cwd, prefixLabel, newRestarts, extraArgs, index)
|
|
301
|
+
} else {
|
|
302
|
+
exit(1)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
})
|
|
298
306
|
|
|
299
|
-
|
|
307
|
+
return proc
|
|
308
|
+
}
|
|
300
309
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
310
|
+
function computeShortcuts() {
|
|
311
|
+
const initials = managedProcesses.map((p) => {
|
|
312
|
+
const words = p.prefixLabel
|
|
313
|
+
.toLowerCase()
|
|
314
|
+
.split(/[^a-z]+/)
|
|
315
|
+
.filter(Boolean)
|
|
316
|
+
return words.map((w) => w[0]).join('')
|
|
317
|
+
})
|
|
305
318
|
|
|
306
|
-
|
|
307
|
-
// format: run-group -p -t @name <cmd1> --- @name2 <cmd2> --- ...
|
|
308
|
-
const runGroupArgs: string[] = ['-p', '-t']
|
|
319
|
+
const lengths = new Array(managedProcesses.length).fill(1) as number[]
|
|
309
320
|
|
|
310
|
-
for (let
|
|
311
|
-
const
|
|
312
|
-
const fullCwd = resolve(cmd.cwd)
|
|
321
|
+
for (let round = 0; round < 5; round++) {
|
|
322
|
+
const shortcuts = initials.map((init, i) => init.slice(0, lengths[i]) || init)
|
|
313
323
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
324
|
+
let hasCollision = false
|
|
325
|
+
const groups = new Map<string, number[]>()
|
|
326
|
+
for (let i = 0; i < shortcuts.length; i++) {
|
|
327
|
+
const key = shortcuts[i]!
|
|
328
|
+
if (!groups.has(key)) groups.set(key, [])
|
|
329
|
+
groups.get(key)!.push(i)
|
|
330
|
+
}
|
|
320
331
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
332
|
+
for (const [, indices] of groups) {
|
|
333
|
+
if (indices.length <= 1) continue
|
|
334
|
+
hasCollision = true
|
|
335
|
+
for (const idx of indices) {
|
|
336
|
+
lengths[idx]!++
|
|
337
|
+
}
|
|
338
|
+
}
|
|
324
339
|
|
|
325
|
-
if (
|
|
326
|
-
|
|
340
|
+
if (!hasCollision) {
|
|
341
|
+
for (let i = 0; i < managedProcesses.length; i++) {
|
|
342
|
+
managedProcesses[i]!.shortcut = shortcuts[i]!
|
|
343
|
+
}
|
|
344
|
+
return
|
|
327
345
|
}
|
|
328
346
|
}
|
|
329
347
|
|
|
330
|
-
|
|
331
|
-
|
|
348
|
+
for (let i = 0; i < managedProcesses.length; i++) {
|
|
349
|
+
const sc = initials[i]!.slice(0, lengths[i]) || initials[i]!
|
|
350
|
+
managedProcesses[i]!.shortcut = sc || String(i + 1)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function main() {
|
|
355
|
+
checkNodeVersion().catch((err) => {
|
|
356
|
+
console.error(err.message)
|
|
357
|
+
exit(1)
|
|
358
|
+
})
|
|
332
359
|
|
|
333
360
|
try {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
361
|
+
if (runCommands.length > 0) {
|
|
362
|
+
const lastScript = runCommands[runCommands.length - 1]
|
|
363
|
+
|
|
364
|
+
if (!noRoot) {
|
|
365
|
+
const filteredCommands = runCommands.filter(
|
|
366
|
+
(name) => !parentRunningScripts.includes(name)
|
|
367
|
+
)
|
|
368
|
+
const scriptPromises = filteredCommands.map((name) => {
|
|
369
|
+
const scriptArgs = !flagsLast || name === lastScript ? forwardArgs : []
|
|
370
|
+
return runScript(name, '.', name, 0, scriptArgs)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
await Promise.all(scriptPromises)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const workspaceScriptMap = await mapWorkspacesToScripts(runCommands)
|
|
377
|
+
|
|
378
|
+
for (const [workspace, { scripts, packageName }] of workspaceScriptMap.entries()) {
|
|
379
|
+
const filteredScripts = scripts.filter(
|
|
380
|
+
(scriptName) => !parentRunningScripts.includes(scriptName)
|
|
381
|
+
)
|
|
382
|
+
const workspaceScriptPromises = filteredScripts.map((scriptName) => {
|
|
383
|
+
const scriptArgs = !flagsLast || scriptName === lastScript ? forwardArgs : []
|
|
384
|
+
return runScript(
|
|
385
|
+
scriptName,
|
|
386
|
+
workspace,
|
|
387
|
+
`${packageName} ${scriptName}`,
|
|
388
|
+
0,
|
|
389
|
+
scriptArgs
|
|
390
|
+
)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
await Promise.all(workspaceScriptPromises)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (managedProcesses.length === 0) {
|
|
398
|
+
exit(0)
|
|
399
|
+
} else {
|
|
400
|
+
computeShortcuts()
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error(`Error running scripts: ${error}`)
|
|
404
|
+
exit(1)
|
|
340
405
|
}
|
|
341
406
|
}
|
|
342
407
|
|
|
343
408
|
main().catch((error) => {
|
|
344
409
|
console.error(`Error running scripts: ${error}`)
|
|
345
|
-
|
|
410
|
+
exit(1)
|
|
346
411
|
})
|
package/bin/run-group
DELETED
|
Binary file
|
|
Binary file
|
package/src/run-group/build.zig
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
pub fn build(b: *std.Build) void {
|
|
4
|
-
const target = b.standardTargetOptions(.{});
|
|
5
|
-
const optimize = b.standardOptimizeOption(.{});
|
|
6
|
-
|
|
7
|
-
const exe = b.addExecutable(.{
|
|
8
|
-
.name = "run-group",
|
|
9
|
-
.root_source_file = b.path("main.zig"),
|
|
10
|
-
.target = target,
|
|
11
|
-
.optimize = optimize,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
b.installArtifact(exe);
|
|
15
|
-
|
|
16
|
-
const run_cmd = b.addRunArtifact(exe);
|
|
17
|
-
run_cmd.step.dependOn(b.getInstallStep());
|
|
18
|
-
if (b.args) |args| {
|
|
19
|
-
run_cmd.addArgs(args);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const run_step = b.step("run", "Run the app");
|
|
23
|
-
run_step.dependOn(&run_cmd.step);
|
|
24
|
-
}
|