diff --git a/scripts/lib/bundled-runtime-deps-install.mjs b/scripts/lib/bundled-runtime-deps-install.mjs new file mode 100644 index 00000000000..730bc727bd8 --- /dev/null +++ b/scripts/lib/bundled-runtime-deps-install.mjs @@ -0,0 +1,66 @@ +import { spawnSync } from "node:child_process"; + +export function createNestedNpmInstallEnv(env = process.env) { + const nextEnv = { ...env }; + delete nextEnv.npm_config_global; + delete nextEnv.npm_config_location; + delete nextEnv.npm_config_prefix; + return nextEnv; +} + +export function createBundledRuntimeDependencyInstallEnv(env = process.env, options = {}) { + const nextEnv = { + ...createNestedNpmInstallEnv(env), + npm_config_dry_run: "false", + npm_config_fetch_retries: env.npm_config_fetch_retries ?? "5", + npm_config_fetch_retry_maxtimeout: env.npm_config_fetch_retry_maxtimeout ?? "120000", + npm_config_fetch_retry_mintimeout: env.npm_config_fetch_retry_mintimeout ?? "10000", + npm_config_fetch_timeout: env.npm_config_fetch_timeout ?? "300000", + npm_config_legacy_peer_deps: "true", + npm_config_package_lock: "false", + npm_config_save: "false", + }; + if (options.ci) { + nextEnv.CI = "1"; + } + if (options.quiet) { + Object.assign(nextEnv, { + npm_config_audit: "false", + npm_config_fund: "false", + npm_config_loglevel: "error", + npm_config_progress: "false", + npm_config_yes: "true", + }); + } + return nextEnv; +} + +export function createBundledRuntimeDependencyInstallArgs(specs = [], options = {}) { + return [ + "install", + ...(options.noAudit ? ["--no-audit"] : []), + ...(options.noFund ? ["--no-fund"] : []), + "--ignore-scripts", + ...(options.silent ? ["--silent"] : []), + ...specs, + ]; +} + +export function runBundledRuntimeDependencyNpmInstall(params) { + const runSpawnSync = params.spawnSyncImpl ?? spawnSync; + const result = runSpawnSync(params.npmRunner.command, params.npmRunner.args, { + cwd: params.cwd, + encoding: "utf8", + env: params.env ?? params.npmRunner.env ?? process.env, + shell: params.npmRunner.shell, + stdio: params.stdio ?? "pipe", + ...(params.timeoutMs ? { timeout: params.timeoutMs } : {}), + windowsHide: true, + windowsVerbatimArguments: params.npmRunner.windowsVerbatimArguments, + }); + if (result.status === 0) { + return; + } + const output = [result.stderr, result.stdout].filter(Boolean).join("\n").trim(); + throw new Error(output || "npm install failed"); +} diff --git a/scripts/postinstall-bundled-plugins.mjs b/scripts/postinstall-bundled-plugins.mjs index f49158ef32d..a4a1ad50527 100644 --- a/scripts/postinstall-bundled-plugins.mjs +++ b/scripts/postinstall-bundled-plugins.mjs @@ -24,8 +24,20 @@ import { import { tmpdir } from "node:os"; import { basename, dirname, isAbsolute, join, posix, relative } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { + createBundledRuntimeDependencyInstallArgs, + createBundledRuntimeDependencyInstallEnv, + createNestedNpmInstallEnv, + runBundledRuntimeDependencyNpmInstall, +} from "./lib/bundled-runtime-deps-install.mjs"; import { resolveNpmRunner } from "./npm-runner.mjs"; +export { + createBundledRuntimeDependencyInstallArgs, + createBundledRuntimeDependencyInstallEnv, + createNestedNpmInstallEnv, +}; + export const BUNDLED_PLUGIN_INSTALL_TARGETS = []; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -582,32 +594,6 @@ export function discoverBundledPluginRuntimeDeps(params = {}) { .toSorted((a, b) => a.name.localeCompare(b.name)); } -export function createNestedNpmInstallEnv(env = process.env) { - const nextEnv = { ...env }; - delete nextEnv.npm_config_global; - delete nextEnv.npm_config_location; - delete nextEnv.npm_config_prefix; - return nextEnv; -} - -export function createBundledRuntimeDependencyInstallEnv(env = process.env) { - return { - ...createNestedNpmInstallEnv(env), - npm_config_dry_run: "false", - npm_config_fetch_retries: env.npm_config_fetch_retries ?? "5", - npm_config_fetch_retry_maxtimeout: env.npm_config_fetch_retry_maxtimeout ?? "120000", - npm_config_fetch_retry_mintimeout: env.npm_config_fetch_retry_mintimeout ?? "10000", - npm_config_fetch_timeout: env.npm_config_fetch_timeout ?? "300000", - npm_config_legacy_peer_deps: "true", - npm_config_package_lock: "false", - npm_config_save: "false", - }; -} - -export function createBundledRuntimeDependencyInstallArgs(missingSpecs) { - return ["install", "--ignore-scripts", ...missingSpecs]; -} - function shouldEagerInstallBundledPluginDeps(env = process.env) { return env?.[EAGER_BUNDLED_PLUGIN_DEPS_ENV]?.trim() === "1"; } @@ -1003,19 +989,12 @@ export function runBundledPluginPostinstall(params = {}) { comSpec: params.comSpec, npmArgs: createBundledRuntimeDependencyInstallArgs(missingSpecs), }); - const result = spawn(npmRunner.command, npmRunner.args, { + runBundledRuntimeDependencyNpmInstall({ cwd: packageRoot, - encoding: "utf8", + npmRunner, env: npmRunner.env ?? installEnv, - stdio: "pipe", - windowsHide: true, - shell: npmRunner.shell, - windowsVerbatimArguments: npmRunner.windowsVerbatimArguments, + spawnSyncImpl: spawn, }); - if (result.status !== 0) { - const output = [result.stderr, result.stdout].filter(Boolean).join("\n").trim(); - throw new Error(output || "npm install failed"); - } log.log(`[postinstall] installed bundled plugin deps: ${missingSpecs.join(", ")}`); } catch (e) { // Non-fatal: gateway will surface the missing dep via doctor. diff --git a/scripts/stage-bundled-plugin-runtime-deps.mjs b/scripts/stage-bundled-plugin-runtime-deps.mjs index eb929f8e0ec..02a37e0a289 100644 --- a/scripts/stage-bundled-plugin-runtime-deps.mjs +++ b/scripts/stage-bundled-plugin-runtime-deps.mjs @@ -1,10 +1,14 @@ -import { spawnSync } from "node:child_process"; import { createHash } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { performance } from "node:perf_hooks"; import { pathToFileURL } from "node:url"; import semverSatisfies from "semver/functions/satisfies.js"; +import { + createBundledRuntimeDependencyInstallArgs, + createBundledRuntimeDependencyInstallEnv, + runBundledRuntimeDependencyNpmInstall, +} from "./lib/bundled-runtime-deps-install.mjs"; import { resolveNpmRunner } from "./npm-runner.mjs"; const TRANSIENT_TEMP_REMOVE_ERROR_CODES = new Set(["EBUSY", "ENOTEMPTY", "EPERM"]); @@ -905,39 +909,17 @@ function createRuntimeInstallManifest(pluginId, pinnedGroups) { } function runNpmInstall(params) { - const npmEnv = { - ...(params.npmRunner.env ?? process.env), - CI: "1", - npm_config_audit: "false", - npm_config_dry_run: "false", - npm_config_fetch_retries: process.env.npm_config_fetch_retries ?? "5", - npm_config_fetch_retry_maxtimeout: process.env.npm_config_fetch_retry_maxtimeout ?? "120000", - npm_config_fetch_retry_mintimeout: process.env.npm_config_fetch_retry_mintimeout ?? "10000", - npm_config_fetch_timeout: process.env.npm_config_fetch_timeout ?? "300000", - npm_config_fund: "false", - npm_config_legacy_peer_deps: "true", - npm_config_loglevel: "error", - npm_config_package_lock: "false", - npm_config_progress: "false", - npm_config_save: "false", - npm_config_yes: "true", - }; - const runSpawnSync = params.spawnSyncImpl ?? spawnSync; - const result = runSpawnSync(params.npmRunner.command, params.npmRunner.args, { + return runBundledRuntimeDependencyNpmInstall({ cwd: params.cwd, - encoding: "utf8", - env: npmEnv, - shell: params.npmRunner.shell, + npmRunner: params.npmRunner, + env: createBundledRuntimeDependencyInstallEnv(params.npmRunner.env ?? process.env, { + ci: true, + quiet: true, + }), + spawnSyncImpl: params.spawnSyncImpl, stdio: ["ignore", "pipe", "pipe"], timeout: params.timeoutMs ?? 5 * 60 * 1000, - windowsHide: true, - windowsVerbatimArguments: params.npmRunner.windowsVerbatimArguments, }); - if (result.status === 0) { - return; - } - const output = [result.stderr, result.stdout].filter(Boolean).join("\n").trim(); - throw new Error(output || "npm install failed"); } function resolveLegacyRuntimeDepsStampPath(pluginDir) { @@ -1203,7 +1185,11 @@ function installPluginRuntimeDeps(params) { runNpmInstall({ cwd: tempInstallDir, npmRunner: resolveNpmRunner({ - npmArgs: ["install", "--no-audit", "--no-fund", "--ignore-scripts", "--silent"], + npmArgs: createBundledRuntimeDependencyInstallArgs([], { + noAudit: true, + noFund: true, + silent: true, + }), }), }); }