renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
183 lines (182 loc) • 7.17 kB
JavaScript
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