refactor(instrumentation): instrument mergeChildConfig (#46601)

As part of investigating a `filter packageFiles for managers` span
taking ~2.5m on a very large repository, we need to know how much of
that is spent merging manager config, as `getManagerConfig` deep-clones
the whole repository config once per manager.
This commit is contained in:
Jamie Tanna
2026-09-30 15:06:15 +00:00
committed by GitHub
parent d2dc1b6a60
commit e5b35200d8
+46 -43
View File
@@ -1,3 +1,4 @@
import { instrument } from '../instrumentation/index.ts';
import { logger } from '../logger/index.ts';
import { clone } from '../util/clone.ts';
import { getHighestVulnerabilitySeverity } from '../util/vulnerability/utils.ts';
@@ -8,50 +9,52 @@ export function mergeChildConfig<
T extends Record<string, any>,
TChild extends Record<string, any> | undefined,
>(parent: T, child: TChild): T & TChild {
logger.trace({ parent, child }, `mergeChildConfig`);
if (!child) {
return parent as never;
}
const parentConfig = clone(parent);
const childConfig = clone(child);
const config: Record<string, any> = { ...parentConfig, ...childConfig };
return instrument('mergeChildConfig', () => {
logger.trace({ parent, child }, `mergeChildConfig`);
if (!child) {
return parent as never;
}
const parentConfig = clone(parent);
const childConfig = clone(child);
const config: Record<string, any> = { ...parentConfig, ...childConfig };
// Ensure highest severity survives parent / child merge
if (config?.isVulnerabilityAlert) {
config.vulnerabilitySeverity = getHighestVulnerabilitySeverity(
parent,
child,
);
}
for (const option of options.getOptions()) {
if (
option.mergeable &&
childConfig[option.name] &&
parentConfig[option.name]
) {
logger.trace(`mergeable option: ${option.name}`);
if (option.name === 'constraints') {
config[option.name] = {
...parentConfig[option.name],
...childConfig[option.name],
};
} else if (option.type === 'array') {
config[option.name] = (parentConfig[option.name] as unknown[]).concat(
config[option.name],
);
} else {
config[option.name] = mergeChildConfig(
parentConfig[option.name] as RenovateConfig,
childConfig[option.name] as RenovateConfig,
);
}
logger.trace(
{ result: config[option.name] },
`Merged config.${option.name}`,
// Ensure highest severity survives parent / child merge
if (config?.isVulnerabilityAlert) {
config.vulnerabilitySeverity = getHighestVulnerabilitySeverity(
parent,
child,
);
}
}
return { ...config, ...config.force };
for (const option of options.getOptions()) {
if (
option.mergeable &&
childConfig[option.name] &&
parentConfig[option.name]
) {
logger.trace(`mergeable option: ${option.name}`);
if (option.name === 'constraints') {
config[option.name] = {
...parentConfig[option.name],
...childConfig[option.name],
};
} else if (option.type === 'array') {
config[option.name] = (parentConfig[option.name] as unknown[]).concat(
config[option.name],
);
} else {
config[option.name] = mergeChildConfig(
parentConfig[option.name] as RenovateConfig,
childConfig[option.name] as RenovateConfig,
);
}
logger.trace(
{ result: config[option.name] },
`Merged config.${option.name}`,
);
}
}
return { ...config, ...config.force };
});
}