Skip to content

Commit

Permalink
feat(hostRules): support https options and platform in host rules fro…
Browse files Browse the repository at this point in the history
…m env (#25015)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
  • Loading branch information
lyonlai and viceice committed Oct 31, 2023
1 parent e231ab8 commit bdabe43
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 8 deletions.
23 changes: 21 additions & 2 deletions docs/usage/self-hosted-configuration.md
Expand Up @@ -266,18 +266,19 @@ If found, it will be imported into `config.npmrc` with `config.npmrcMerge` set t

The format of the environment variables must follow:

- Datasource name (e.g. `NPM`, `PYPI`)
- Datasource name (e.g. `NPM`, `PYPI`) or Platform name (only `GITHUB`)
- Underscore (`_`)
- `matchHost`
- Underscore (`_`)
- Field name (`TOKEN`, `USERNAME`, or `PASSWORD`)
- Field name (`TOKEN`, `USERNAME`, `PASSWORD`, `HTTPSPRIVATEKEY`, `HTTPSCERTIFICATE`, `HTTPSCERTIFICATEAUTHORITY`)

Hyphens (`-`) in datasource or host name must be replaced with double underscores (`__`).
Periods (`.`) in host names must be replaced with a single underscore (`_`).

<!-- prettier-ignore -->
!!! note
You can't use these prefixes with the `detectHostRulesFromEnv` config option: `npm_config_`, `npm_lifecycle_`, `npm_package_`.
In addition, platform host rules will only be picked up when `matchHost` is supplied.

### npmjs registry token example

Expand Down Expand Up @@ -330,6 +331,24 @@ You can skip the host part, and use only the datasource and credentials.
}
```

### Platform with https authentication options

`GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSCERTIFICATE=certificate GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSPRIVATEKEY=private-key GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSCERTIFICATEAUTHORITY=certificate-authority`:

```json
{
"hostRules": [
{
"hostType": "github",
"matchHost": "some.github-enterprise.host",
"httpsPrivateKey": "private-key",
"httpsCertificate": "certificate",
"httpsCertificateAuthority": "certificate-authority"
}
]
}
```

## dockerChildPrefix

Adds a custom prefix to the default Renovate sidecar Docker containers name and label.
Expand Down
42 changes: 42 additions & 0 deletions lib/workers/global/config/parse/host-rules-from-env.spec.ts
Expand Up @@ -51,6 +51,35 @@ describe('workers/global/config/parse/host-rules-from-env', () => {
]);
});

it('support https authentication options', () => {
const envParam: NodeJS.ProcessEnv = {
GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSPRIVATEKEY: 'private-key',
GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSCERTIFICATE: 'certificate',
GITHUB_SOME_GITHUB__ENTERPRISE_HOST_HTTPSCERTIFICATEAUTHORITY:
'certificate-authority',
};
expect(hostRulesFromEnv(envParam)).toMatchObject([
{
hostType: 'github',
matchHost: 'some.github-enterprise.host',
httpsPrivateKey: 'private-key',
httpsCertificate: 'certificate',
httpsCertificateAuthority: 'certificate-authority',
},
]);
});

it('make sure {{PLATFORM}}_TOKEN will not be picked up', () => {
const unsupportedEnv = ['GITHUB_TOKEN'];

for (const e of unsupportedEnv) {
const envParam: NodeJS.ProcessEnv = {
[e]: 'private-key',
};
expect(hostRulesFromEnv(envParam)).toMatchObject([]);
}
});

it('supports datasource env token', () => {
const envParam: NodeJS.ProcessEnv = {
PYPI_TOKEN: 'some-token',
Expand All @@ -60,6 +89,19 @@ describe('workers/global/config/parse/host-rules-from-env', () => {
]);
});

it('supports platform env token', () => {
const envParam: NodeJS.ProcessEnv = {
GITHUB_SOME_GITHUB__ENTERPRISE_HOST_TOKEN: 'some-token',
};
expect(hostRulesFromEnv(envParam)).toMatchObject([
{
hostType: 'github',
matchHost: 'some.github-enterprise.host',
token: 'some-token',
},
]);
});

it('rejects incomplete datasource env token', () => {
const envParam: NodeJS.ProcessEnv = {
PYPI_FOO_TOKEN: 'some-token',
Expand Down
62 changes: 56 additions & 6 deletions lib/workers/global/config/parse/host-rules-from-env.ts
Expand Up @@ -4,12 +4,57 @@ import type { HostRule } from '../../../../types';

type AuthField = 'token' | 'username' | 'password';

type HttpsAuthField =
| 'httpscertificate'
| 'httpsprivatekey'
| 'httpscertificateauthority';

function isAuthField(x: unknown): x is AuthField {
return x === 'token' || x === 'username' || x === 'password';
}

function isHttpsAuthField(x: unknown): x is HttpsAuthField {
return (
x === 'httpscertificate' ||
x === 'httpsprivatekey' ||
x === 'httpscertificateauthority'
);
}

function restoreHttpsAuthField(x: HttpsAuthField | AuthField): string {
switch (x) {
case 'httpsprivatekey':
return 'httpsPrivateKey';
case 'httpscertificate':
return 'httpsCertificate';
case 'httpscertificateauthority':
return 'httpsCertificateAuthority';
}

return x;
}

function setHostRuleValue(
rule: HostRule,
key: string,
value: string | undefined
): void {
if (value !== undefined) {
switch (key) {
case 'token':
case 'username':
case 'password':
case 'httpsCertificateAuthority':
case 'httpsCertificate':
case 'httpsPrivateKey':
rule[key] = value;
}
}
}

export function hostRulesFromEnv(env: NodeJS.ProcessEnv): HostRule[] {
const datasources = new Set(getDatasourceList());
const platforms = new Set(['github']);

const hostRules: HostRule[] = [];

Expand All @@ -23,12 +68,17 @@ export function hostRulesFromEnv(env: NodeJS.ProcessEnv): HostRule[] {
// Double underscore __ is used in place of hyphen -
const splitEnv = envName.toLowerCase().replace(/__/g, '-').split('_');
const hostType = splitEnv.shift()!;
if (datasources.has(hostType)) {
const suffix = splitEnv.pop()!;
if (isAuthField(suffix)) {
if (
datasources.has(hostType) ||
(platforms.has(hostType) && splitEnv.length > 1)
) {
let suffix = splitEnv.pop()!;
if (isAuthField(suffix) || isHttpsAuthField(suffix)) {
suffix = restoreHttpsAuthField(suffix);

let matchHost: string | undefined = undefined;
const rule: HostRule = {};
rule[suffix] = env[envName];
setHostRuleValue(rule, suffix, env[envName]);
if (splitEnv.length === 0) {
// host-less rule
} else if (splitEnv.length === 1) {
Expand All @@ -43,7 +93,7 @@ export function hostRulesFromEnv(env: NodeJS.ProcessEnv): HostRule[] {
logger.debug(`Converting ${envName} into a global host rule`);
if (existingRule) {
// Add current field to existing rule
existingRule[suffix] = env[envName];
setHostRuleValue(existingRule, suffix, env[envName]);
} else {
// Create a new rule
const newRule: HostRule = {
Expand All @@ -52,7 +102,7 @@ export function hostRulesFromEnv(env: NodeJS.ProcessEnv): HostRule[] {
if (matchHost) {
newRule.matchHost = matchHost;
}
newRule[suffix] = env[envName];
setHostRuleValue(newRule, suffix, env[envName]);
hostRules.push(newRule);
}
}
Expand Down

0 comments on commit bdabe43

Please sign in to comment.