Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(config): Better prefix handling and enhanced coverage #12083

Merged
merged 5 commits into from
Oct 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions lib/workers/global/config/parse/env.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RenovateOptions } from '../../../../config/types';
import { PlatformId } from '../../../../constants';
import { logger } from '../../../../logger';
import * as env from './env';

describe('workers/global/config/parse/env', () => {
Expand Down Expand Up @@ -32,12 +33,53 @@ describe('workers/global/config/parse/env', () => {
const envParam: NodeJS.ProcessEnv = { RENOVATE_TOKEN: 'a' };
expect(env.getConfig(envParam).token).toBe('a');
});
it('supports custom prefixes', () => {
const envParam: NodeJS.ProcessEnv = {
ENV_PREFIX: 'FOOBAR_',
FOOBAR_TOKEN: 'abc',
};
const res = env.getConfig(envParam);
expect(res).toMatchObject({ token: 'abc' });
});
it('supports json', () => {
const envParam: NodeJS.ProcessEnv = {
RENOVATE_LOCK_FILE_MAINTENANCE: '{}',
};
expect(env.getConfig(envParam).lockFileMaintenance).toEqual({});
});
it('supports arrays of objects', () => {
const envParam: NodeJS.ProcessEnv = {
RENOVATE_HOST_RULES: JSON.stringify([{ foo: 'bar' }]),
};
const res = env.getConfig(envParam);
expect(res).toMatchObject({ hostRules: [{ foo: 'bar' }] });
});
it('skips misconfigured arrays', () => {
const envName = 'RENOVATE_HOST_RULES';
const val = JSON.stringify('foobar');
const envParam: NodeJS.ProcessEnv = { [envName]: val };

const res = env.getConfig(envParam);

expect(res).toEqual({ hostRules: [] });
expect(logger.debug).toHaveBeenLastCalledWith(
{ val, envName },
'Could not parse object array'
);
});
it('skips garbage array values', () => {
const envName = 'RENOVATE_HOST_RULES';
const val = '!@#';
const envParam: NodeJS.ProcessEnv = { [envName]: val };

const res = env.getConfig(envParam);

expect(res).toEqual({ hostRules: [] });
expect(logger.debug).toHaveBeenLastCalledWith(
{ val, envName },
'Could not parse environment variable'
);
});
it('supports GitHub token', () => {
const envParam: NodeJS.ProcessEnv = {
RENOVATE_TOKEN: 'github.com token',
Expand Down Expand Up @@ -182,6 +224,25 @@ describe('workers/global/config/parse/env', () => {
expect(config.enabled).toBe(false);
expect(config.token).toBe('a');
});
describe('malformed RENOVATE_CONFIG', () => {
let processExit;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a type


beforeAll(() => {
processExit = jest
.spyOn(process, 'exit')
.mockImplementation((() => {}) as never);
viceice marked this conversation as resolved.
Show resolved Hide resolved
});

afterAll(() => {
processExit.mockRestore();
});

it('crashes', () => {
const envParam: NodeJS.ProcessEnv = { RENOVATE_CONFIG: '!@#' };
env.getConfig(envParam);
expect(processExit).toHaveBeenCalledWith(1);
});
});
});
describe('.getEnvName(definition)', () => {
it('returns empty', () => {
Expand Down
30 changes: 21 additions & 9 deletions lib/workers/global/config/parse/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ import { getDatasourceList } from '../../../../datasource';
import { logger } from '../../../../logger';
import type { HostRule } from '../../../../types';

// istanbul ignore if
if (process.env.ENV_PREFIX) {
for (const [key, val] of Object.entries(process.env)) {
if (key.startsWith(process.env.ENV_PREFIX)) {
process.env[key.replace(process.env.ENV_PREFIX, 'RENOVATE_')] = val;
function normalizePrefixes(
env: NodeJS.ProcessEnv,
prefix: string | undefined
): NodeJS.ProcessEnv {
const result = { ...env };
if (prefix) {
for (const [key, val] of Object.entries(result)) {
if (key.startsWith(prefix)) {
const newKey = key.replace(prefix, 'RENOVATE_');
result[newKey] = val;
delete result[key];
}
}
}
return result;
}

export function getEnvName(option: Partial<RenovateOptions>): string {
Expand All @@ -27,7 +35,9 @@ export function getEnvName(option: Partial<RenovateOptions>): string {
return `RENOVATE_${nameWithUnderscores.toUpperCase()}`;
}

export function getConfig(env: NodeJS.ProcessEnv): AllConfig {
export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig {
const env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX);

const options = getOptions();

let config: AllConfig = {};
Expand All @@ -36,7 +46,7 @@ export function getConfig(env: NodeJS.ProcessEnv): AllConfig {
try {
config = JSON.parse(env.RENOVATE_CONFIG);
logger.debug({ config }, 'Detected config in env RENOVATE_CONFIG');
} catch (err) /* istanbul ignore next */ {
} catch (err) {
logger.fatal({ err }, 'Could not parse RENOVATE_CONFIG');
process.exit(1);
}
Expand All @@ -56,7 +66,6 @@ export function getConfig(env: NodeJS.ProcessEnv): AllConfig {
if (option.env !== false) {
const envName = getEnvName(option);
if (env[envName]) {
// istanbul ignore if
if (option.type === 'array' && option.subType === 'object') {
try {
const parsed = JSON.parse(env[envName]);
Expand All @@ -69,7 +78,10 @@ export function getConfig(env: NodeJS.ProcessEnv): AllConfig {
);
}
} catch (err) {
logger.debug({ val: env[envName], envName }, 'Could not parse CLI');
logger.debug(
{ val: env[envName], envName },
'Could not parse environment variable'
);
}
} else {
const coerce = coersions[option.type];
Expand Down