mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 15:06:27 +00:00
feat(config)!: make host rule detection configurable and opt-in (#12294)
Add configuration option `detectHostRulesFromEnv`. BREAKING CHANGE: `hostRules` are no longer automatically derived from env variables such as `NPM_X_TOKEN`. Set `detectHostRulesFromEnv=true` in config to re-enable same functionality.
This commit is contained in:
parent
f14d5d9171
commit
70700eedae
9 changed files with 93 additions and 194 deletions
|
@ -392,68 +392,4 @@ Note: Encrypted values can't be used in the "Admin/Bot config".
|
||||||
|
|
||||||
### hostRules configuration using environment variables
|
### hostRules configuration using environment variables
|
||||||
|
|
||||||
Self-hosted users can use environment variables to configure the most common types of `hostRules` for authentication.
|
Self-hosted users can enable the option [`detectHostRulesFromEnv`](../self-hosted-configuration.md#detectHostRulesFromEnv) to configure the most common types of `hostRules` via environment variables.
|
||||||
|
|
||||||
The format of the environment variables must follow:
|
|
||||||
|
|
||||||
- Datasource name (e.g. `NPM`, `PYPI`)
|
|
||||||
- Underscore (`_`)
|
|
||||||
- `matchHost`
|
|
||||||
- Underscore (`_`)
|
|
||||||
- Field name (`TOKEN`, `USER_NAME`, or `PASSWORD`)
|
|
||||||
|
|
||||||
Hyphens (`-`) in datasource or host name must be replaced with double underscores (`__`).
|
|
||||||
Periods (`.`) in host names must be replaced with a single underscore (`_`).
|
|
||||||
|
|
||||||
Note: the following prefixes cannot be supported for this functionality: `npm_config_`, `npm_lifecycle_`, `npm_package_`.
|
|
||||||
|
|
||||||
#### npmjs registry token example
|
|
||||||
|
|
||||||
`NPM_REGISTRY_NPMJS_ORG_TOKEN=abc123`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hostRules": [
|
|
||||||
{
|
|
||||||
"hostType": "npm",
|
|
||||||
"matchHost": "registry.npmjs.org",
|
|
||||||
"token": "abc123"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GitLab Tags username/password example
|
|
||||||
|
|
||||||
`GITLAB__TAGS_CODE__HOST_COMPANY_COM_USERNAME=bot GITLAB__TAGS_CODE__HOST_COMPANY_COM_PASSWORD=botpass123`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hostRules": [
|
|
||||||
{
|
|
||||||
"hostType": "gitlab-tags",
|
|
||||||
"matchHost": "code-host.company.com",
|
|
||||||
"username": "bot",
|
|
||||||
"password": "botpass123"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Datasource and credentials only
|
|
||||||
|
|
||||||
You can skip the host part, and use just the datasource and credentials.
|
|
||||||
|
|
||||||
`DOCKER_USERNAME=bot DOCKER_PASSWORD=botpass123`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hostRules": [
|
|
||||||
{
|
|
||||||
"hostType": "docker",
|
|
||||||
"username": "bot",
|
|
||||||
"password": "botpass123"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -139,6 +139,72 @@ This feature is disabled by default because it may prove surprising or undesirab
|
||||||
Currently this capability is supported for the `npm` manager only - specifically the `~/.npmrc` file.
|
Currently this capability is supported for the `npm` manager only - specifically the `~/.npmrc` file.
|
||||||
If found, it will be imported into `config.npmrc` with `config.npmrcMerge` will be set to `true`.
|
If found, it will be imported into `config.npmrc` with `config.npmrcMerge` will be set to `true`.
|
||||||
|
|
||||||
|
## detectHostRulesFromEnv
|
||||||
|
|
||||||
|
The format of the environment variables must follow:
|
||||||
|
|
||||||
|
- Datasource name (e.g. `NPM`, `PYPI`)
|
||||||
|
- Underscore (`_`)
|
||||||
|
- `matchHost`
|
||||||
|
- Underscore (`_`)
|
||||||
|
- Field name (`TOKEN`, `USER_NAME`, or `PASSWORD`)
|
||||||
|
|
||||||
|
Hyphens (`-`) in datasource or host name must be replaced with double underscores (`__`).
|
||||||
|
Periods (`.`) in host names must be replaced with a single underscore (`_`).
|
||||||
|
|
||||||
|
Note: the following prefixes cannot be supported for this functionality: `npm_config_`, `npm_lifecycle_`, `npm_package_`.
|
||||||
|
|
||||||
|
### npmjs registry token example
|
||||||
|
|
||||||
|
`NPM_REGISTRY_NPMJS_ORG_TOKEN=abc123`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hostRules": [
|
||||||
|
{
|
||||||
|
"hostType": "npm",
|
||||||
|
"matchHost": "registry.npmjs.org",
|
||||||
|
"token": "abc123"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GitLab Tags username/password example
|
||||||
|
|
||||||
|
`GITLAB__TAGS_CODE__HOST_COMPANY_COM_USERNAME=bot GITLAB__TAGS_CODE__HOST_COMPANY_COM_PASSWORD=botpass123`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hostRules": [
|
||||||
|
{
|
||||||
|
"hostType": "gitlab-tags",
|
||||||
|
"matchHost": "code-host.company.com",
|
||||||
|
"username": "bot",
|
||||||
|
"password": "botpass123"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Datasource and credentials only
|
||||||
|
|
||||||
|
You can skip the host part, and use just the datasource and credentials.
|
||||||
|
|
||||||
|
`DOCKER_USERNAME=bot DOCKER_PASSWORD=botpass123`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hostRules": [
|
||||||
|
{
|
||||||
|
"hostType": "docker",
|
||||||
|
"username": "bot",
|
||||||
|
"password": "botpass123"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## dockerChildPrefix
|
## dockerChildPrefix
|
||||||
|
|
||||||
Adds a custom prefix to the default Renovate sidecar Docker containers name and label.
|
Adds a custom prefix to the default Renovate sidecar Docker containers name and label.
|
||||||
|
|
|
@ -15,6 +15,14 @@ const options: RenovateOptions[] = [
|
||||||
default: false,
|
default: false,
|
||||||
globalOnly: true,
|
globalOnly: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'detectHostRulesFromEnv',
|
||||||
|
description:
|
||||||
|
'If true, Renovate tries to detect host rules from environment variables.',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
globalOnly: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'allowPostUpgradeCommandTemplating',
|
name: 'allowPostUpgradeCommandTemplating',
|
||||||
description: 'If true allow templating for post-upgrade commands.',
|
description: 'If true allow templating for post-upgrade commands.',
|
||||||
|
|
|
@ -74,6 +74,7 @@ export interface GlobalOnlyConfig {
|
||||||
autodiscoverFilter?: string;
|
autodiscoverFilter?: string;
|
||||||
baseDir?: string;
|
baseDir?: string;
|
||||||
cacheDir?: string;
|
cacheDir?: string;
|
||||||
|
detectHostRulesFromEnv?: boolean;
|
||||||
forceCli?: boolean;
|
forceCli?: boolean;
|
||||||
gitNoVerify?: GitNoVerifyOption[];
|
gitNoVerify?: GitNoVerifyOption[];
|
||||||
gitPrivateKey?: string;
|
gitPrivateKey?: string;
|
||||||
|
|
|
@ -81,54 +81,3 @@ Object {
|
||||||
"token": "a gitlab.com token",
|
"token": "a gitlab.com token",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`workers/global/config/parse/env .getConfig(env) supports datasource env token 1`] = `
|
|
||||||
Object {
|
|
||||||
"hostRules": Array [
|
|
||||||
Object {
|
|
||||||
"hostType": "pypi",
|
|
||||||
"token": "some-token",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`workers/global/config/parse/env .getConfig(env) supports docker username/password 1`] = `
|
|
||||||
Object {
|
|
||||||
"hostRules": Array [
|
|
||||||
Object {
|
|
||||||
"hostType": "docker",
|
|
||||||
"password": "some-password",
|
|
||||||
"username": "some-username",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`workers/global/config/parse/env .getConfig(env) supports domain and host names with case insensitivity 1`] = `
|
|
||||||
Object {
|
|
||||||
"hostRules": Array [
|
|
||||||
Object {
|
|
||||||
"hostType": "github-tags",
|
|
||||||
"matchHost": "github.com",
|
|
||||||
"token": "some-token",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"hostType": "pypi",
|
|
||||||
"matchHost": "my.custom.host",
|
|
||||||
"password": "some-password",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`workers/global/config/parse/env .getConfig(env) supports password-only 1`] = `
|
|
||||||
Object {
|
|
||||||
"hostRules": Array [
|
|
||||||
Object {
|
|
||||||
"hostType": "npm",
|
|
||||||
"password": "some-password",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
|
@ -158,79 +158,6 @@ describe('workers/global/config/parse/env', () => {
|
||||||
token: 'an Azure DevOps token',
|
token: 'an Azure DevOps token',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('supports docker username/password', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
DOCKER_USERNAME: 'some-username',
|
|
||||||
DOCKER_PASSWORD: 'some-password',
|
|
||||||
};
|
|
||||||
expect(env.getConfig(envParam)).toMatchSnapshot({
|
|
||||||
hostRules: [
|
|
||||||
{
|
|
||||||
hostType: 'docker',
|
|
||||||
password: 'some-password',
|
|
||||||
username: 'some-username',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('supports password-only', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
NPM_PASSWORD: 'some-password',
|
|
||||||
};
|
|
||||||
expect(env.getConfig(envParam)).toMatchSnapshot({
|
|
||||||
hostRules: [{ hostType: 'npm', password: 'some-password' }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('supports domain and host names with case insensitivity', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
GITHUB__TAGS_GITHUB_COM_TOKEN: 'some-token',
|
|
||||||
pypi_my_CUSTOM_HOST_passWORD: 'some-password',
|
|
||||||
};
|
|
||||||
const res = env.getConfig(envParam);
|
|
||||||
expect(res).toMatchSnapshot({
|
|
||||||
hostRules: [
|
|
||||||
{ matchHost: 'github.com', token: 'some-token' },
|
|
||||||
{ matchHost: 'my.custom.host', password: 'some-password' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('regression test for #10937', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
GIT__TAGS_GITLAB_EXAMPLE__DOMAIN_NET_USERNAME: 'some-user',
|
|
||||||
GIT__TAGS_GITLAB_EXAMPLE__DOMAIN_NET_PASSWORD: 'some-password',
|
|
||||||
};
|
|
||||||
const res = env.getConfig(envParam);
|
|
||||||
expect(res).toMatchObject({
|
|
||||||
hostRules: [
|
|
||||||
{
|
|
||||||
hostType: 'git-tags',
|
|
||||||
matchHost: 'gitlab.example-domain.net',
|
|
||||||
password: 'some-password',
|
|
||||||
username: 'some-user',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('supports datasource env token', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
PYPI_TOKEN: 'some-token',
|
|
||||||
};
|
|
||||||
expect(env.getConfig(envParam)).toMatchSnapshot({
|
|
||||||
hostRules: [{ hostType: 'pypi', token: 'some-token' }],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('rejects incomplete datasource env token', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
PYPI_FOO_TOKEN: 'some-token',
|
|
||||||
};
|
|
||||||
expect(env.getConfig(envParam).hostRules).toHaveLength(0);
|
|
||||||
});
|
|
||||||
it('rejects npm env', () => {
|
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
|
||||||
npm_package_devDependencies__types_registry_auth_token: '4.2.0',
|
|
||||||
};
|
|
||||||
expect(env.getConfig(envParam).hostRules).toHaveLength(0);
|
|
||||||
});
|
|
||||||
it('supports Bitbucket token', () => {
|
it('supports Bitbucket token', () => {
|
||||||
const envParam: NodeJS.ProcessEnv = {
|
const envParam: NodeJS.ProcessEnv = {
|
||||||
RENOVATE_PLATFORM: PlatformId.Bitbucket,
|
RENOVATE_PLATFORM: PlatformId.Bitbucket,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { getOptions } from '../../../../config/options';
|
||||||
import type { AllConfig, RenovateOptions } from '../../../../config/types';
|
import type { AllConfig, RenovateOptions } from '../../../../config/types';
|
||||||
import { PlatformId } from '../../../../constants';
|
import { PlatformId } from '../../../../constants';
|
||||||
import { logger } from '../../../../logger';
|
import { logger } from '../../../../logger';
|
||||||
import { hostRulesFromEnv } from './host-rules-from-env';
|
|
||||||
|
|
||||||
function normalizePrefixes(
|
function normalizePrefixes(
|
||||||
env: NodeJS.ProcessEnv,
|
env: NodeJS.ProcessEnv,
|
||||||
|
@ -118,8 +117,6 @@ export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
config.hostRules = [...config.hostRules, ...hostRulesFromEnv(env)];
|
|
||||||
|
|
||||||
// These env vars are deprecated and deleted to make sure they're not used
|
// These env vars are deprecated and deleted to make sure they're not used
|
||||||
const unsupportedEnv = [
|
const unsupportedEnv = [
|
||||||
'BITBUCKET_TOKEN',
|
'BITBUCKET_TOKEN',
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
import upath from 'upath';
|
import upath from 'upath';
|
||||||
|
import { mocked } from '../../../../../test/util';
|
||||||
import { readFile } from '../../../../util/fs';
|
import { readFile } from '../../../../util/fs';
|
||||||
import getArgv from './__fixtures__/argv';
|
import getArgv from './__fixtures__/argv';
|
||||||
|
import * as _hostRulesFromEnv from './host-rules-from-env';
|
||||||
|
|
||||||
jest.mock('../../../../datasource/npm');
|
jest.mock('../../../../datasource/npm');
|
||||||
jest.mock('../../../../util/fs');
|
jest.mock('../../../../util/fs');
|
||||||
|
jest.mock('./host-rules-from-env');
|
||||||
try {
|
try {
|
||||||
jest.mock('../../config.js');
|
jest.mock('../../config.js');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// file does not exist
|
// file does not exist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { hostRulesFromEnv } = mocked(_hostRulesFromEnv);
|
||||||
|
|
||||||
describe('workers/global/config/parse/index', () => {
|
describe('workers/global/config/parse/index', () => {
|
||||||
describe('.parseConfigs(env, defaultArgv)', () => {
|
describe('.parseConfigs(env, defaultArgv)', () => {
|
||||||
let configParser: typeof import('.');
|
let configParser: typeof import('.');
|
||||||
let defaultArgv: string[];
|
let defaultArgv: string[];
|
||||||
let defaultEnv: NodeJS.ProcessEnv;
|
let defaultEnv: NodeJS.ProcessEnv;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.resetModules();
|
|
||||||
configParser = await import('./index');
|
configParser = await import('./index');
|
||||||
defaultArgv = getArgv();
|
defaultArgv = getArgv();
|
||||||
defaultEnv = {
|
defaultEnv = {
|
||||||
|
@ -125,5 +128,12 @@ describe('workers/global/config/parse/index', () => {
|
||||||
const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv);
|
const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv);
|
||||||
expect(parsed.npmrc).toBeNull();
|
expect(parsed.npmrc).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('parses host rules from env', async () => {
|
||||||
|
defaultArgv = defaultArgv.concat(['--detect-host-rules-from-env=true']);
|
||||||
|
hostRulesFromEnv.mockReturnValueOnce([{ matchHost: 'example.org' }]);
|
||||||
|
const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv);
|
||||||
|
expect(parsed.hostRules).toContainEqual({ matchHost: 'example.org' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ensureTrailingSlash } from '../../../../util/url';
|
||||||
import * as cliParser from './cli';
|
import * as cliParser from './cli';
|
||||||
import * as envParser from './env';
|
import * as envParser from './env';
|
||||||
import * as fileParser from './file';
|
import * as fileParser from './file';
|
||||||
|
import { hostRulesFromEnv } from './host-rules-from-env';
|
||||||
|
|
||||||
export async function parseConfigs(
|
export async function parseConfigs(
|
||||||
env: NodeJS.ProcessEnv,
|
env: NodeJS.ProcessEnv,
|
||||||
|
@ -81,6 +82,10 @@ export async function parseConfigs(
|
||||||
config = mergeChildConfig(config, globalManagerConfig);
|
config = mergeChildConfig(config, globalManagerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.detectHostRulesFromEnv) {
|
||||||
|
const hostRules = hostRulesFromEnv(env);
|
||||||
|
config.hostRules = [...config.hostRules, ...hostRules];
|
||||||
|
}
|
||||||
// Get global config
|
// Get global config
|
||||||
logger.trace({ config }, 'Full config');
|
logger.trace({ config }, 'Full config');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue