|
1 | 1 | import * as child_process from "node:child_process"; |
2 | 2 | import * as fs from "node:fs/promises"; |
3 | 3 | import * as path from "node:path"; |
| 4 | +import { execPath } from "node:process"; |
4 | 5 | import { bsc_exe, rescript_legacy_exe } from "#cli/bins"; |
5 | 6 |
|
| 7 | +// On GitHub Actions runners, `npm` is not a native binary but a shell script |
| 8 | +// installed alongside the `node` executable in the hostedtoolcache directory. |
| 9 | +// Spawning it directly by name (`spawn("npm")`) can fail with ENOENT because |
| 10 | +// the PATH in CI sometimes gets polluted with bogus entries (e.g. `/tmp/xfs-*` |
| 11 | +// prepended), causing `execvp` to stop searching before reaching the real npm. |
| 12 | +// To avoid relying on PATH resolution, we resolve `npm` relative to |
| 13 | +// `process.execPath` (the current Node binary) and run it with `shell: true` |
| 14 | +// so the shebang (`#!/usr/bin/env node`) inside the npm script is honoured. |
| 15 | +const npmBin = path.join(path.dirname(execPath), "npm"); |
| 16 | + |
6 | 17 | /** |
7 | 18 | * @typedef {{ |
8 | 19 | * throwOnFail?: boolean, |
@@ -52,6 +63,7 @@ export function setup(cwd = process.cwd()) { |
52 | 63 |
|
53 | 64 | if (process.env.CI) { |
54 | 65 | console.log("[exec] PATH =", options.env?.PATH ?? process.env.PATH); |
| 66 | + console.log("[npm] using", npmBin); |
55 | 67 | } |
56 | 68 |
|
57 | 69 | const stdoutChunks = []; |
@@ -256,7 +268,10 @@ export function setup(cwd = process.cwd()) { |
256 | 268 | * @return {Promise<ExecResult>} |
257 | 269 | */ |
258 | 270 | npm(args = [], options = {}) { |
259 | | - return exec("npm", args, options); |
| 271 | + return exec(npmBin, args, { |
| 272 | + ...options, |
| 273 | + shell: process.platform !== "win32" || options.shell, |
| 274 | + }); |
260 | 275 | }, |
261 | 276 | }; |
262 | 277 | } |
0 commit comments