builderman 1.5.2 → 1.5.3
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/dist/internal/task-executor.js +33 -11
- package/dist/internal/util.d.ts +16 -0
- package/dist/internal/util.js +49 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { $TASK_INTERNAL, $PIPELINE_INTERNAL } from "./constants.js";
|
|
4
4
|
import { PipelineError } from "../errors.js";
|
|
5
|
-
import { parseCommandLine } from "./util.js";
|
|
5
|
+
import { parseCommandLine, resolveExecutable } from "./util.js";
|
|
6
6
|
/**
|
|
7
7
|
* Executes a task (either a regular task or a nested pipeline).
|
|
8
8
|
*/
|
|
@@ -229,23 +229,45 @@ function executeRegularTask(task, taskId, taskName, context, callbacks) {
|
|
|
229
229
|
callbacks.onTaskFailed(taskId, pipelineError);
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
|
-
const accumulatedPath = [
|
|
233
|
-
path.join(taskCwd, "node_modules", ".bin"),
|
|
234
|
-
path.join(process.cwd(), "node_modules", ".bin"),
|
|
235
|
-
process.env.PATH,
|
|
236
|
-
]
|
|
237
|
-
.filter(Boolean)
|
|
238
|
-
.join(process.platform === "win32" ? ";" : ":");
|
|
239
232
|
// Merge environment variables in order: process.env -> pipeline.env -> task.env -> command.env
|
|
240
233
|
const accumulatedEnv = {
|
|
241
234
|
...process.env,
|
|
242
|
-
PATH
|
|
243
|
-
Path: accumulatedPath,
|
|
235
|
+
// We'll compute PATH/Path below once we've built the accumulatedPath string
|
|
244
236
|
...config?.env,
|
|
245
237
|
...taskEnv,
|
|
246
238
|
...commandEnv,
|
|
247
239
|
};
|
|
248
|
-
const
|
|
240
|
+
const accumulatedPath = [
|
|
241
|
+
path.join(taskCwd, "node_modules", ".bin"),
|
|
242
|
+
path.join(process.cwd(), "node_modules", ".bin"),
|
|
243
|
+
process.env.PATH,
|
|
244
|
+
]
|
|
245
|
+
.filter(Boolean)
|
|
246
|
+
.join(process.platform === "win32" ? ";" : ":");
|
|
247
|
+
// Ensure PATH is set consistently (Windows is case-insensitive)
|
|
248
|
+
accumulatedEnv.PATH = accumulatedPath;
|
|
249
|
+
accumulatedEnv.Path = accumulatedPath;
|
|
250
|
+
let finalCmd;
|
|
251
|
+
let finalArgs;
|
|
252
|
+
if (process.platform === "win32" && !cmd.includes("\\") && !cmd.includes("/")) {
|
|
253
|
+
// On Windows, for bare commands like "pnpm" we delegate resolution to cmd.exe
|
|
254
|
+
// so that PATHEXT and other shell semantics are respected. This closely matches
|
|
255
|
+
// Node's spawn behavior when using shell: true, but keeps a consistent API.
|
|
256
|
+
finalCmd = process.env.ComSpec || "cmd.exe";
|
|
257
|
+
finalArgs = ["/d", "/s", "/c", commandString];
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Resolve executable from PATH (needed when shell: false)
|
|
261
|
+
const { cmd: resolvedCmd, needsCmdWrapper } = resolveExecutable(cmd, accumulatedPath);
|
|
262
|
+
finalCmd = resolvedCmd;
|
|
263
|
+
finalArgs = args;
|
|
264
|
+
// On Windows, .cmd and .bat files need to be run via cmd.exe when shell: false
|
|
265
|
+
if (needsCmdWrapper) {
|
|
266
|
+
finalCmd = process.env.ComSpec || "cmd.exe";
|
|
267
|
+
finalArgs = ["/c", resolvedCmd, ...args];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const child = spawnFn(finalCmd, finalArgs, {
|
|
249
271
|
cwd: taskCwd,
|
|
250
272
|
stdio: ["inherit", "pipe", "pipe"],
|
|
251
273
|
shell: false,
|
package/dist/internal/util.d.ts
CHANGED
|
@@ -14,3 +14,19 @@ export declare function parseCommandLine(command: string): {
|
|
|
14
14
|
cmd: string;
|
|
15
15
|
args: string[];
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Resolves an executable command from PATH.
|
|
19
|
+
* When using spawn with shell: false, Node.js doesn't automatically resolve
|
|
20
|
+
* executables from PATH like a shell would. This function searches PATH
|
|
21
|
+
* directories to find the executable.
|
|
22
|
+
*
|
|
23
|
+
* On Windows, .cmd and .bat files need to be executed via cmd.exe when using shell: false.
|
|
24
|
+
*
|
|
25
|
+
* @param cmd - The command name to resolve (e.g., "pnpm", "node")
|
|
26
|
+
* @param pathEnv - The PATH environment variable value
|
|
27
|
+
* @returns An object with the resolved command and whether it needs cmd.exe wrapper on Windows
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveExecutable(cmd: string, pathEnv: string | undefined): {
|
|
30
|
+
cmd: string;
|
|
31
|
+
needsCmdWrapper: boolean;
|
|
32
|
+
};
|
package/dist/internal/util.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import * as fs from "node:fs";
|
|
1
3
|
import { $TASK_INTERNAL } from "./constants.js";
|
|
2
4
|
export function validateTasks(tasks) {
|
|
3
5
|
if (tasks?.some((dep) => !($TASK_INTERNAL in dep))) {
|
|
@@ -54,3 +56,50 @@ export function parseCommandLine(command) {
|
|
|
54
56
|
const [cmd, ...args] = tokens;
|
|
55
57
|
return { cmd, args };
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolves an executable command from PATH.
|
|
61
|
+
* When using spawn with shell: false, Node.js doesn't automatically resolve
|
|
62
|
+
* executables from PATH like a shell would. This function searches PATH
|
|
63
|
+
* directories to find the executable.
|
|
64
|
+
*
|
|
65
|
+
* On Windows, .cmd and .bat files need to be executed via cmd.exe when using shell: false.
|
|
66
|
+
*
|
|
67
|
+
* @param cmd - The command name to resolve (e.g., "pnpm", "node")
|
|
68
|
+
* @param pathEnv - The PATH environment variable value
|
|
69
|
+
* @returns An object with the resolved command and whether it needs cmd.exe wrapper on Windows
|
|
70
|
+
*/
|
|
71
|
+
export function resolveExecutable(cmd, pathEnv) {
|
|
72
|
+
// If cmd is already an absolute path or contains path separators, return as-is
|
|
73
|
+
if (path.isAbsolute(cmd) || cmd.includes(path.sep)) {
|
|
74
|
+
return { cmd, needsCmdWrapper: false };
|
|
75
|
+
}
|
|
76
|
+
// If no PATH provided, return original cmd
|
|
77
|
+
if (!pathEnv) {
|
|
78
|
+
return { cmd, needsCmdWrapper: false };
|
|
79
|
+
}
|
|
80
|
+
const pathDirs = pathEnv.split(process.platform === "win32" ? ";" : ":");
|
|
81
|
+
// On Windows, check for .exe, .cmd, .bat extensions
|
|
82
|
+
const extensions = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
83
|
+
for (const dir of pathDirs) {
|
|
84
|
+
if (!dir)
|
|
85
|
+
continue;
|
|
86
|
+
for (const ext of extensions) {
|
|
87
|
+
const candidate = path.join(dir, `${cmd}${ext}`);
|
|
88
|
+
try {
|
|
89
|
+
// Check if file exists and is executable
|
|
90
|
+
// On Windows, we also need to check if it's a file (not a directory)
|
|
91
|
+
const stats = fs.statSync(candidate, { throwIfNoEntry: false });
|
|
92
|
+
if (stats && stats.isFile()) {
|
|
93
|
+
// On Windows, .cmd and .bat files need to be run via cmd.exe when shell: false
|
|
94
|
+
const needsCmdWrapper = process.platform === "win32" && (ext === ".cmd" || ext === ".bat");
|
|
95
|
+
return { cmd: candidate, needsCmdWrapper };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Continue searching
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If not found, return original cmd (spawn will handle the error)
|
|
104
|
+
return { cmd, needsCmdWrapper: false };
|
|
105
|
+
}
|