renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
207 lines (206 loc) • 9.39 kB
JavaScript
import { CONFIG_VALIDATION } from "../../constants/error-messages.js";
import { get, set } from "../../util/cache/memory/index.js";
import { regEx } from "../../util/regex.js";
import { GlobalConfig } from "../global.js";
import { logger } from "../../logger/index.js";
import { clone } from "../../util/clone.js";
import { massageConfig } from "../massage.js";
import { removedPresets } from "./common.js";
import { mergeChildConfig } from "../utils.js";
import { migrateConfig } from "../migration.js";
import { compile } from "../../util/template/index.js";
import { ExternalHostError } from "../../types/errors/external-host-error.js";
import { getPreset as getPreset$1, isInternal } from "./internal/index.js";
import { PRESET_DEP_NOT_FOUND } from "./util.js";
import { parsePreset } from "./parse.js";
import { isArray, isNullOrUndefined, isObject, isString } from "@sindresorhus/is";
//#region lib/config/presets/index.ts
const presetSourceLoaders = {
forgejo: () => import("./forgejo/index.js"),
gitea: () => import("./gitea/index.js"),
github: () => import("./github/index.js"),
gitlab: () => import("./gitlab/index.js"),
http: () => import("./http/index.js"),
local: () => import("./local/index.js"),
npm: () => import("./npm/index.js")
};
const presetCacheNamespace = "preset";
function replaceArgs(obj, argMapping) {
if (isString(obj)) {
let returnStr = obj;
for (const [arg, argVal] of Object.entries(argMapping)) {
const re = regEx(`{{${arg}}}`, "g", false);
returnStr = returnStr.replace(re, argVal);
}
return returnStr;
}
if (isArray(obj)) {
const returnArray = [];
for (const item of obj) returnArray.push(replaceArgs(item, argMapping));
return returnArray;
}
if (isObject(obj)) {
const returnObj = {};
for (const [key, val] of Object.entries(obj)) returnObj[key] = replaceArgs(val, argMapping);
return returnObj;
}
return obj;
}
async function getPreset(preset, baseConfig) {
logger.trace(`getPreset(${preset})`);
const newPreset = removedPresets[preset];
if (newPreset) return getPreset(newPreset, baseConfig);
if (newPreset === null) return {};
const { presetSource, repo, presetPath, presetName, tag, params, rawParams } = parsePreset(preset);
let presetConfig;
if (presetSource === "internal") presetConfig = getPreset$1({
repo,
presetPath,
presetName,
tag
});
else {
const cacheKey = `preset:${preset}`;
const packageCache = GlobalConfig.get("presetCachePersistence") ? await import("../../util/cache/package/index.js") : void 0;
if (packageCache) presetConfig = await packageCache.get(presetCacheNamespace, cacheKey);
else presetConfig = get(cacheKey);
if (isNullOrUndefined(presetConfig)) {
presetConfig = await (await presetSourceLoaders[presetSource]()).getPreset({
repo,
presetPath,
presetName,
tag
});
if (packageCache) await packageCache.set(presetCacheNamespace, cacheKey, presetConfig, 15);
else set(cacheKey, presetConfig);
}
}
if (!presetConfig) throw new Error(PRESET_DEP_NOT_FOUND);
logger.trace({ presetConfig }, `Found preset ${preset}`);
if (params) {
const argMapping = {};
for (const [index, value] of params.entries()) argMapping[`arg${index}`] = value;
if (rawParams) argMapping.args = rawParams;
presetConfig = replaceArgs(presetConfig, argMapping);
}
logger.trace({ presetConfig }, `Applied params to preset ${preset}`);
const presetKeys = Object.keys(presetConfig);
if (presetKeys.length === 2 && presetKeys.includes("description") && presetKeys.includes("extends")) delete presetConfig.description;
const packageListKeys = ["description", "matchPackageNames"];
if (presetKeys.every((key) => packageListKeys.includes(key))) delete presetConfig.description;
const { migratedConfig } = migrateConfig(presetConfig);
return massageConfig(migratedConfig);
}
/**
* @param [mergeInternalPresets=true] when resolving the config presets, whether to merge Renovate internal presets into the resulting configuration.
* When set to `false`, this will resolve these internal presets (recursively), but not merge them.
* This is primarily intended to be used by "shallow config" resolution (for logging purposes).
*/
async function resolveConfigPresets(inputConfig, baseConfig, _ignorePresets, existingPresets = [], mergeInternalPresets = true) {
const allVisitedPresets = {
merged: /* @__PURE__ */ new Set(),
unmerged: /* @__PURE__ */ new Set()
};
let ignorePresets = clone(_ignorePresets);
if (!ignorePresets || ignorePresets.length === 0) ignorePresets = inputConfig.ignorePresets ?? [];
logger.trace({
config: inputConfig,
existingPresets,
mergeInternalPresets
}, "resolveConfigPresets");
let config = {};
if (inputConfig.extends?.length) {
inputConfig.extends = inputConfig.extends.map((tmpl) => compile(tmpl, {}));
for (const preset of inputConfig.extends) {
if (!mergeInternalPresets && isInternal(preset)) {
logger.once.trace({
ignoredPreset: preset,
mergeInternalPresets
}, "Not merging preset");
allVisitedPresets.unmerged.add(preset);
continue;
}
if (shouldResolvePreset(preset, existingPresets, ignorePresets)) {
logger.trace(`Resolving preset "${preset}"`);
const { config: presetConfig, visitedPresets } = await resolveConfigPresets(await fetchPreset(preset, baseConfig, inputConfig, existingPresets), baseConfig ?? inputConfig, ignorePresets, existingPresets.concat([preset]), mergeInternalPresets);
if (inputConfig?.ignoreDeps?.length === 0) delete presetConfig.description;
config = mergeChildConfig(config, presetConfig);
allVisitedPresets.merged.add(preset);
for (const mergedPreset of visitedPresets.merged) allVisitedPresets.merged.add(mergedPreset);
for (const unmergedPreset of visitedPresets.unmerged) allVisitedPresets.unmerged.add(unmergedPreset);
}
}
}
logger.trace({ config }, `Post-preset resolve config`);
config = mergeChildConfig(config, inputConfig);
delete config.extends;
delete config.ignorePresets;
logger.trace({ config }, `Post-merge resolve config`);
for (const [key, val] of Object.entries(config)) {
const ignoredKeys = ["content", "onboardingConfig"];
if (isArray(val)) {
config[key] = [];
for (const element of val) if (isObject(element)) {
const { config: presetConfig, visitedPresets: visited } = await resolveConfigPresets(element, baseConfig, ignorePresets, existingPresets, mergeInternalPresets);
config[key].push(presetConfig);
for (const mergedPreset of visited.merged) allVisitedPresets.merged.add(mergedPreset);
for (const unmergedPreset of visited.unmerged) allVisitedPresets.unmerged.add(unmergedPreset);
} else config[key].push(element);
} else if (isObject(val) && !ignoredKeys.includes(key)) {
logger.trace(`Resolving object "${key}"`);
const { config: presetConfig, visitedPresets: visited } = await resolveConfigPresets(val, baseConfig, ignorePresets, existingPresets, mergeInternalPresets);
config[key] = presetConfig;
for (const mergedPreset of visited.merged) allVisitedPresets.merged.add(mergedPreset);
for (const unmergedPreset of visited.unmerged) allVisitedPresets.unmerged.add(unmergedPreset);
}
}
logger.trace({ config: inputConfig }, "Input config");
logger.trace({
config,
visitedPresets: allVisitedPresets
}, "Resolved config");
return {
config,
visitedPresets: {
merged: Array.from(allVisitedPresets.merged),
unmerged: Array.from(allVisitedPresets.unmerged)
}
};
}
async function fetchPreset(preset, baseConfig, inputConfig, existingPresets) {
try {
return await getPreset(preset, baseConfig ?? inputConfig);
} catch (err) {
logger.debug({
preset,
err
}, "Preset fetch error");
if (err instanceof ExternalHostError) throw err;
if (err.message === "rate-limit-exceeded") throw err;
const error = new Error(CONFIG_VALIDATION);
if (err.message === "dep not found") error.validationError = `Cannot find preset's package (${preset})`;
else if (err.message === "preset renovate-config not found") error.validationError = `Preset package is missing a renovate-config entry (${preset})`;
else if (err.message === "preset not found") error.validationError = `Preset name not found within published preset config (${preset})`;
else if (err.message === "invalid preset") error.validationError = `Preset is invalid (${preset})`;
else if (err.message === "prohibited sub-preset") error.validationError = `Sub-presets cannot be combined with a custom path (${preset})`;
else if (err.message === "invalid preset JSON") error.validationError = `Preset is invalid JSON (${preset})`;
else error.validationError = `Preset caused unexpected error (${preset})`;
if (existingPresets.length) error.validationError += ". Note: this is a *nested* preset so please contact the preset author if you are unable to fix it yourself.";
logger.info({ validationError: error.validationError }, "Throwing preset error");
throw error;
}
}
function shouldResolvePreset(preset, existingPresets, ignorePresets) {
if (existingPresets.includes(preset)) {
logger.debug(`Already seen preset ${preset} in [${existingPresets.join(", ")}]`);
return false;
}
if (ignorePresets.includes(preset)) {
logger.debug(`Ignoring preset ${preset} in [${existingPresets.join(", ")}]`);
return false;
}
return true;
}
//#endregion
export { resolveConfigPresets };
//# sourceMappingURL=index.js.map