Skip to content

Commit

Permalink
feat(npm): npmrcMerge (#11857)
Browse files Browse the repository at this point in the history
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
  • Loading branch information
rarkins and HonkingGoose authored Sep 23, 2021
1 parent da8c324 commit 0f22613
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 10 deletions.
9 changes: 9 additions & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,15 @@ Typically you would encrypt it and put it inside the `encrypted` object.

See [Private npm module support](https://docs.renovatebot.com/getting-started/private-packages) for details on how this is used.

## npmrcMerge

This option exists to provide flexibility about whether `npmrc` strings in config should override `.npmrc` files in the repo, or be merged with them.
In some situations you need the ability to force override `.npmrc` contents in a repo (`npmMerge=false`) while in others you might want to simply supplement the settings already in the `.npmrc` (`npmMerge=true`).
A use case for the latter is if you are a Renovate bot admin and wish to provide a default token for `npmjs.org` without removing any other `.npmrc` settings which individual repositories have configured (such as scopes/registries).

If `false` (default), it means that defining `config.npmrc` will result in any `.npmrc` file in the repo being overridden and therefore its values ignored.
If configured to `true`, it means that any `.npmrc` file in the repo will have `config.npmrc` prepended to it before running `npm`.

## packageRules

`packageRules` is a powerful feature that lets you apply rules to individual packages or to groups of packages using regex pattern matching.
Expand Down
2 changes: 2 additions & 0 deletions docs/usage/getting-started/private-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ You can add an `.npmrc` authentication line to your Renovate config under the fi
```

If configured like this, Renovate will use this to authenticate with npm and will ignore any `.npmrc` files(s) it finds checked into the repository.
If you wish for the values in your `config.npmrc` to be _merged_ (prepended) with any values found in repos then also set `config.npmrcMerge=true`.
This merge approach is similar to how `npm` itself behaves if `.npmrc` is found in both the user home directory as well as a project.

#### Add npmToken to Renovate config

Expand Down
8 changes: 8 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,14 @@ const options: RenovateOptions[] = [
stage: 'branch',
type: 'string',
},
{
name: 'npmrcMerge',
description:
'Whether to merge config.npmrc with repo .npmrc content if both are found.',
stage: 'branch',
type: 'boolean',
default: false,
},
{
name: 'npmToken',
description: 'npm token used for authenticating with the default registry.',
Expand Down
1 change: 1 addition & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface RenovateSharedConfig {
dependencyDashboardApproval?: boolean;
hashedBranchLength?: number;
npmrc?: string;
npmrcMerge?: boolean;
platform?: string;
postUpgradeTasks?: PostUpgradeTasks;
prBodyColumns?: string[];
Expand Down
16 changes: 15 additions & 1 deletion lib/manager/npm/extract/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('manager/npm/extract/index', () => {
);
expect(res.npmrc).toBeDefined();
});
it('ignores .npmrc when config.npmrc is defined', async () => {
it('ignores .npmrc when config.npmrc is defined and npmrcMerge=false', async () => {
fs.readLocalFile = jest.fn((fileName) => {
if (fileName === '.npmrc') {
return 'some-npmrc\n';
Expand All @@ -136,6 +136,20 @@ describe('manager/npm/extract/index', () => {
);
expect(res.npmrc).toBeUndefined();
});
it('reads .npmrc when config.npmrc is merged', async () => {
fs.readLocalFile = jest.fn((fileName) => {
if (fileName === '.npmrc') {
return 'repo-npmrc\n';
}
return null;
});
const res = await npmExtract.extractPackageFile(
input01Content,
'package.json',
{ npmrc: 'config-npmrc', npmrcMerge: true }
);
expect(res.npmrc).toEqual(`config-npmrc\nrepo-npmrc\n`);
});
it('finds and filters .npmrc with variables', async () => {
fs.readLocalFile = jest.fn((fileName) => {
if (fileName === '.npmrc') {
Expand Down
22 changes: 13 additions & 9 deletions lib/manager/npm/extract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,29 +96,33 @@ export async function extractPackageFile(

let npmrc: string;
const npmrcFileName = getSiblingFileName(fileName, '.npmrc');
const npmrcContent = await readLocalFile(npmrcFileName, 'utf8');
if (is.string(npmrcContent)) {
if (is.string(config.npmrc)) {
let repoNpmrc = await readLocalFile(npmrcFileName, 'utf8');
if (is.string(repoNpmrc)) {
if (is.string(config.npmrc) && !config.npmrcMerge) {
logger.debug(
{ npmrcFileName },
'Repo .npmrc file is ignored due to presence of config.npmrc'
'Repo .npmrc file is ignored due to config.npmrc with config.npmrcMerge=force'
);
} else {
npmrc = npmrcContent;
if (npmrc?.includes('package-lock')) {
npmrc = config.npmrc || '';
if (npmrc.length) {
npmrc = npmrc.replace(/\n?$/, '\n');
}
if (repoNpmrc?.includes('package-lock')) {
logger.debug('Stripping package-lock setting from .npmrc');
npmrc = npmrc.replace(/(^|\n)package-lock.*?(\n|$)/g, '\n');
repoNpmrc = repoNpmrc.replace(/(^|\n)package-lock.*?(\n|$)/g, '\n');
}
if (npmrc.includes('=${') && !getGlobalConfig().exposeAllEnv) {
if (repoNpmrc.includes('=${') && !getGlobalConfig().exposeAllEnv) {
logger.debug(
{ npmrcFileName },
'Stripping .npmrc file of lines with variables'
);
npmrc = npmrc
repoNpmrc = repoNpmrc
.split('\n')
.filter((line) => !line.includes('=${'))
.join('\n');
}
npmrc += repoNpmrc;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/manager/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface ExtractConfig {
gradle?: { timeout?: number };
aliases?: Record<string, string>;
npmrc?: string;
npmrcMerge?: boolean;
skipInstalls?: boolean;
updateInternalDeps?: boolean;
deepExtract?: boolean;
Expand Down

0 comments on commit 0f22613

Please sign in to comment.