renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
216 lines (215 loc) • 9.38 kB
JavaScript
import { SYSTEM_INSUFFICIENT_DISK_SPACE } from "../../../../constants/error-messages.js";
import { getEnv } from "../../../../util/env.js";
import { newlineRegex, regEx } from "../../../../util/regex.js";
import { GlobalConfig } from "../../../../config/global.js";
import { uniqueStrings } from "../../../../util/string.js";
import { logger } from "../../../../logger/index.js";
import { ExternalHostError } from "../../../../types/errors/external-host-error.js";
import { localPathIsFile, readLocalFile, writeLocalFile } from "../../../../util/fs/index.js";
import { exec, getToolSettingsOptions } from "../../../../util/exec/index.js";
import { NpmDatasource } from "../../../datasource/npm/index.js";
import { getNodeToolConstraint } from "./node-version.js";
import { getNodeOptions, getPackageManagerVersion, lazyLoadPackageJson } from "./utils.js";
import { getYarnLock, getYarnVersionFromLock } from "../extract/yarn.js";
import { isString } from "@sindresorhus/is";
import { quote } from "shlex";
import upath from "upath";
import semver from "semver";
//#region lib/modules/manager/npm/post-update/yarn.ts
async function checkYarnrc(lockFileDir) {
let offlineMirror = false;
let yarnPath = null;
try {
const yarnrc = await readLocalFile(upath.join(lockFileDir, ".yarnrc"), "utf8");
if (isString(yarnrc)) {
offlineMirror = !!yarnrc.split(newlineRegex).find((line) => line.startsWith("yarn-offline-mirror "));
const pathLine = yarnrc.split(newlineRegex).find((line) => line.startsWith("yarn-path "));
if (pathLine) yarnPath = pathLine.replace(regEx(/^yarn-path\s+"?(.+?)"?$/), "$1");
if (yarnPath) yarnPath = upath.join(lockFileDir, yarnPath);
const yarnBinaryExists = yarnPath ? await localPathIsFile(yarnPath) : false;
let scrubbedYarnrc = yarnrc.replace("--install.pure-lockfile true", "").replace("--install.frozen-lockfile true", "");
if (!yarnBinaryExists) {
scrubbedYarnrc = scrubbedYarnrc.replace(regEx(/^yarn-path\s+"?.+?"?$/gm), "");
yarnPath = null;
}
if (yarnrc !== scrubbedYarnrc) {
logger.debug(`Writing scrubbed .yarnrc to ${lockFileDir}`);
await writeLocalFile(upath.join(lockFileDir, ".yarnrc"), scrubbedYarnrc);
}
}
} catch {}
return {
offlineMirror,
yarnPath
};
}
function getOptimizeCommand(fileName) {
return [
"sed",
"-i",
"s/ steps,/ steps.slice(0,1),/",
fileName
];
}
function isYarnUpdate(upgrade) {
return upgrade.depType === "packageManager" && upgrade.depName === "yarn";
}
async function generateLockFile(lockFileDir, env, config = {}, upgrades = []) {
const lockFileName = upath.join(lockFileDir, "yarn.lock");
logger.debug(`Spawning yarn install to create ${lockFileName}`);
let lockFile = null;
try {
const lazyPgkJson = lazyLoadPackageJson(lockFileDir);
const toolConstraints = [await getNodeToolConstraint(config, upgrades, lockFileDir, lazyPgkJson)];
const yarnUpdate = upgrades.find(isYarnUpdate);
const yarnCompatibility = (yarnUpdate ? yarnUpdate.newValue : config.constraints?.yarn) ?? getPackageManagerVersion("yarn", await lazyPgkJson.getValue()) ?? getYarnVersionFromLock(await getYarnLock(lockFileName));
const minYarnVersion = semver.validRange(yarnCompatibility) && semver.minVersion(yarnCompatibility);
const isYarn1 = !minYarnVersion || minYarnVersion.major === 1;
const isYarnDedupeAvailable = minYarnVersion && semver.gte(minYarnVersion, "2.2.0");
const isYarnModeAvailable = minYarnVersion && semver.gte(minYarnVersion, "3.0.0");
const yarnTool = {
toolName: "yarn",
constraint: "^1.22.18"
};
const hasPackageManager = !!config.managerData?.hasPackageManager || !!upgrades[0]?.managerData?.hasPackageManager;
if (!isYarn1 && hasPackageManager) toolConstraints.push({
toolName: "corepack",
constraint: config.constraints?.corepack
});
else {
toolConstraints.push(yarnTool);
if (isYarn1 && minYarnVersion) yarnTool.constraint = yarnCompatibility;
}
const extraEnv = {
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
npm_config_store: env.npm_config_store,
CI: "true"
};
const commands = [];
let cmdOptions = "";
if (config.skipInstalls !== false) {
if (isYarn1) {
const { offlineMirror, yarnPath } = await checkYarnrc(lockFileDir);
// v8 ignore else -- TODO: add test #40625
if (!offlineMirror) {
logger.debug("Updating yarn.lock only - skipping node_modules");
yarnTool.toolName = "yarn-slim";
if (yarnPath) commands.push({
command: getOptimizeCommand(yarnPath),
ignoreFailure: true
});
}
} else if (isYarnModeAvailable) cmdOptions += " --mode=update-lockfile";
}
if (isYarn1) {
cmdOptions += " --ignore-engines --ignore-platform --network-timeout 100000";
extraEnv.YARN_CACHE_FOLDER = env.YARN_CACHE_FOLDER;
} else {
extraEnv.YARN_ENABLE_IMMUTABLE_INSTALLS = "false";
extraEnv.YARN_HTTP_TIMEOUT = "100000";
extraEnv.YARN_GLOBAL_FOLDER = env.YARN_GLOBAL_FOLDER;
if (!config.managerData?.yarnZeroInstall) {
logger.debug("Enabling global cache as zero-install is not detected");
extraEnv.YARN_ENABLE_GLOBAL_CACHE = "1";
}
}
if (!GlobalConfig.get("allowScripts") || config.ignoreScripts) if (isYarn1) cmdOptions += " --ignore-scripts";
else if (isYarnModeAvailable) {
if (config.skipInstalls === false) cmdOptions += " --mode=skip-build";
} else extraEnv.YARN_ENABLE_SCRIPTS = "0";
const { nodeMaxMemory } = getToolSettingsOptions(config.toolSettings);
if (nodeMaxMemory) extraEnv.NODE_OPTIONS = getNodeOptions(nodeMaxMemory);
const execOptions = {
cwdFile: lockFileName,
extraEnv,
docker: {},
toolConstraints
};
/* v8 ignore next 4 -- needs test */
if (GlobalConfig.get("exposeAllEnv")) {
extraEnv.NPM_AUTH = env.NPM_AUTH;
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
}
if (yarnUpdate && !isYarn1) {
logger.debug("Updating Yarn binary");
commands.push(`yarn set version ${quote(yarnUpdate.newValue)}`);
}
const allEnv = getEnv();
if (allEnv.RENOVATE_X_YARN_PROXY) {
// v8 ignore else -- TODO: add test #40625
if (allEnv.HTTP_PROXY && !isYarn1) {
commands.push("yarn config unset --home httpProxy");
commands.push(`yarn config set --home httpProxy ${quote(allEnv.HTTP_PROXY)}`);
}
// v8 ignore else -- TODO: add test #40625
if (allEnv.HTTPS_PROXY && !isYarn1) {
commands.push("yarn config unset --home httpsProxy");
commands.push(`yarn config set --home httpsProxy ${quote(allEnv.HTTPS_PROXY)}`);
}
}
commands.push(`yarn install${cmdOptions}`);
const lockUpdates = upgrades.filter((upgrade) => upgrade.isLockfileUpdate);
if (lockUpdates.length) {
logger.debug("Performing lockfileUpdate (yarn)");
if (isYarn1) commands.push(`yarn upgrade ${lockUpdates.map((update) => update.depName).filter(isString).filter(uniqueStrings).map(quote).join(" ")}${cmdOptions}`);
else commands.push(`yarn up -R ${lockUpdates.map((update) => `${update.depName}`).filter(uniqueStrings).map(quote).join(" ")}${cmdOptions}`);
}
["fewer", "highest"].forEach((s) => {
if (config.postUpdateOptions?.includes(`yarnDedupe${s.charAt(0).toUpperCase()}${s.slice(1)}`)) {
logger.debug(`Performing yarn dedupe ${s}`);
if (isYarn1) {
commands.push(`npx yarn-deduplicate --strategy ${s}`);
commands.push(`yarn install${cmdOptions}`);
} else if (isYarnDedupeAvailable && s === "highest") commands.push(`yarn dedupe --strategy ${s}${cmdOptions}`);
else logger.debug(`yarn dedupe ${s} not available`);
}
});
if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) {
logger.debug(`Removing ${lockFileName} first due to lock file maintenance upgrade`);
try {
await writeLocalFile(lockFileName, "");
} catch (err) {
// v8 ignore next -- TODO: add test #40625
logger.debug({
err,
lockFileName
}, "Error clearing `yarn.lock` 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({
err,
type: "yarn"
}, "lock file error");
const stdouterr = String(err.stdout) + String(err.stderr);
// v8 ignore if -- TODO: add test #40625
if (stdouterr.includes("ENOSPC: no space left on device") || stdouterr.includes("Out of diskspace")) throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE);
// v8 ignore if -- TODO: add test #40625
if (stdouterr.includes("The registry may be down.") || stdouterr.includes("getaddrinfo ENOTFOUND registry.yarnpkg.com") || stdouterr.includes("getaddrinfo ENOTFOUND registry.npmjs.org")) throw new ExternalHostError(err, NpmDatasource.id);
return {
error: true,
stderr: err.stderr,
stdout: err.stdout
};
}
return { lockFile };
}
function fuzzyMatchAdditionalYarnrcYml(additionalYarnRcYml, existingYarnrRcYml) {
const keys = new Map(Object.keys(existingYarnrRcYml.npmRegistries ?? {}).map((x) => [x.replace(/\/$/, "").replace(/^https?:/, ""), x]));
return {
...additionalYarnRcYml,
npmRegistries: Object.entries(additionalYarnRcYml.npmRegistries ?? {}).map(([k, v]) => {
return { [keys.get(k.replace(/\/$/, "")) ?? k]: v };
}).reduce((acc, cur) => ({
...acc,
...cur
}), {})
};
}
//#endregion
export { fuzzyMatchAdditionalYarnrcYml, generateLockFile, isYarnUpdate };
//# sourceMappingURL=yarn.js.map