renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
250 lines (249 loc) • 11 kB
JavaScript
import { regEx } from "../regex.js";
import { GlobalConfig } from "../../config/global.js";
import { logger } from "../../logger/index.js";
import { toArray } from "../array.js";
import { getChildEnv } from "../exec/utils.js";
import { isArray, isNumber, isPlainObject, isPrimitive, isString } from "@sindresorhus/is";
import handlebars from "handlebars";
//#region lib/util/template/index.ts
const helpers = {
encodeURIComponent,
decodeURIComponent,
encodeBase64: (str) => Buffer.from(str ?? "").toString("base64"),
decodeBase64: (str) => Buffer.from(str ?? "", "base64").toString(),
stringToPrettyJSON: (input) => JSON.stringify(JSON.parse(input), null, 2),
toJSON: (input) => JSON.stringify(input),
toArray: (...args) => {
args.pop();
return args;
},
toObject: (...args) => {
args.pop();
if (args.length % 2 !== 0) throw new Error(`Must contain an even number of elements`);
const keys = args.filter((_, index) => index % 2 === 0);
const values = args.filter((_, index) => index % 2 === 1);
return Object.fromEntries(keys.map((key, index) => [key, values[index]]));
},
replace: (find, replace, context) => (context ?? "").replace(regEx(find, "g"), replace),
lowercase: (str) => str?.toLowerCase(),
containsString: (str, subStr) => str?.includes(subStr),
equals: (arg1, arg2) => arg1 === arg2,
includes: (arg1, arg2) => {
if (isArray(arg1, isString) && isString(arg2)) return arg1.includes(arg2);
return false;
},
split: (str, separator) => {
if (isString(str) && isString(separator)) return str.split(separator);
return [];
},
and: (...args) => {
args.pop();
return args.every(Boolean);
},
or: (...args) => {
args.pop();
return args.some(Boolean);
},
lookupArray: (obj, key, options) => {
return toArray(obj).filter((element) => !handlebars.Utils.isEmpty(element)).map((element) => options.lookupProperty(element, key)).filter((value) => value !== void 0);
},
distinct: (obj) => {
const seen = /* @__PURE__ */ new Set();
return toArray(obj).filter((value) => {
const str = JSON.stringify(value);
if (seen.has(str)) return false;
seen.add(str);
return true;
});
},
add: (a, b) => {
if (isNumber(a) && isNumber(b)) return a + b;
throw new Error("add: inputs are not valid");
}
};
for (const [name, fn] of Object.entries(helpers)) handlebars.registerHelper(name, fn);
/**
* Config options whose **values** are exposed as template variables.
* e.g. `groupName` is in this list because you can write `{{groupName}}` inside a template.
* This is distinct from `supportsTemplating`, which marks options whose values
* are themselves *compiled* as Handlebars templates via `template.compile()`.
*/
const exposedConfigOptions = [
"additionalBranchPrefix",
"addLabels",
"allowedVersions",
"branchName",
"branchPrefix",
"branchTopic",
"commitBody",
"commitMessage",
"commitMessageAction",
"commitMessageExtra",
"commitMessagePrefix",
"commitMessageSuffix",
"commitMessageTopic",
"gitAuthor",
"group",
"groupName",
"groupSlug",
"labels",
"prBodyColumns",
"prBodyDefinitions",
"prBodyNotes",
"prTitle",
"semanticCommitScope",
"semanticCommitType",
"separateMajorMinor",
"separateMinorPatch",
"separateMultipleMinor",
"sourceDirectory"
];
/**
* The complete set of field names that may be referenced inside a Handlebars template.
* Union of runtime fields (`allowedFields`) and config options (`exposedConfigOptions`).
* Used by the compile proxy to warn on unknown variable references.
*/
const allowedTemplateFields = new Set([...Object.keys({
baseBranch: "The baseBranch for this branch/PR",
body: "The body of the release notes",
categories: "The categories of the manager of the dependency being updated",
currentValue: "The extracted current value of the dependency being updated",
currentVersion: "The version that would be currently installed. For example, if currentValue is ^3.0.0 then currentVersion might be 3.1.0.",
currentVersionAgeInDays: "The age of the current version in days",
currentVersionTimestamp: "The timestamp of the current version",
currentDigest: "The extracted current digest of the dependency being updated",
currentDigestShort: "The extracted current short digest of the dependency being updated",
datasource: "The datasource used to look up the upgrade",
depName: "The name of the dependency being updated",
depNameLinked: "The dependency name already linked to its home page using markdown",
depNameSanitized: "The depName field sanitized for use in branches after removing spaces and special characters",
depType: "The dependency type (if extracted - manager-dependent)",
depTypes: "A deduplicated array of dependency types (if extracted - manager-dependent) in a branch",
displayFrom: "The current value, formatted for display",
displayPending: "Latest pending update, if internalChecksFilter is in use",
displayTo: "The to value, formatted for display",
hasReleaseNotes: "true if the upgrade has release notes",
indentation: "The indentation of the dependency being updated",
isGroup: "true if the upgrade is part of a group",
isLockfileUpdate: "true if the branch is a lock file update",
isMajor: "true if the upgrade is major",
isMinor: "true if the upgrade is minor",
isPatch: "true if the upgrade is a patch upgrade",
isPin: "true if the upgrade is pinning dependencies",
isPinDigest: "true if the upgrade is pinning digests",
isRollback: "true if the upgrade is a rollback PR",
isReplacement: "true if the upgrade is a replacement",
isRange: "true if the new value is a range",
isSingleVersion: "true if the upgrade is to a single version rather than a range",
isVulnerabilityAlert: "true if the upgrade is a vulnerability alert",
logJSON: "ChangeLogResult object for the upgrade",
manager: "The (package) manager which detected the dependency",
newDigest: "The new digest value",
newDigestShort: "A shortened version of newDigest, for use when the full digest is too long to be conveniently displayed",
newMajor: "The major version of the new version. e.g. \"3\" if the new version is \"3.1.0\"",
newMinor: "The minor version of the new version. e.g. \"1\" if the new version is \"3.1.0\"",
newPatch: "The patch version of the new version. e.g. \"0\" if the new version is \"3.1.0\"",
newName: "The name of the new dependency that replaces the current deprecated dependency",
newNameLinked: "The new dependency name already linked to its home page using markdown",
newValue: "The new value in the upgrade. Can be a range or version e.g. \"^3.0.0\" or \"3.1.0\"",
newVersion: "The new version in the upgrade, e.g. \"3.1.0\"",
newVersionAgeInDays: "The age of the new version in days",
major: "The major version of the current version. e.g. \"3\" if the current version is \"3.1.0\"",
minor: "The minor version of the current version. e.g. \"1\" if the current version is \"3.1.0\"",
patch: "The patch version of the current version. e.g. \"0\" if the current version is \"3.1.0\"",
packageFile: "The filename that the dependency was found in",
packageFileDir: "The directory with full path where the packageFile was found",
packageName: "The full name that was used to look up the dependency",
packageScope: "The scope of the package name. Supports Maven group ID only",
parentDir: "The name of the directory that the dependency was found in, without full path",
parentOrg: "The name of the parent organization for the current repository",
platform: "VCS platform in use, e.g. \"github\", \"gitlab\", etc.",
prettyDepType: "Massaged depType",
prettyNewMajor: "The new major value with v prepended to it.",
prettyNewVersion: "The new version value with v prepended to it.",
project: "ChangeLogProject object",
recreateClosed: "If true, this PR will be recreated if closed",
references: "A list of references for the upgrade",
releases: "An array of releases for an upgrade",
releaseNotes: "A ChangeLogNotes object for the release",
releaseTimestamp: "The timestamp of the release",
renovateVersion: "The currently running Renovate version. Only supported in the `user-agent` configuration option.",
repository: "The current repository",
semanticPrefix: "The fully generated semantic prefix for commit messages",
sourceRepo: "The repository in the sourceUrl, if present",
sourceRepoName: "The repository name in the sourceUrl, if present",
sourceRepoOrg: "The repository organization in the sourceUrl, if present",
sourceRepoSlug: "The slugified pathname of the sourceUrl, if present",
sourceUrl: "The source URL for the package",
topLevelOrg: "The name of the top-level organization for the current repository",
updateType: "One of digest, pin, rollback, patch, minor, major, replacement, pinDigest",
upgrades: "An array of upgrade objects in the branch",
url: "The url of the release notes",
version: "The version number of the changelog",
versioning: "The versioning scheme in use",
versions: "An array of ChangeLogRelease objects in the upgrade",
vulnerabilitySeverity: "The severity for a vulnerability alert upgrade (LOW, MEDIUM, MODERATE, HIGH, CRITICAL, UNKNOWN)"
}), ...exposedConfigOptions]);
var CompileInputProxyHandler = class {
warnVariables;
constructor(warnVariables) {
this.warnVariables = warnVariables;
}
get(target, prop) {
if (prop === "env") return target[prop];
if (!allowedTemplateFields.has(prop)) {
this.warnVariables.add(prop);
return;
}
const value = target[prop];
if (prop === "prBodyDefinitions") return value;
if (isArray(value)) return value.map((element) => isPrimitive(element) ? element : proxyCompileInput(element, this.warnVariables));
if (isPlainObject(value)) return proxyCompileInput(value, this.warnVariables);
return value;
}
};
function proxyCompileInput(input, warnVariables) {
return new Proxy(input, new CompileInputProxyHandler(warnVariables));
}
function compile(template, input, filterFields = true) {
const env = getChildEnv({});
const data = {
...GlobalConfig.get(),
...input,
env
};
const warnVariables = /* @__PURE__ */ new Set();
const filteredInput = filterFields ? proxyCompileInput(data, warnVariables) : data;
logger.trace({
template,
filteredInput
}, "Compiling template");
const result = handlebars.compile(template, { noEscape: true })(filteredInput);
if (warnVariables.size > 0) logger.info({
varNames: Array.from(warnVariables),
template
}, "Disallowed variable names in template");
return result;
}
function safeCompile(template, input, filterFields = true) {
try {
return compile(template, input, filterFields);
} catch (err) {
logger.warn({
err,
template
}, "Error compiling template");
return "";
}
}
/**
* Validate that the template is a valid Handlebars template string.
*
* NOTE: Does not validate that fields are set to valid field names.
*/
function validate(template) {
handlebars.parse(template);
}
//#endregion
export { compile, safeCompile, validate };
//# sourceMappingURL=index.js.map