renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
289 lines • 13.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkYarnrc = checkYarnrc;
exports.getOptimizeCommand = getOptimizeCommand;
exports.isYarnUpdate = isYarnUpdate;
exports.generateLockFile = generateLockFile;
exports.fuzzyMatchAdditionalYarnrcYml = fuzzyMatchAdditionalYarnrcYml;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const semver_1 = tslib_1.__importDefault(require("semver"));
const shlex_1 = require("shlex");
const upath_1 = tslib_1.__importDefault(require("upath"));
const global_1 = require("../../../../config/global");
const error_messages_1 = require("../../../../constants/error-messages");
const logger_1 = require("../../../../logger");
const external_host_error_1 = require("../../../../types/errors/external-host-error");
const env_1 = require("../../../../util/env");
const exec_1 = require("../../../../util/exec");
const fs_1 = require("../../../../util/fs");
const regex_1 = require("../../../../util/regex");
const string_1 = require("../../../../util/string");
const npm_1 = require("../../../datasource/npm");
const yarn_1 = require("../extract/yarn");
const node_version_1 = require("./node-version");
const utils_1 = require("./utils");
async function checkYarnrc(lockFileDir) {
let offlineMirror = false;
let yarnPath = null;
try {
const yarnrc = await (0, fs_1.readLocalFile)(upath_1.default.join(lockFileDir, '.yarnrc'), 'utf8');
if (is_1.default.string(yarnrc)) {
const mirrorLine = yarnrc
.split(regex_1.newlineRegex)
.find((line) => line.startsWith('yarn-offline-mirror '));
offlineMirror = !!mirrorLine;
const pathLine = yarnrc
.split(regex_1.newlineRegex)
.find((line) => line.startsWith('yarn-path '));
if (pathLine) {
yarnPath = pathLine.replace((0, regex_1.regEx)(/^yarn-path\s+"?(.+?)"?$/), '$1');
}
if (yarnPath) {
// resolve binary relative to `yarnrc`
yarnPath = upath_1.default.join(lockFileDir, yarnPath);
}
const yarnBinaryExists = yarnPath
? await (0, fs_1.localPathIsFile)(yarnPath)
: false;
let scrubbedYarnrc = yarnrc
.replace('--install.pure-lockfile true', '')
.replace('--install.frozen-lockfile true', '');
if (!yarnBinaryExists) {
scrubbedYarnrc = scrubbedYarnrc.replace((0, regex_1.regEx)(/^yarn-path\s+"?.+?"?$/gm), '');
yarnPath = null;
}
if (yarnrc !== scrubbedYarnrc) {
logger_1.logger.debug(`Writing scrubbed .yarnrc to ${lockFileDir}`);
await (0, fs_1.writeLocalFile)(upath_1.default.join(lockFileDir, '.yarnrc'), scrubbedYarnrc);
}
}
}
catch /* istanbul ignore next */ {
// not found
}
return { offlineMirror, yarnPath };
}
function getOptimizeCommand(fileName) {
return `sed -i 's/ steps,/ steps.slice(0,1),/' ${(0, shlex_1.quote)(fileName)}`;
}
function isYarnUpdate(upgrade) {
return upgrade.depType === 'packageManager' && upgrade.depName === 'yarn';
}
async function generateLockFile(lockFileDir, env, config = {}, upgrades = []) {
const lockFileName = upath_1.default.join(lockFileDir, 'yarn.lock');
logger_1.logger.debug(`Spawning yarn install to create ${lockFileName}`);
let lockFile = null;
try {
const lazyPgkJson = (0, utils_1.lazyLoadPackageJson)(lockFileDir);
const toolConstraints = [
await (0, node_version_1.getNodeToolConstraint)(config, upgrades, lockFileDir, lazyPgkJson),
];
const yarnUpdate = upgrades.find(isYarnUpdate);
const yarnCompatibility = (yarnUpdate ? yarnUpdate.newValue : config.constraints?.yarn) ??
(0, utils_1.getPackageManagerVersion)('yarn', await lazyPgkJson.getValue()) ??
(0, yarn_1.getYarnVersionFromLock)(await (0, yarn_1.getYarnLock)(lockFileName));
const minYarnVersion = semver_1.default.validRange(yarnCompatibility) &&
semver_1.default.minVersion(yarnCompatibility);
const isYarn1 = !minYarnVersion || minYarnVersion.major === 1;
const isYarnDedupeAvailable = minYarnVersion && semver_1.default.gte(minYarnVersion, '2.2.0');
const isYarnModeAvailable = minYarnVersion && semver_1.default.gte(minYarnVersion, '3.0.0');
const yarnTool = {
toolName: 'yarn',
constraint: '^1.22.18', // needs to be a v1 yarn, otherwise v2 will be installed
};
// check first upgrade, see #17786
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 = ''; // should have a leading space
if (config.skipInstalls !== false) {
if (isYarn1) {
const { offlineMirror, yarnPath } = await checkYarnrc(lockFileDir);
if (!offlineMirror) {
logger_1.logger.debug('Updating yarn.lock only - skipping node_modules');
// The following change causes Yarn 1.x to exit gracefully after updating the lock file but without installing node_modules
yarnTool.toolName = 'yarn-slim';
if (yarnPath) {
commands.push(getOptimizeCommand(yarnPath) + ' || true');
}
}
}
else if (isYarnModeAvailable) {
// Don't run the link step and only fetch what's necessary to compute an updated lockfile
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_1.logger.debug('Enabling global cache as zero-install is not detected');
extraEnv.YARN_ENABLE_GLOBAL_CACHE = '1';
}
}
if (!global_1.GlobalConfig.get('allowScripts') || config.ignoreScripts) {
if (isYarn1) {
cmdOptions += ' --ignore-scripts';
}
else if (isYarnModeAvailable) {
if (config.skipInstalls === false) {
// Don't run the build scripts
cmdOptions += ' --mode=skip-build';
}
}
else {
extraEnv.YARN_ENABLE_SCRIPTS = '0';
}
}
const execOptions = {
cwdFile: lockFileName,
extraEnv,
docker: {},
toolConstraints,
};
// istanbul ignore if
if (global_1.GlobalConfig.get('exposeAllEnv')) {
extraEnv.NPM_AUTH = env.NPM_AUTH;
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
}
if (yarnUpdate && !isYarn1) {
logger_1.logger.debug('Updating Yarn binary');
// TODO: types (#22198)
commands.push(`yarn set version ${(0, shlex_1.quote)(yarnUpdate.newValue)}`);
}
const allEnv = (0, env_1.getEnv)();
if (allEnv.RENOVATE_X_YARN_PROXY) {
if (allEnv.HTTP_PROXY && !isYarn1) {
commands.push('yarn config unset --home httpProxy');
commands.push(`yarn config set --home httpProxy ${(0, shlex_1.quote)(allEnv.HTTP_PROXY)}`);
}
if (allEnv.HTTPS_PROXY && !isYarn1) {
commands.push('yarn config unset --home httpsProxy');
commands.push(`yarn config set --home httpsProxy ${(0, shlex_1.quote)(allEnv.HTTPS_PROXY)}`);
}
}
// This command updates the lock file based on package.json
commands.push(`yarn install${cmdOptions}`);
// rangeStrategy = update-lockfile
const lockUpdates = upgrades.filter((upgrade) => upgrade.isLockfileUpdate);
if (lockUpdates.length) {
logger_1.logger.debug('Performing lockfileUpdate (yarn)');
if (isYarn1) {
// `yarn upgrade` updates based on the version range specified in the package file
// note - this can hit a yarn bug, see https://github.com/yarnpkg/yarn/issues/8236
commands.push(`yarn upgrade ${lockUpdates
.map((update) => update.depName)
.filter(is_1.default.string)
.filter(string_1.uniqueStrings)
.map(shlex_1.quote)
.join(' ')}${cmdOptions}`);
}
else {
// `yarn up -R` updates to the latest release in each range
commands.push(`yarn up -R ${lockUpdates
// TODO: types (#22198)
.map((update) => `${update.depName}`)
.filter(string_1.uniqueStrings)
.map(shlex_1.quote)
.join(' ')}${cmdOptions}`);
}
}
// postUpdateOptions
['fewer', 'highest'].forEach((s) => {
if (config.postUpdateOptions?.includes(`yarnDedupe${s.charAt(0).toUpperCase()}${s.slice(1)}`)) {
logger_1.logger.debug(`Performing yarn dedupe ${s}`);
if (isYarn1) {
commands.push(`npx yarn-deduplicate --strategy ${s}`);
// Run yarn again in case any changes are necessary
commands.push(`yarn install${cmdOptions}`);
}
else if (isYarnDedupeAvailable && s === 'highest') {
commands.push(`yarn dedupe --strategy ${s}${cmdOptions}`);
}
else {
logger_1.logger.debug(`yarn dedupe ${s} not available`);
}
}
});
if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) {
logger_1.logger.debug(`Removing ${lockFileName} first due to lock file maintenance upgrade`);
// Note: Instead of just deleting the `yarn.lock` file, we just wipe it
// and keep an empty lock file. Deleting the lock file could result in different
// Yarn semantics. e.g. Yarn 2+ will error when `yarn install` is executed in
// a subdirectory which is not part of a Yarn workspace. Yarn suggests to create
// an empty lock file if a subdirectory should be treated as its own workspace.
// https://github.com/yarnpkg/berry/blob/20612e82d26ead5928cc27bf482bb8d62dde87d3/packages/yarnpkg-core/sources/Project.ts#L284.
try {
await (0, fs_1.writeLocalFile)(lockFileName, '');
}
catch (err) /* istanbul ignore next */ {
logger_1.logger.debug({ err, lockFileName }, 'Error clearing `yarn.lock` for lock file maintenance');
}
}
// Run the commands
await (0, exec_1.exec)(commands, execOptions);
// Read the result
lockFile = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
}
catch (err) /* istanbul ignore next */ {
if (err.message === error_messages_1.TEMPORARY_ERROR) {
throw err;
}
logger_1.logger.debug({
err,
type: 'yarn',
}, 'lock file error');
const stdouterr = String(err.stdout) + String(err.stderr);
if (stdouterr.includes('ENOSPC: no space left on device') ||
stdouterr.includes('Out of diskspace')) {
throw new Error(error_messages_1.SYSTEM_INSUFFICIENT_DISK_SPACE);
}
if (stdouterr.includes('The registry may be down.') ||
stdouterr.includes('getaddrinfo ENOTFOUND registry.yarnpkg.com') ||
stdouterr.includes('getaddrinfo ENOTFOUND registry.npmjs.org')) {
throw new external_host_error_1.ExternalHostError(err, npm_1.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]) => {
const key = keys.get(k.replace(/\/$/, '')) ?? k;
return { [key]: v };
})
.reduce((acc, cur) => ({ ...acc, ...cur }), {}),
};
}
//# sourceMappingURL=yarn.js.map