feat: new 'lockfile-update' rangeStrategy (#3108)

Adds a new `rangeStrategy` value `lockfile-update'. Enabling this means you will get PRs that contain updates for the versions in your lock file (currently npm and yarn only) even if the range in the `package.json` file hasn't changed because the update is "in range".

Closes #1382
This commit is contained in:
Rhys Arkins 2019-01-24 06:23:08 +01:00 committed by GitHub
parent baf5d264c9
commit 62d92660b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 200 additions and 29 deletions

View file

@ -593,7 +593,14 @@ const options = [
description: 'Policy for how to modify/update existing ranges.', description: 'Policy for how to modify/update existing ranges.',
type: 'string', type: 'string',
default: 'replace', default: 'replace',
allowedValues: ['auto', 'pin', 'bump', 'replace', 'widen'], allowedValues: [
'auto',
'pin',
'bump',
'replace',
'widen',
'update-lockfile',
],
cli: false, cli: false,
env: false, env: false,
}, },
@ -825,7 +832,7 @@ const options = [
description: 'Branch topic', description: 'Branch topic',
type: 'string', type: 'string',
default: default:
'{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x', '{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}',
cli: false, cli: false,
}, },
{ {

View file

@ -39,11 +39,16 @@ function determineLockFileDirs(config, packageFiles) {
} }
continue; // eslint-disable-line no-continue continue; // eslint-disable-line no-continue
} }
if (upgrade.isLockfileUpdate) {
yarnLockDirs.push(upgrade.yarnLock);
npmLockDirs.push(upgrade.npmLock);
}
} }
if ( if (
config.upgrades.every( config.upgrades.every(
upgrade => upgrade.updateType === 'lockFileMaintenance' upgrade =>
upgrade.updateType === 'lockFileMaintenance' || upgrade.isLockfileUpdate
) )
) { ) {
return { return {
@ -366,12 +371,16 @@ async function getAdditionalFiles(config, packageFiles) {
const lockFileDir = path.dirname(lockFile); const lockFileDir = path.dirname(lockFile);
const fileName = path.basename(lockFile); const fileName = path.basename(lockFile);
logger.debug(`Generating ${fileName} for ${lockFileDir}`); logger.debug(`Generating ${fileName} for ${lockFileDir}`);
const upgrades = config.upgrades.filter(
upgrade => upgrade.npmLock === lockFile
);
const res = await npm.generateLockFile( const res = await npm.generateLockFile(
upath.join(config.localDir, lockFileDir), upath.join(config.localDir, lockFileDir),
env, env,
fileName, fileName,
config.skipInstalls, config.skipInstalls,
config.binarySource config.binarySource,
upgrades
); );
if (res.error) { if (res.error) {
// istanbul ignore if // istanbul ignore if
@ -415,10 +424,14 @@ async function getAdditionalFiles(config, packageFiles) {
const lockFileDir = path.dirname(lockFile); const lockFileDir = path.dirname(lockFile);
logger.debug(`Generating yarn.lock for ${lockFileDir}`); logger.debug(`Generating yarn.lock for ${lockFileDir}`);
const lockFileName = upath.join(lockFileDir, 'yarn.lock'); const lockFileName = upath.join(lockFileDir, 'yarn.lock');
const upgrades = config.upgrades.filter(
upgrade => upgrade.yarnLock === lockFile
);
const res = await yarn.generateLockFile( const res = await yarn.generateLockFile(
upath.join(config.localDir, lockFileDir), upath.join(config.localDir, lockFileDir),
env, env,
config config,
upgrades
); );
if (res.error) { if (res.error) {
// istanbul ignore if // istanbul ignore if

View file

@ -12,7 +12,8 @@ async function generateLockFile(
env, env,
filename, filename,
skipInstalls, skipInstalls,
binarySource binarySource,
upgrades = []
) { ) {
logger.debug(`Spawning npm install to create ${cwd}/${filename}`); logger.debug(`Spawning npm install to create ${cwd}/${filename}`);
let lockFile = null; let lockFile = null;
@ -76,6 +77,19 @@ async function generateLockFile(
})); }));
logger.debug(`npm stdout:\n${stdout}`); logger.debug(`npm stdout:\n${stdout}`);
logger.debug(`npm stderr:\n${stderr}`); logger.debug(`npm stderr:\n${stderr}`);
const lockUpdates = upgrades.filter(upgrade => upgrade.isLockfileUpdate);
if (lockUpdates.length) {
const updateCmd =
cmd +
lockUpdates
.map(update => ` ${update.depName}@${update.toVersion}`)
.join('');
({ stdout, stderr } = await exec(updateCmd, {
cwd,
shell: true,
env,
}));
}
const duration = process.hrtime(startTime); const duration = process.hrtime(startTime);
const seconds = Math.round(duration[0] + duration[1] / 1e9); const seconds = Math.round(duration[0] + duration[1] / 1e9);
lockFile = await fs.readFile(upath.join(cwd, filename), 'utf8'); lockFile = await fs.readFile(upath.join(cwd, filename), 'utf8');

View file

@ -7,7 +7,7 @@ module.exports = {
generateLockFile, generateLockFile,
}; };
async function generateLockFile(cwd, env, config = {}) { async function generateLockFile(cwd, env, config = {}, upgrades = []) {
const { binarySource, yarnIntegrity } = config; const { binarySource, yarnIntegrity } = config;
logger.debug(`Spawning yarn install to create ${cwd}/yarn.lock`); logger.debug(`Spawning yarn install to create ${cwd}/yarn.lock`);
let lockFile = null; let lockFile = null;
@ -73,22 +73,37 @@ async function generateLockFile(cwd, env, config = {}) {
cmd = 'yarn'; cmd = 'yarn';
} }
logger.debug(`Using yarn: ${cmd}`); logger.debug(`Using yarn: ${cmd}`);
cmd += ' install'; let cmdExtras = '';
cmd += ' --ignore-scripts'; cmdExtras += ' --ignore-scripts';
cmd += ' --ignore-engines'; cmdExtras += ' --ignore-engines';
cmd += ' --ignore-platform'; cmdExtras += ' --ignore-platform';
cmd += process.env.YARN_MUTEX_FILE cmdExtras += process.env.YARN_MUTEX_FILE
? ` --mutex file:${process.env.YARN_MUTEX_FILE}` ? ` --mutex file:${process.env.YARN_MUTEX_FILE}`
: ' --mutex network:31879'; : ' --mutex network:31879';
const installCmd = cmd + ' install' + cmdExtras;
// TODO: Switch to native util.promisify once using only node 8 // TODO: Switch to native util.promisify once using only node 8
({ stdout, stderr } = await exec(cmd, { ({ stdout, stderr } = await exec(installCmd, {
cwd, cwd,
shell: true, shell: true,
env, env,
})); }));
logger.debug(`yarn stdout:\n${stdout}`); logger.debug(`yarn stdout:\n${stdout}`);
logger.debug(`yarn stderr:\n${stderr}`); logger.debug(`yarn stderr:\n${stderr}`);
const lockUpdates = upgrades
.filter(upgrade => upgrade.isLockfileUpdate)
.map(upgrade => upgrade.depName);
if (lockUpdates.length) {
const updateCmd =
cmd +
' upgrade' +
lockUpdates.map(depName => ` ${depName}`).join('') +
cmdExtras;
({ stdout, stderr } = await exec(updateCmd, {
cwd,
shell: true,
env,
}));
}
const duration = process.hrtime(startTime); const duration = process.hrtime(startTime);
const seconds = Math.round(duration[0] + duration[1] / 1e9); const seconds = Math.round(duration[0] + duration[1] / 1e9);
lockFile = await fs.readFile(upath.join(cwd, 'yarn.lock'), 'utf8'); lockFile = await fs.readFile(upath.join(cwd, 'yarn.lock'), 'utf8');

View file

@ -4,6 +4,7 @@ const {
minor, minor,
patch, patch,
prerelease, prerelease,
satisfies,
valid: isVersion, valid: isVersion,
} = require('semver'); } = require('semver');
const { parseRange } = require('semver-utils'); const { parseRange } = require('semver-utils');
@ -16,6 +17,12 @@ function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) {
if (rangeStrategy === 'pin' || isVersion(currentValue)) { if (rangeStrategy === 'pin' || isVersion(currentValue)) {
return toVersion; return toVersion;
} }
if (rangeStrategy === 'lockfile-update') {
if (satisfies(toVersion, currentValue)) {
return currentValue;
}
return getNewValue(currentValue, 'replace', fromVersion, toVersion);
}
const parsedRange = parseRange(currentValue); const parsedRange = parseRange(currentValue);
const element = parsedRange[parsedRange.length - 1]; const element = parsedRange[parsedRange.length - 1];
if (rangeStrategy === 'widen') { if (rangeStrategy === 'widen') {

View file

@ -41,7 +41,9 @@ function getPrList(config, branches) {
} else { } else {
text += upgrade.depName.replace(prTitleRe, '@​$1'); text += upgrade.depName.replace(prTitleRe, '@​$1');
} }
text += ` to \`${upgrade.newDigest || upgrade.newValue}\``; text += upgrade.isLockfileUpdate
? ` to \`${upgrade.toVersion}\``
: ` to \`${upgrade.newDigest || upgrade.newValue}\``;
text += '\n'; text += '\n';
} }
if (!seen.includes(text)) { if (!seen.includes(text)) {

View file

@ -13,7 +13,7 @@ module.exports = {
}; };
async function lookupUpdates(config) { async function lookupUpdates(config) {
const { depName, currentValue } = config; const { depName, currentValue, lockedVersion } = config;
logger.trace({ dependency: depName, currentValue }, 'lookupUpdates'); logger.trace({ dependency: depName, currentValue }, 'lookupUpdates');
const { const {
equals, equals,
@ -126,10 +126,15 @@ async function lookupUpdates(config) {
newMajor: getMajor(fromVersion), newMajor: getMajor(fromVersion),
}); });
} }
let filterStart = fromVersion;
if (lockedVersion && rangeStrategy === 'lockfile-update') {
// Look for versions greater than the current locked version that still satisfy the package.json range
filterStart = lockedVersion;
}
// Filter latest, unstable, etc // Filter latest, unstable, etc
const filteredVersions = filterVersions( const filteredVersions = filterVersions(
config, config,
fromVersion, filterStart,
dependency.latestVersion, dependency.latestVersion,
allVersions, allVersions,
releases releases
@ -147,12 +152,19 @@ async function lookupUpdates(config) {
toVersion toVersion
); );
if (!update.newValue || update.newValue === currentValue) { if (!update.newValue || update.newValue === currentValue) {
continue; // eslint-disable-line no-continue if (!config.lockedVersion) {
continue; // eslint-disable-line no-continue
}
update.updateType = 'lockfileUpdate';
update.fromVersion = lockedVersion;
update.isSingleVersion = true;
} }
update.newMajor = getMajor(toVersion); update.newMajor = getMajor(toVersion);
update.newMinor = getMinor(toVersion); update.newMinor = getMinor(toVersion);
update.updateType = getType(config, fromVersion, toVersion); update.updateType =
update.isSingleVersion = !!isSingleVersion(update.newValue); update.updateType || getType(config, update.fromVersion, toVersion);
update.isSingleVersion =
update.isSingleVersion || !!isSingleVersion(update.newValue);
if (!isVersion(update.newValue)) { if (!isVersion(update.newValue)) {
update.isRange = true; update.isRange = true;
} }
@ -186,7 +198,7 @@ async function lookupUpdates(config) {
release => release =>
filteredVersions.length && filteredVersions.length &&
(filteredVersions.includes(release.version) || (filteredVersions.includes(release.version) ||
release.version === fromVersion) release.version === filterStart)
); );
} else if (!currentValue) { } else if (!currentValue) {
res.skipReason = 'unsupported-value'; res.skipReason = 'unsupported-value';
@ -245,8 +257,8 @@ async function lookupUpdates(config) {
} }
for (const update of res.updates) { for (const update of res.updates) {
const { updateType, fromVersion, toVersion } = update; const { updateType, fromVersion, toVersion } = update;
if (updateType === 'bump') { if (['bump', 'lockfileUpdate'].includes(updateType)) {
update.isBump = true; update[updateType === 'bump' ? 'isBump' : 'isLockfileUpdate'] = true;
if (getMajor(toVersion) > getMajor(fromVersion)) { if (getMajor(toVersion) > getMajor(fromVersion)) {
update.updateType = 'major'; update.updateType = 'major';
} else if ( } else if (
@ -265,6 +277,7 @@ async function lookupUpdates(config) {
.filter( .filter(
update => update =>
update.newValue !== config.currentValue || update.newValue !== config.currentValue ||
update.isLockfileUpdate ||
(update.newDigest && !update.newDigest.startsWith(config.currentDigest)) (update.newDigest && !update.newDigest.startsWith(config.currentDigest))
); );
if (res.updates.some(update => update.updateType === 'pin')) { if (res.updates.some(update => update.updateType === 'pin')) {
@ -301,6 +314,9 @@ function getType(config, fromVersion, toVersion) {
function getBucket(config, update) { function getBucket(config, update) {
const { separateMajorMinor, separateMultipleMajor } = config; const { separateMajorMinor, separateMultipleMajor } = config;
const { updateType, newMajor } = update; const { updateType, newMajor } = update;
if (updateType === 'lockfileUpdate') {
return updateType;
}
if ( if (
!separateMajorMinor || !separateMajorMinor ||
config.major.automerge === true || config.major.automerge === true ||

View file

@ -29,6 +29,33 @@ describe('generateLockFile', () => {
expect(res.error).not.toBeDefined(); expect(res.error).not.toBeDefined();
expect(res.lockFile).toEqual('package-lock-contents'); expect(res.lockFile).toEqual('package-lock-contents');
}); });
it('performs lock file updates', async () => {
getInstalledPath.mockReturnValueOnce('node_modules/npm');
exec.mockReturnValueOnce({
stdout: '',
stderror: '',
});
exec.mockReturnValueOnce({
stdout: '',
stderror: '',
});
fs.readFile = jest.fn(() => 'package-lock-contents');
const skipInstalls = true;
const updates = [
{ depName: 'some-dep', toVersion: '1.0.1', isLockfileUpdate: true },
];
const res = await npmHelper.generateLockFile(
'some-dir',
{},
'package-lock.json',
skipInstalls,
null,
updates
);
expect(fs.readFile.mock.calls.length).toEqual(1);
expect(res.error).not.toBeDefined();
expect(res.lockFile).toEqual('package-lock-contents');
});
it('performs full install', async () => { it('performs full install', async () => {
getInstalledPath.mockReturnValueOnce('node_modules/npm'); getInstalledPath.mockReturnValueOnce('node_modules/npm');
exec.mockReturnValueOnce({ exec.mockReturnValueOnce({

View file

@ -22,6 +22,22 @@ describe('generateLockFile', () => {
expect(fs.readFile.mock.calls.length).toEqual(1); expect(fs.readFile.mock.calls.length).toEqual(1);
expect(res.lockFile).toEqual('package-lock-contents'); expect(res.lockFile).toEqual('package-lock-contents');
}); });
it('performs lock file updates', async () => {
getInstalledPath.mockReturnValueOnce('node_modules/yarn');
exec.mockReturnValueOnce({
stdout: '',
stderror: '',
});
exec.mockReturnValueOnce({
stdout: '',
stderror: '',
});
fs.readFile = jest.fn(() => 'package-lock-contents');
const res = await yarnHelper.generateLockFile('some-dir', {}, {}, [
{ depName: 'some-dep', isLockfileUpdate: true },
]);
expect(res.lockFile).toEqual('package-lock-contents');
});
it('catches errors', async () => { it('catches errors', async () => {
getInstalledPath.mockReturnValueOnce('node_modules/yarn'); getInstalledPath.mockReturnValueOnce('node_modules/yarn');
exec.mockReturnValueOnce({ exec.mockReturnValueOnce({

View file

@ -1143,6 +1143,48 @@ Array [
] ]
`; `;
exports[`workers/repository/process/lookup .lookupUpdates() supports lock file updates mixed with regular updates 1`] = `
Array [
Object {
"canBeUnpublished": false,
"fromVersion": "0.4.0",
"isLockfileUpdate": true,
"isRange": true,
"isSingleVersion": true,
"newMajor": 0,
"newMinor": 4,
"newValue": "^0.4.0",
"releaseTimestamp": "2011-06-10T17:20:04.719Z",
"toVersion": "0.4.4",
"updateType": "minor",
},
Object {
"canBeUnpublished": false,
"fromVersion": "0.4.4",
"isRange": true,
"isSingleVersion": false,
"newMajor": 0,
"newMinor": 9,
"newValue": "^0.9.0",
"releaseTimestamp": "2013-09-04T17:07:22.948Z",
"toVersion": "0.9.7",
"updateType": "minor",
},
Object {
"canBeUnpublished": false,
"fromVersion": "0.4.4",
"isRange": true,
"isSingleVersion": false,
"newMajor": 1,
"newMinor": 4,
"newValue": "^1.0.0",
"releaseTimestamp": "2015-05-17T04:25:07.299Z",
"toVersion": "1.4.1",
"updateType": "major",
},
]
`;
exports[`workers/repository/process/lookup .lookupUpdates() supports majorgte updates 1`] = ` exports[`workers/repository/process/lookup .lookupUpdates() supports majorgte updates 1`] = `
Array [ Array [
Object { Object {

View file

@ -54,6 +54,17 @@ describe('workers/repository/process/lookup', () => {
.reply(200, qJson); .reply(200, qJson);
expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot(); expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot();
}); });
it('supports lock file updates mixed with regular updates', async () => {
config.currentValue = '^0.4.0';
config.rangeStrategy = 'lockfile-update';
config.depName = 'q';
config.purl = 'pkg:npm/q';
config.lockedVersion = '0.4.0';
nock('https://registry.npmjs.org')
.get('/q')
.reply(200, qJson);
expect((await lookup.lookupUpdates(config)).updates).toMatchSnapshot();
});
it('returns multiple updates if grouping but separateMajorMinor=true', async () => { it('returns multiple updates if grouping but separateMajorMinor=true', async () => {
config.groupName = 'somegroup'; config.groupName = 'somegroup';
config.currentValue = '0.4.0'; config.currentValue = '0.4.0';

View file

@ -10,7 +10,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
@ -107,7 +107,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
@ -301,7 +301,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
@ -495,7 +495,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
@ -592,7 +592,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
@ -689,7 +689,7 @@ Array [
"binarySource": "bundled", "binarySource": "bundled",
"branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
"branchPrefix": "renovate/", "branchPrefix": "renovate/",
"branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x", "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
"bumpVersion": null, "bumpVersion": null,
"commitBody": null, "commitBody": null,
"commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",

View file

@ -758,6 +758,7 @@ Behaviour:
- `bump` = e.g. bump the range even if the new version satisifies the existing range, e.g. `^1.0.0` -> `^1.1.0` - `bump` = e.g. bump the range even if the new version satisifies the existing range, e.g. `^1.0.0` -> `^1.1.0`
- `replace` = Replace the range with a newer one if the new version falls outside it, e.g. `^1.0.0` -> `^2.0.0` - `replace` = Replace the range with a newer one if the new version falls outside it, e.g. `^1.0.0` -> `^2.0.0`
- `widen` = Widen the range with newer one, e.g. `^1.0.0` -> `^1.0.0 || ^2.0.0` - `widen` = Widen the range with newer one, e.g. `^1.0.0` -> `^1.0.0 || ^2.0.0`
- `update-lockfile` = Update the lock file when in-range updates are available, otherwise 'replace' for updates out of range
Renovate's "auto" strategy works like this for npm: Renovate's "auto" strategy works like this for npm: