Skip to content

Commit

Permalink
feat(manager/npm): Optimize npm dedupe option (#25466)
Browse files Browse the repository at this point in the history
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
  • Loading branch information
jelhan and HonkingGoose committed Nov 1, 2023
1 parent 837c885 commit e0f9266
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 23 deletions.
2 changes: 1 addition & 1 deletion docs/usage/configuration-options.md
Expand Up @@ -2972,7 +2972,7 @@ Table with options:
| `gomodTidyE` | Run `go mod tidy -e` after Go module updates. |
| `gomodUpdateImportPaths` | Update source import paths on major module updates, using [mod](https://github.com/marwan-at-work/mod). |
| `helmUpdateSubChartArchives` | Update subchart archives in the `/charts` folder. |
| `npmDedupe` | Run `npm dedupe` after `package-lock.json` updates. |
| `npmDedupe` | Run `npm install` with `--prefer-dedupe` for npm >= 7 or `npm dedupe` after `package-lock.json` update for npm <= 6. |
| `pnpmDedupe` | Run `pnpm dedupe --config.ignore-scripts=true` after `pnpm-lock.yaml` updates. |
| `yarnDedupeFewer` | Run `yarn-deduplicate --strategy fewer` after `yarn.lock` updates. |
| `yarnDedupeHighest` | Run `yarn-deduplicate --strategy highest` (`yarn dedupe --strategy highest` for Yarn >=2.2.0) after `yarn.lock` updates. |
Expand Down
Expand Up @@ -3,25 +3,7 @@
exports[`modules/manager/npm/post-update/npm generates lock files 1`] = `
[
{
"cmd": "npm install --no-audit --ignore-scripts",
"options": {
"cwd": "some-dir",
"encoding": "utf-8",
"env": {
"HOME": "/home/user",
"HTTPS_PROXY": "https://example.com",
"HTTP_PROXY": "http://example.com",
"LANG": "en_US.UTF-8",
"LC_ALL": "en_US",
"NO_PROXY": "localhost",
"PATH": "/tmp/path",
},
"maxBuffer": 10485760,
"timeout": 900000,
},
},
{
"cmd": "npm dedupe",
"cmd": "npm install --package-lock-only --no-audit --prefer-dedupe --ignore-scripts",
"options": {
"cwd": "some-dir",
"encoding": "utf-8",
Expand Down
56 changes: 56 additions & 0 deletions lib/modules/manager/npm/post-update/npm.spec.ts
Expand Up @@ -162,6 +162,62 @@ describe('modules/manager/npm/post-update/npm', () => {
expect(execSnapshots).toEqual([]);
});

it('deduplicates dependencies on installation with npm >= 7', async () => {
const execSnapshots = mockExecAll();
// package.json
fs.readLocalFile.mockResolvedValueOnce('{}');
fs.readLocalFile.mockResolvedValueOnce('package-lock-contents');
const postUpdateOptions = ['npmDedupe'];
const updates = [
{ packageName: 'some-dep', newVersion: '1.0.1', isLockfileUpdate: false },
];
const res = await npmHelper.generateLockFile(
'some-dir',
{},
'package-lock.json',
{ postUpdateOptions },
updates
);
expect(fs.readLocalFile).toHaveBeenCalledTimes(2);
expect(res.error).toBeFalse();
expect(res.lockFile).toBe('package-lock-contents');
expect(execSnapshots).toHaveLength(1);
expect(execSnapshots).toMatchObject([
{
cmd: 'npm install --package-lock-only --no-audit --prefer-dedupe --ignore-scripts',
},
]);
});

it('deduplicates dependencies after installation with npm <= 6', async () => {
const execSnapshots = mockExecAll();
// package.json
fs.readLocalFile.mockResolvedValueOnce('package-lock-contents');
const postUpdateOptions = ['npmDedupe'];
const updates = [
{ packageName: 'some-dep', newVersion: '1.0.1', isLockfileUpdate: false },
];
const res = await npmHelper.generateLockFile(
'some-dir',
{},
'package-lock.json',
{ postUpdateOptions, constraints: { npm: '^6.0.0' } },
updates
);
expect(fs.readLocalFile).toHaveBeenCalledTimes(1);
expect(res.error).toBeFalse();
expect(res.lockFile).toBe('package-lock-contents');
expect(execSnapshots).toHaveLength(2);
expect(execSnapshots).toMatchObject([
{
cmd: 'npm install --no-audit --ignore-scripts',
},
{
cmd: 'npm dedupe',
},
]);
});

it('runs twice if remediating', async () => {
const execSnapshots = mockExecAll();
fs.readLocalFile.mockResolvedValueOnce('package-lock-contents');
Expand Down
19 changes: 16 additions & 3 deletions lib/modules/manager/npm/post-update/npm.ts
@@ -1,5 +1,6 @@
// TODO: types (#22198)
import is from '@sindresorhus/is';
import semver from 'semver';
import upath from 'upath';
import { GlobalConfig } from '../../../../config/global';
import {
Expand Down Expand Up @@ -49,10 +50,14 @@ export async function generateLockFile(
config.constraints?.npm ??
getPackageManagerVersion('npm', await lazyPgkJson.getValue()),
};
const supportsPreferDedupeFlag =
!npmToolConstraint.constraint ||
semver.intersects('>=7.0.0', npmToolConstraint.constraint);
const commands: string[] = [];
let cmdOptions = '';
if (
postUpdateOptions?.includes('npmDedupe') === true ||
(postUpdateOptions?.includes('npmDedupe') === true &&
!supportsPreferDedupeFlag) ||
skipInstalls === false
) {
logger.debug('Performing node_modules install');
Expand All @@ -62,6 +67,11 @@ export async function generateLockFile(
cmdOptions += '--package-lock-only --no-audit';
}

if (postUpdateOptions?.includes('npmDedupe') && supportsPreferDedupeFlag) {
logger.debug('Deduplicate dependencies on installation');
cmdOptions += ' --prefer-dedupe';
}

if (!GlobalConfig.get('allowScripts') || config.ignoreScripts) {
cmdOptions += ' --ignore-scripts';
}
Expand Down Expand Up @@ -130,8 +140,11 @@ export async function generateLockFile(
}

// postUpdateOptions
if (config.postUpdateOptions?.includes('npmDedupe')) {
logger.debug('Performing npm dedupe');
if (
config.postUpdateOptions?.includes('npmDedupe') &&
!supportsPreferDedupeFlag
) {
logger.debug('Performing npm dedupe after installation');
commands.push('npm dedupe');
}

Expand Down

0 comments on commit e0f9266

Please sign in to comment.