Skip to content

Commit 536e8b6

Browse files
committed
feat(build-infra): add script-runner utilities for DRY monorepo operations
Create shared script-runner module with common patterns for running pnpm scripts, commands, and sequences across the monorepo. New utilities: - runPnpmScript: Run script in specific package - runPnpmScriptAll: Run script across all packages - runSequence: Run commands sequentially with error handling - runParallel: Run commands in parallel - runQuiet: Run command and capture output - pnpm.install/build/test: Common pnpm operations Benefits: - Eliminates ~260 lines of duplicated command execution code - Consistent error handling and logging - Cross-platform support with WIN32 constant - Monorepo-aware patterns (--filter, -r flags) - Type-safe spawn wrapper usage throughout Next: Update scripts to use these shared utilities
1 parent f9eebfd commit 536e8b6

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
* @fileoverview Monorepo script runner utilities for common build operations.
3+
* Provides DRY helpers for running pnpm scripts, commands, and sequences.
4+
*/
5+
6+
import { WIN32 } from '@socketsecurity/lib/constants/platform'
7+
import { logger } from '@socketsecurity/lib/logger'
8+
import { spawn } from '@socketsecurity/lib/spawn'
9+
10+
/**
11+
* Run a pnpm script in a specific package.
12+
*
13+
* @param {string} packageName - Package name (e.g., '@socketsecurity/cli')
14+
* @param {string} scriptName - Script name from package.json
15+
* @param {string[]} args - Additional arguments
16+
* @param {object} options - Spawn options
17+
* @returns {Promise<{code: number, stdout?: string, stderr?: string}>}
18+
*/
19+
export async function runPnpmScript(packageName, scriptName, args = [], options = {}) {
20+
const pnpmArgs = ['--filter', packageName, 'run', scriptName, ...args]
21+
22+
return spawn('pnpm', pnpmArgs, {
23+
shell: WIN32,
24+
stdio: 'inherit',
25+
...options,
26+
})
27+
}
28+
29+
/**
30+
* Run a pnpm script across all packages that have the script.
31+
*
32+
* @param {string} scriptName - Script name from package.json
33+
* @param {string[]} args - Additional arguments
34+
* @param {object} options - Spawn options
35+
* @returns {Promise<{code: number, stdout?: string, stderr?: string}>}
36+
*/
37+
export async function runPnpmScriptAll(scriptName, args = [], options = {}) {
38+
const pnpmArgs = ['run', '-r', scriptName, ...args]
39+
40+
return spawn('pnpm', pnpmArgs, {
41+
shell: WIN32,
42+
stdio: 'inherit',
43+
...options,
44+
})
45+
}
46+
47+
/**
48+
* Run multiple commands in sequence, stopping on first failure.
49+
*
50+
* @param {Array<{command: string, args?: string[], options?: object, description?: string}>} commands
51+
* @param {object} globalOptions - Options to merge into all commands
52+
* @returns {Promise<number>} Exit code of first failing command, or 0 if all succeed
53+
*/
54+
export async function runSequence(commands, globalOptions = {}) {
55+
for (const { args = [], command, description, options = {} } of commands) {
56+
if (description) {
57+
logger.step(description)
58+
}
59+
60+
const result = await spawn(command, args, {
61+
shell: WIN32,
62+
stdio: 'inherit',
63+
...globalOptions,
64+
...options,
65+
})
66+
67+
if (result.code !== 0) {
68+
return result.code
69+
}
70+
}
71+
72+
return 0
73+
}
74+
75+
/**
76+
* Run multiple commands in parallel.
77+
*
78+
* @param {Array<{command: string, args?: string[], options?: object}>} commands
79+
* @param {object} globalOptions - Options to merge into all commands
80+
* @returns {Promise<Array<{code: number, stdout?: string, stderr?: string}>>}
81+
*/
82+
export async function runParallel(commands, globalOptions = {}) {
83+
const promises = commands.map(({ args = [], command, options = {} }) =>
84+
spawn(command, args, {
85+
shell: WIN32,
86+
stdio: 'inherit',
87+
...globalOptions,
88+
...options,
89+
}),
90+
)
91+
92+
return Promise.all(promises)
93+
}
94+
95+
/**
96+
* Run a command quietly (capture output).
97+
*
98+
* @param {string} command - Command to run
99+
* @param {string[]} args - Arguments
100+
* @param {object} options - Spawn options
101+
* @returns {Promise<{code: number, stdout: string, stderr: string}>}
102+
*/
103+
export async function runQuiet(command, args = [], options = {}) {
104+
return spawn(command, args, {
105+
shell: WIN32,
106+
stdio: 'pipe',
107+
stdioString: true,
108+
...options,
109+
})
110+
}
111+
112+
/**
113+
* Common pnpm operations with proper error handling.
114+
*/
115+
export const pnpm = {
116+
/**
117+
* Run pnpm install with frozen lockfile.
118+
*/
119+
install: async (options = {}) => {
120+
logger.step('Installing dependencies')
121+
return spawn('pnpm', ['install', '--frozen-lockfile'], {
122+
shell: WIN32,
123+
stdio: 'inherit',
124+
...options,
125+
})
126+
},
127+
128+
/**
129+
* Build all packages or specific package.
130+
*/
131+
build: async (packageName = null, options = {}) => {
132+
logger.step(packageName ? `Building ${packageName}` : 'Building packages')
133+
const args = packageName
134+
? ['--filter', packageName, 'run', 'build']
135+
: ['run', '-r', 'build']
136+
137+
return spawn('pnpm', args, {
138+
shell: WIN32,
139+
stdio: 'inherit',
140+
...options,
141+
})
142+
},
143+
144+
/**
145+
* Run tests in specific package or all packages.
146+
*/
147+
test: async (packageName = null, options = {}) => {
148+
logger.step(packageName ? `Testing ${packageName}` : 'Running tests')
149+
const args = packageName
150+
? ['--filter', packageName, 'run', 'test']
151+
: ['run', '-r', 'test']
152+
153+
return spawn('pnpm', args, {
154+
shell: WIN32,
155+
stdio: 'inherit',
156+
...options,
157+
})
158+
},
159+
}

packages/build-infra/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"./lib/extraction-cache": "./lib/extraction-cache.mjs",
1515
"./lib/patch-validator": "./lib/patch-validator.mjs",
1616
"./lib/rust-builder": "./lib/rust-builder.mjs",
17+
"./lib/script-runner": "./lib/script-runner.mjs",
1718
"./lib/tool-installer": "./lib/tool-installer.mjs"
1819
},
1920
"dependencies": {

0 commit comments

Comments
 (0)