UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

183 lines (182 loc) • 7.17 kB
import "../../../../constants/error-messages.js"; import { GlobalConfig } from "../../../../config/global.js"; import { uniqueStrings } from "../../../../util/string.js"; import { logger } from "../../../../logger/index.js"; import { parseSingleYaml } from "../../../../util/yaml.js"; import { deleteLocalFile, ensureCacheDir, getSiblingFileName, localPathExists, readLocalFile } from "../../../../util/fs/index.js"; import { exec, getToolSettingsOptions } from "../../../../util/exec/index.js"; import { PNPM_CACHE_DIR, PNPM_STORE_DIR } from "../constants.js"; import { getNodeToolConstraint } from "./node-version.js"; import { getNodeOptions, getPackageManagerVersion, lazyLoadPackageJson } from "./utils.js"; import { isNumber, isNumericString } from "@sindresorhus/is"; import { quote } from "shlex"; import upath from "upath"; //#region lib/modules/manager/npm/post-update/pnpm.ts function getPnpmConstraintFromUpgrades(upgrades) { for (const upgrade of upgrades) if (upgrade.depName === "pnpm" && upgrade.newVersion) return upgrade.newVersion; return null; } async function generateLockFile(lockFileDir, env, config, upgrades = []) { const lockFileName = upath.join(lockFileDir, "pnpm-lock.yaml"); logger.debug(`Spawning pnpm install to create ${lockFileName}`); let lockFile = null; const commands = []; try { const lazyPgkJson = lazyLoadPackageJson(lockFileDir); const pnpmToolConstraint = { toolName: "pnpm", constraint: getPnpmConstraintFromUpgrades(upgrades) ?? config.constraints?.pnpm ?? getPackageManagerVersion("pnpm", await lazyPgkJson.getValue()) ?? await getConstraintFromLockFile(lockFileName) }; const pnpmConfigCacheDir = await ensureCacheDir(PNPM_CACHE_DIR); const pnpmConfigStoreDir = await ensureCacheDir(PNPM_STORE_DIR); const extraEnv = { NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE, npm_config_store: env.npm_config_store, npm_config_cache_dir: pnpmConfigCacheDir, npm_config_store_dir: pnpmConfigStoreDir, pnpm_config_cache_dir: pnpmConfigCacheDir, pnpm_config_store_dir: pnpmConfigStoreDir }; const { nodeMaxMemory } = getToolSettingsOptions(config.toolSettings); if (nodeMaxMemory) extraEnv.NODE_OPTIONS = getNodeOptions(nodeMaxMemory); const execOptions = { cwdFile: lockFileName, extraEnv, docker: {}, toolConstraints: [await getNodeToolConstraint(config, upgrades, lockFileDir, lazyPgkJson), pnpmToolConstraint] }; /* v8 ignore next 4 -- needs test */ if (GlobalConfig.get("exposeAllEnv")) { extraEnv.NPM_AUTH = env.NPM_AUTH; extraEnv.NPM_EMAIL = env.NPM_EMAIL; } const pnpmWorkspaceFilePath = getSiblingFileName(lockFileName, "pnpm-workspace.yaml"); let args = "--lockfile-only"; if (await localPathExists(pnpmWorkspaceFilePath)) { const pnpmWorkspace = parseSingleYaml(await readLocalFile(pnpmWorkspaceFilePath, "utf8")); if (pnpmWorkspace?.packages?.length) { logger.debug(`Found pnpm workspace with ${pnpmWorkspace.packages.length} package definitions`); args += " --recursive"; } } if (!GlobalConfig.get("allowScripts")) { args += " --ignore-scripts"; args += " --ignore-pnpmfile"; } else if (config.ignoreScripts) args += " --ignore-scripts"; logger.trace({ args }, "pnpm command options"); const lockUpdates = upgrades.filter((upgrade) => upgrade.isLockfileUpdate); if (lockUpdates.length !== upgrades.length) commands.push(`pnpm install ${args}`); if (lockUpdates.length) { logger.debug("Performing lockfileUpdate (pnpm)"); commands.push(`pnpm update --no-save ${lockUpdates.map((update) => `${update.packageName}@${update.newVersion}`).filter(uniqueStrings).map(quote).join(" ")} ${args}`); } if (config.postUpdateOptions?.includes("pnpmDedupe")) commands.push("pnpm dedupe --ignore-scripts"); if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) { logger.debug(`Removing ${lockFileName} first due to lock file maintenance upgrade`); try { await deleteLocalFile(lockFileName); } catch (err) /* v8 ignore next -- TODO: add test #40625 */ { logger.debug({ err, lockFileName }, "Error removing `pnpm-lock.yaml` for lock file maintenance"); } } await exec(commands, execOptions); lockFile = await readLocalFile(lockFileName, "utf8"); } catch (err) { // v8 ignore if -- TODO: add test #40625 if (err.message === "temporary-error") throw err; logger.debug({ commands, err, stdout: err.stdout, stderr: err.stderr, type: "pnpm" }, "lock file error"); return { error: true, stderr: err.stderr, stdout: err.stdout }; } return { lockFile }; } async function getConstraintFromLockFile(lockFileName) { let constraint = null; try { const lockfileContent = await readLocalFile(lockFileName, "utf8"); if (!lockfileContent) { logger.trace(`Empty pnpm lock file: ${lockFileName}`); return null; } const pnpmLock = parseSingleYaml(lockfileContent); if (!isNumber(pnpmLock?.lockfileVersion) && !isNumericString(pnpmLock?.lockfileVersion)) { logger.trace(`Invalid pnpm lockfile version: ${lockFileName}`); return null; } const { lowerConstraint, upperConstraint } = lockToPnpmVersionMapping.find((m) => m.lockfileVersion === pnpmLock.lockfileVersion) ?? { lockfileVersion: 5, lowerConstraint: ">=3", upperConstraint: "<3.5.0" }; constraint = lowerConstraint; if (upperConstraint) constraint += ` ${upperConstraint}`; } catch (err) { logger.warn({ err }, "Error getting pnpm constraints from lock file"); } return constraint; } /** * pnpm lockfiles have corresponding version numbers called "lockfileVersion" * each lockfileVersion can only be generated by a certain pnpm version range * eg. lockfileVersion: 5.4 can only be generated by pnpm version >=7 && <8 * official list can be found here : https://github.com/pnpm/spec/tree/master/lockfile * we use the mapping present below to find the compatible pnpm version range for a given lockfileVersion * * the various terms used in the mapping are explained below: * lowerConstraint : lowest pnpm version that can generate the lockfileVersion * upperConstraint : highest pnpm version that can generate the lockfileVersion * lowerBound : highest pnpm version that is less than the lowerConstraint * upperBound : lowest pnpm version that is greater than the upperConstraint * * To handle future lockfileVersions, we need to: * 1. add a upperBound and upperConstraint to the current latest lockfileVersion * 2. add an object for the new lockfileVersion with lowerBound and lowerConstraint * * lockfileVersion from v6 on are strings */ const lockToPnpmVersionMapping = [ { lockfileVersion: "9.0", lowerConstraint: ">=9" }, { lockfileVersion: "6.0", lowerConstraint: ">=7.24.2", upperConstraint: "<9" }, { lockfileVersion: 5.4, lowerConstraint: ">=7", upperConstraint: "<8" }, { lockfileVersion: 5.3, lowerConstraint: ">=6", upperConstraint: "<7" }, { lockfileVersion: 5.2, lowerConstraint: ">=5.10.0", upperConstraint: "<6" }, { lockfileVersion: 5.1, lowerConstraint: ">=3.5.0", upperConstraint: "<5.9.3" } ]; //#endregion export { generateLockFile }; //# sourceMappingURL=pnpm.js.map