mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-31 09:41:46 +00:00
c41b46bac3
* ci(linter): add renovate/logger-static-message rule Adds a custom oxlint rule that enforces a static msg component for logger.warn/error/fatal calls (including logger.once variants) so the messages can be grouped in metrics and error catching services, per docs/development/best-practices.md. It also flags metadata properties named 'error' that hold error-ish values, because the bunyan serializer only handles the 'err' key. The rule is enabled as an error for lib/ production code and disabled for spec files. All 33 pre-existing violations are fixed by moving interpolated values into the metadata object, making messages static, and renaming 'error' metadata keys to 'err'. Affected log-message assertions in co-located specs are updated accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: ignore validator output * fixup new merged in log messages --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
293 lines
8.5 KiB
TypeScript
293 lines
8.5 KiB
TypeScript
import { isArray } from '@sindresorhus/is';
|
|
import JSON5 from 'json5';
|
|
import { getEnvName } from '../../../../config/options/env.ts';
|
|
import { getOptions } from '../../../../config/options/index.ts';
|
|
import type { AllConfig } from '../../../../config/types.ts';
|
|
import { logger } from '../../../../logger/index.ts';
|
|
import { parseJson } from '../../../../util/common.ts';
|
|
import { coersions } from './coersions.ts';
|
|
import { migrateAndValidateConfig } from './util.ts';
|
|
|
|
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;
|
|
}
|
|
|
|
const renameKeys = {
|
|
aliases: 'registryAliases',
|
|
azureAutoComplete: 'platformAutomerge', // migrate: azureAutoComplete
|
|
gitLabAutomerge: 'platformAutomerge', // migrate: gitLabAutomerge
|
|
mergeConfidenceApiBaseUrl: 'mergeConfidenceEndpoint',
|
|
mergeConfidenceSupportedDatasources: 'mergeConfidenceDatasources',
|
|
allowedPostUpgradeCommands: 'allowedCommands',
|
|
baseBranches: 'baseBranchPatterns',
|
|
};
|
|
|
|
function renameEnvKeys(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
const result = { ...env };
|
|
for (const [from, to] of Object.entries(renameKeys)) {
|
|
const fromKey = getEnvName({ name: from });
|
|
const toKey = getEnvName({ name: to });
|
|
if (env[fromKey]) {
|
|
result[toKey] = env[fromKey];
|
|
delete result[fromKey];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const migratedKeysWithValues = [
|
|
{
|
|
oldName: 'recreateClosed',
|
|
newName: 'recreateWhen',
|
|
from: 'true',
|
|
to: 'always',
|
|
},
|
|
{
|
|
oldName: 'recreateClosed',
|
|
newName: 'recreateWhen',
|
|
from: 'false',
|
|
to: 'auto',
|
|
},
|
|
];
|
|
|
|
function massageEnvKeyValues(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
const result = { ...env };
|
|
for (const { oldName, newName, from, to } of migratedKeysWithValues) {
|
|
const key = getEnvName({ name: oldName });
|
|
if (env[key] !== undefined && result[key] === from) {
|
|
delete result[key];
|
|
result[getEnvName({ name: newName })] = to;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
interface ConvertedExperimentalEnvVar {
|
|
optionName: string;
|
|
// Normalize the raw env var value before passing it through, if needed.
|
|
normalizeValue?: (value: string) => string;
|
|
}
|
|
|
|
// Maps RENOVATE_X_ env vars that have been promoted to regular config options
|
|
// to the option name they now correspond to.
|
|
export const convertedExperimentalEnvVars: ReadonlyMap<
|
|
string,
|
|
ConvertedExperimentalEnvVar
|
|
> = new Map([
|
|
['RENOVATE_X_AUTODISCOVER_REPO_SORT', { optionName: 'autodiscoverRepoSort' }],
|
|
[
|
|
'RENOVATE_X_AUTODISCOVER_REPO_ORDER',
|
|
{ optionName: 'autodiscoverRepoOrder' },
|
|
],
|
|
['RENOVATE_X_DOCKER_MAX_PAGES', { optionName: 'dockerMaxPages' }],
|
|
['RENOVATE_X_DELETE_CONFIG_FILE', { optionName: 'deleteConfigFile' }],
|
|
['RENOVATE_X_S3_ENDPOINT', { optionName: 's3Endpoint' }],
|
|
['RENOVATE_X_S3_PATH_STYLE', { optionName: 's3PathStyle' }],
|
|
[
|
|
'RENOVATE_X_MERGE_CONFIDENCE_API_BASE_URL',
|
|
{ optionName: 'mergeConfidenceEndpoint' },
|
|
],
|
|
[
|
|
'RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES',
|
|
{ optionName: 'mergeConfidenceDatasources' },
|
|
],
|
|
[
|
|
'RENOVATE_X_REPO_CACHE_FORCE_LOCAL',
|
|
{
|
|
optionName: 'repositoryCacheForceLocal',
|
|
// The old env var was treated as a flag: any non-empty value meant true.
|
|
normalizeValue: (v: string) => (v ? 'true' : v),
|
|
},
|
|
],
|
|
]);
|
|
|
|
/**
|
|
* Massages the experimental env vars which have been converted to config options
|
|
*
|
|
* e.g. RENOVATE_X_AUTODISCOVER_REPO_SORT -> RENOVATE_AUTODISCOVER_REPO_SORT
|
|
*/
|
|
function massageConvertedExperimentalVars(
|
|
env: NodeJS.ProcessEnv,
|
|
): NodeJS.ProcessEnv {
|
|
const result = { ...env };
|
|
for (const [
|
|
oldKey,
|
|
{ optionName, normalizeValue },
|
|
] of convertedExperimentalEnvVars) {
|
|
if (env[oldKey] !== undefined) {
|
|
const newKey = getEnvName({ name: optionName });
|
|
result[newKey] = normalizeValue
|
|
? normalizeValue(env[oldKey])
|
|
: env[oldKey];
|
|
delete result[oldKey];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export async function getConfig(
|
|
inputEnv: NodeJS.ProcessEnv,
|
|
configEnvKey = 'RENOVATE_CONFIG',
|
|
): Promise<AllConfig> {
|
|
const env = prepareEnv(inputEnv);
|
|
const config = await parseAndValidateOrExit(env, configEnvKey);
|
|
|
|
const options = getOptions();
|
|
config.hostRules ??= [];
|
|
|
|
for (const option of options) {
|
|
if (option.env === false) {
|
|
continue;
|
|
}
|
|
|
|
const envName = getEnvName(option);
|
|
const envVal = env[envName];
|
|
if (!envVal) {
|
|
continue;
|
|
}
|
|
|
|
if (option.type === 'array' && option.subType === 'object') {
|
|
try {
|
|
const parsed = JSON5.parse(envVal);
|
|
if (isArray(parsed)) {
|
|
// @ts-expect-error -- type can't be narrowed
|
|
config[option.name] = parsed;
|
|
} else {
|
|
logger.debug(
|
|
{ val: envVal, envName },
|
|
'Could not parse object array',
|
|
);
|
|
}
|
|
} catch {
|
|
logger.debug(
|
|
{ val: envVal, envName },
|
|
'Could not parse environment variable',
|
|
);
|
|
}
|
|
} else {
|
|
const coerce = coersions[option.type];
|
|
try {
|
|
// @ts-expect-error -- type can't be narrowed
|
|
config[option.name] = coerce(envVal);
|
|
} catch (e) {
|
|
throw new Error(`${envName} was invalid: ${e}`);
|
|
}
|
|
|
|
if (option.name === 'dryRun') {
|
|
if ((config[option.name] as string) === 'true') {
|
|
logger.warn('env config dryRun property has been changed to full');
|
|
config[option.name] = 'full';
|
|
} else if ((config[option.name] as string) === 'false') {
|
|
logger.warn('env config dryRun property has been changed to null');
|
|
delete config[option.name];
|
|
} else if ((config[option.name] as string) === 'null') {
|
|
delete config[option.name];
|
|
}
|
|
}
|
|
|
|
if (option.name === 'requireConfig') {
|
|
if ((config[option.name] as string) === 'true') {
|
|
logger.warn(
|
|
'env config requireConfig property has been changed to required',
|
|
);
|
|
config[option.name] = 'required';
|
|
} else if ((config[option.name] as string) === 'false') {
|
|
logger.warn(
|
|
'env config requireConfig property has been changed to optional',
|
|
);
|
|
config[option.name] = 'optional';
|
|
}
|
|
}
|
|
|
|
if (option.name === 'platformCommit') {
|
|
if ((config[option.name] as string) === 'true') {
|
|
logger.warn(
|
|
'env config platformCommit property has been changed to enabled',
|
|
);
|
|
config[option.name] = 'enabled';
|
|
} else if ((config[option.name] as string) === 'false') {
|
|
logger.warn(
|
|
'env config platformCommit property has been changed to disabled',
|
|
);
|
|
config[option.name] = 'disabled';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const githubComToken = env.GITHUB_COM_TOKEN ?? env.RENOVATE_GITHUB_COM_TOKEN;
|
|
if (githubComToken) {
|
|
logger.debug(`Converting GITHUB_COM_TOKEN into a global host rule`);
|
|
config.hostRules.push({
|
|
hostType: 'github',
|
|
matchHost: 'github.com',
|
|
token: githubComToken,
|
|
});
|
|
}
|
|
|
|
// These env vars are deprecated and deleted to make sure they're not used
|
|
const unsupportedEnv = [
|
|
'BITBUCKET_TOKEN',
|
|
'BITBUCKET_USERNAME',
|
|
'BITBUCKET_PASSWORD',
|
|
'GITHUB_ENDPOINT',
|
|
'GITHUB_TOKEN',
|
|
'GITLAB_ENDPOINT',
|
|
'GITLAB_TOKEN',
|
|
'VSTS_ENDPOINT',
|
|
'VSTS_TOKEN',
|
|
];
|
|
|
|
for (const val of unsupportedEnv) {
|
|
delete env[val];
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
export function prepareEnv(inputEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
let env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX);
|
|
env = massageConvertedExperimentalVars(env);
|
|
env = renameEnvKeys(env);
|
|
// massage the values of migrated configuration keys
|
|
return massageEnvKeyValues(env);
|
|
}
|
|
|
|
export async function parseAndValidateOrExit(
|
|
env: NodeJS.ProcessEnv,
|
|
configEnvKey: string,
|
|
): Promise<AllConfig> {
|
|
if (!env[configEnvKey]) {
|
|
return {};
|
|
}
|
|
|
|
try {
|
|
const config = parseJson(
|
|
env[configEnvKey],
|
|
`${configEnvKey}.env.json5`,
|
|
) as AllConfig;
|
|
logger.debug({ config }, `Detected config in env ${configEnvKey}`);
|
|
|
|
return await migrateAndValidateConfig(config, `${configEnvKey}`);
|
|
} catch (err) {
|
|
logger.fatal(
|
|
{ err, configEnvKey },
|
|
'Could not parse config environment variable',
|
|
);
|
|
process.exit(1);
|
|
}
|
|
}
|