Skip to content

Commit

Permalink
feat: support new rangeStrategy=in-range-only (#13257)
Browse files Browse the repository at this point in the history
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
3 people committed Jan 30, 2022
1 parent a02355d commit 368903a
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/usage/configuration-options.md
Expand Up @@ -2055,6 +2055,7 @@ Behavior:
- `replace` = Replace the range with a newer one if the new version falls outside it, and update nothing otherwise
- `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. Works for `bundler`, `composer`, `npm`, `yarn`, `terraform` and `poetry` so far
- `in-range-only` = Update the lock file when in-range updates are available, ignore package file updates

Renovate's `"auto"` strategy works like this for npm:

Expand All @@ -2072,6 +2073,11 @@ If instead you'd prefer to be updated to `^1.2.0` in cases like this, then confi

This feature supports simple caret (`^`) and tilde (`~`) ranges only, like `^1.0.0` and `~1.0.0`.

The `in-range-only` strategy may be useful if you want to leave the package file unchanged and only do `update-lockfile` within the existing range.
The `in-range-only` strategy behaves like `update-lockfile`, but discards any updates where the new version of the dependency is not equal to the current version.
We recommend you avoid using the `in-range-only` strategy unless you strictly need it.
Using the `in-range-only` strategy may result in you being multiple releases behind without knowing it.

## rebaseLabel

On supported platforms it is possible to add a label to a PR to manually request Renovate to recreate/rebase it.
Expand Down
1 change: 1 addition & 0 deletions lib/config/options/index.ts
Expand Up @@ -1169,6 +1169,7 @@ const options: RenovateOptions[] = [
'replace',
'widen',
'update-lockfile',
'in-range-only',
],
cli: false,
env: false,
Expand Down
28 changes: 28 additions & 0 deletions lib/manager/index.spec.ts
Expand Up @@ -152,6 +152,34 @@ describe('manager/index', () => {
manager.getRangeStrategy({ manager: 'dummy', rangeStrategy: 'bump' })
).not.toBeNull();
});

it('returns update-lockfile for in-range-only', () => {
manager.getManagers().set('dummy', {
defaultConfig: {},
supportedDatasources: [],
});
expect(
manager.getRangeStrategy({
manager: 'dummy',
rangeStrategy: 'in-range-only',
})
).toBe('update-lockfile');
});

it('returns update-lockfile for in-range-only if it is proposed my manager', () => {
manager.getManagers().set('dummy', {
defaultConfig: {},
supportedDatasources: [],
getRangeStrategy: () => 'in-range-only',
});
expect(
manager.getRangeStrategy({
manager: 'dummy',
rangeStrategy: 'in-range-only',
})
).toBe('update-lockfile');
});

afterEach(() => {
manager.getManagers().delete('dummy');
});
Expand Down
10 changes: 9 additions & 1 deletion lib/manager/index.ts
Expand Up @@ -79,11 +79,19 @@ export function getRangeStrategy(config: RangeConfig): RangeStrategy {
const m = managers.get(manager);
if (m.getRangeStrategy) {
// Use manager's own function if it exists
return m.getRangeStrategy(config);
const managerRangeStrategy = m.getRangeStrategy(config);
if (managerRangeStrategy === 'in-range-only') {
return 'update-lockfile';
}
return managerRangeStrategy;
}
if (rangeStrategy === 'auto') {
// default to 'replace' for auto
return 'replace';
}
if (rangeStrategy === 'in-range-only') {
return 'update-lockfile';
}

return config.rangeStrategy;
}
1 change: 1 addition & 0 deletions lib/types/versioning.ts
Expand Up @@ -5,4 +5,5 @@ export type RangeStrategy =
| 'pin'
| 'replace'
| 'update-lockfile'
| 'in-range-only'
| 'widen';
5 changes: 5 additions & 0 deletions lib/workers/branch/reuse.spec.ts
Expand Up @@ -54,6 +54,11 @@ describe('workers/branch/reuse', () => {
rangeStrategy: 'update-lockfile',
branchName: 'current',
},
{
packageFile: 'package.json',
rangeStrategy: 'in-range-only',
branchName: 'current',
},
];
git.branchExists.mockReturnValueOnce(true);
git.isBranchConflicted.mockResolvedValueOnce(false);
Expand Down
Expand Up @@ -238,6 +238,22 @@ Object {
}
`;

exports[`workers/repository/process/lookup/index .lookupUpdates() handles the in-range-only strategy and updates lockfile within range 1`] = `
Array [
Object {
"bucket": "non-major",
"isLockfileUpdate": true,
"isRange": true,
"newMajor": 1,
"newMinor": 4,
"newValue": "^1.2.1",
"newVersion": "1.4.1",
"releaseTimestamp": "2015-05-17T04:25:07.299Z",
"updateType": "minor",
},
]
`;

exports[`workers/repository/process/lookup/index .lookupUpdates() handles unknown datasource 1`] = `Array []`;

exports[`workers/repository/process/lookup/index .lookupUpdates() handles update-lockfile 1`] = `
Expand Down
25 changes: 25 additions & 0 deletions lib/workers/repository/process/lookup/index.spec.ts
Expand Up @@ -31,6 +31,7 @@ const qJson = {
...loadJsonFixture('01.json', fixtureRoot),
latestVersion: '1.4.1',
};

const helmetJson = loadJsonFixture('02.json', fixtureRoot);
const coffeelintJson = loadJsonFixture('coffeelint.json', fixtureRoot);
const nextJson = loadJsonFixture('next.json', fixtureRoot);
Expand Down Expand Up @@ -341,6 +342,30 @@ describe('workers/repository/process/lookup/index', () => {
expect(res.updates).toMatchSnapshot();
expect(res.updates[0].updateType).toBe('minor');
});

it('handles the in-range-only strategy and updates lockfile within range', async () => {
config.currentValue = '^1.2.1';
config.lockedVersion = '1.2.1';
config.rangeStrategy = 'in-range-only';
config.depName = 'q';
config.datasource = datasourceNpmId;
httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
const res = await lookup.lookupUpdates(config);
expect(res.updates).toMatchSnapshot();
expect(res.updates[0].updateType).toBe('minor');
});

it('handles the in-range-only strategy and discards changes not within range', async () => {
config.currentValue = '~1.2.0';
config.lockedVersion = '1.2.0';
config.rangeStrategy = 'in-range-only';
config.depName = 'q';
config.datasource = datasourceNpmId;
httpMock.scope('https://registry.npmjs.org').get('/q').reply(200, qJson);
const res = await lookup.lookupUpdates(config);
expect(res.updates).toBeEmptyArray();
});

it('handles unconstrainedValue values', async () => {
config.lockedVersion = '1.2.1';
config.rangeStrategy = 'update-lockfile';
Expand Down
7 changes: 7 additions & 0 deletions lib/workers/repository/process/lookup/index.ts
Expand Up @@ -103,6 +103,7 @@ export async function lookupUpdates(
let allVersions = dependency.releases.filter((release) =>
versioning.isVersion(release.version)
);

// istanbul ignore if
if (allVersions.length === 0) {
const message = `Found no results from datasource that look like a version`;
Expand Down Expand Up @@ -364,6 +365,12 @@ export async function lookupUpdates(
update.isLockfileUpdate ||
(update.newDigest && !update.newDigest.startsWith(currentDigest))
);
// If range strategy specified in config is 'in-range-only', also strip out updates where currentValue !== newValue
if (config.rangeStrategy === 'in-range-only') {
res.updates = res.updates.filter(
(update) => update.newValue === currentValue
);
}
} catch (err) /* istanbul ignore next */ {
if (err instanceof ExternalHostError || err.message === CONFIG_VALIDATION) {
throw err;
Expand Down

0 comments on commit 368903a

Please sign in to comment.