From bdabe43094c47d6c061fef05e0202d7d079e641e Mon Sep 17 00:00:00 2001 From: Yun Lai <ylai@squareup.com> Date: Wed, 1 Nov 2023 00:04:30 +1100 Subject: [PATCH] feat(hostRules): support https options and platform in host rules from env (#25015) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- docs/usage/self-hosted-configuration.md | 23 ++++++- .../config/parse/host-rules-from-env.spec.ts | 42 +++++++++++++ .../config/parse/host-rules-from-env.ts | 62 +++++++++++++++++-- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index c92325ff76..c06bb004c6 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -266,11 +266,11 @@ 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 (`_`). @@ -278,6 +278,7 @@ 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 @@ -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. diff --git a/lib/workers/global/config/parse/host-rules-from-env.spec.ts b/lib/workers/global/config/parse/host-rules-from-env.spec.ts index 39a624e074..a23234e4a1 100644 --- a/lib/workers/global/config/parse/host-rules-from-env.spec.ts +++ b/lib/workers/global/config/parse/host-rules-from-env.spec.ts @@ -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', @@ -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', diff --git a/lib/workers/global/config/parse/host-rules-from-env.ts b/lib/workers/global/config/parse/host-rules-from-env.ts index 2db3e81a71..42b0c2928e 100644 --- a/lib/workers/global/config/parse/host-rules-from-env.ts +++ b/lib/workers/global/config/parse/host-rules-from-env.ts @@ -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[] = []; @@ -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) { @@ -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 = { @@ -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); } } -- GitLab