UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

230 lines (229 loc) • 7.9 kB
import { newlineRegex, regEx } from "../../../util/regex.js"; import { logger } from "../../../logger/index.js"; import { find } from "../../../util/host-rules.js"; import { detectPlatform } from "../../../util/common.js"; import { parseSingleYaml } from "../../../util/yaml.js"; import { GithubTagsDatasource } from "../../datasource/github-tags/index.js"; import { GitlabTagsDatasource } from "../../datasource/gitlab-tags/index.js"; import { extractDependency as extractDependency$1 } from "../npm/extract/common/dependency.js"; import { parseLine } from "../gomod/line-parser.js"; import { pep508ToPackageDependency } from "../pep621/utils.js"; import { matchesPrecommitConfigHeuristic, matchesPrecommitDependencyHeuristic } from "./parsing.js"; import { isEmptyObject, isNonEmptyObject, isPlainObject } from "@sindresorhus/is"; //#region lib/modules/manager/pre-commit/extract.ts /** * Determines the datasource(id) to be used for this dependency * @param repository the full git url, ie git@github.com/user/project. * Used in debug statements to clearly indicate the related dependency. * @param hostname the hostname (ie github.com) * Used to determine which renovate datasource should be used. * Is matched literally against `github.com` and `gitlab.com`. * If that doesn't match, `hostRules.find()` is used to find related sources. * In that case, the hostname is passed on as registryUrl to the corresponding datasource. */ function determineDatasource(repository, hostname) { if (hostname === "github.com" || detectPlatform(repository) === "github") { logger.debug({ repository, hostname }, "Found github dependency"); return { datasource: GithubTagsDatasource.id }; } if (hostname === "gitlab.com") { logger.debug({ repository, hostname }, "Found gitlab dependency"); return { datasource: GitlabTagsDatasource.id }; } if (detectPlatform(repository) === "gitlab") { logger.debug({ repository, hostname }, "Found gitlab dependency with custom registryUrl"); return { datasource: GitlabTagsDatasource.id, registryUrls: [`https://${hostname}`] }; } const hostUrl = `https://${hostname}`; if (isEmptyObject(find({ url: hostUrl }))) { logger.debug({ repository, hostUrl }, "Provided hostname does not match any hostRules. Ignoring"); return { skipReason: "unknown-registry", registryUrls: [hostname] }; } for (const [hostType, sourceId] of [["github", GithubTagsDatasource.id], ["gitlab", GitlabTagsDatasource.id]]) if (isNonEmptyObject(find({ hostType, url: hostUrl }))) { logger.debug({ repository, hostUrl, hostType }, `Provided hostname matches a ${hostType} hostrule.`); return { datasource: sourceId, registryUrls: [hostname] }; } logger.debug({ repository, registry: hostUrl }, "Provided hostname did not match any of the hostRules of hostType github nor gitlab"); return { skipReason: "unknown-registry", registryUrls: [hostname] }; } const gitUrlRegex = regEx(/\.git$/i); const revLineWithFrozenCommentRegex = regEx(/^\s*rev:\s*(?<replaceString>(?<currentDigest>[a-f0-9]{40})(?<commentWhiteSpaces>\s+)#\s*frozen:\s*(?<currentValue>\S+))/); function extractWithRegex(content) { logger.trace("pre-commit.extractWithRegex()"); const regexDeps = /* @__PURE__ */ new Map(); for (const line of content.split(newlineRegex)) { if (line.trim().startsWith("#")) continue; const match = revLineWithFrozenCommentRegex.exec(line); if (match?.groups) { const { currentDigest, currentValue, replaceString, commentWhiteSpaces } = match.groups; regexDeps.set(currentDigest, { currentDigest, currentValue, replaceString, autoReplaceStringTemplate: `{{newDigest}}${commentWhiteSpaces}# frozen: {{newValue}}` }); } } return regexDeps; } function extractDependency(tag, repository) { logger.debug(`Found version ${tag}`); const urlMatchers = [ regEx("^https?://(?<hostname>[^/]+)/(?<depName>\\S*)"), regEx("^git@(?<hostname>[^:]+):(?<depName>\\S*)"), regEx(/^git:\/\/(?<hostname>[^/]+)\/(?<depName>\S*)/), regEx(/^ssh:\/\/git@(?<hostname>[^/]+)\/(?<depName>\S*)/) ]; for (const urlMatcher of urlMatchers) { const match = urlMatcher.exec(repository); if (match?.groups) { const hostname = match.groups.hostname; const depName = match.groups.depName.replace(gitUrlRegex, ""); return { ...determineDatasource(repository, hostname), depName, depType: "repository", packageName: depName, currentValue: tag }; } } logger.info({ repository }, "Could not separate hostname from full dependency url."); return { depName: void 0, depType: "repository", datasource: void 0, packageName: void 0, skipReason: "invalid-url", currentValue: tag }; } /** * Find all supported dependencies in the pre-commit yaml object. * * @param precommitFile the parsed yaml config file * @param regexDeps Map of regex-extracted deps keyed by digest for enrichment */ function findDependencies(precommitFile, regexDeps) { if (!precommitFile.repos) { logger.debug(`No repos section found, skipping file`); return []; } const packageDependencies = []; for (const item of precommitFile.repos) { if (item.repo !== "meta") { for (const hook of item.hooks ?? []) if (hook.language === "node") hook.additional_dependencies?.map((req) => { const match = regEx("^(?<name>.+)@(?<range>.+)$").exec(req); if (!match?.groups) return; const depType = "pre-commit-node"; const dep = extractDependency$1(depType, match.groups.name, match.groups.range); packageDependencies.push({ depType, depName: match.groups.name, packageName: match.groups.name, ...dep }); }); else if (hook.language === "python") hook.additional_dependencies?.map((req) => { const dep = pep508ToPackageDependency("pre-commit-python", req); if (dep) packageDependencies.push(dep); }); else if (hook.language === "golang") hook.additional_dependencies?.map((req) => { const dep = parseLine(`require ${req.replace("@", " ")}`); if (dep) { const depType = "pre-commit-golang"; packageDependencies.push({ ...dep, depType }); } }); } if (matchesPrecommitDependencyHeuristic(item)) { logger.trace(item, "Matched pre-commit dependency spec"); const repository = String(item.repo); const tag = String(item.rev); const dep = extractDependency(tag, repository); const regexDep = regexDeps.get(tag); if (regexDep) { dep.currentDigest = regexDep.currentDigest; dep.currentValue = regexDep.currentValue; dep.replaceString = regexDep.replaceString; dep.autoReplaceStringTemplate = regexDep.autoReplaceStringTemplate; } packageDependencies.push(dep); } else logger.trace(item, "Did not find pre-commit repo spec"); } return packageDependencies; } function extractPackageFile(content, packageFile) { let parsedContent; try { parsedContent = parseSingleYaml(content); } catch (err) { logger.debug({ filename: packageFile, err }, "Failed to parse pre-commit config YAML"); return null; } if (!isPlainObject(parsedContent)) { logger.debug({ packageFile }, `Parsing of pre-commit config YAML returned invalid result`); return null; } if (!matchesPrecommitConfigHeuristic(parsedContent)) { logger.debug({ packageFile }, `File does not look like a pre-commit config file`); return null; } try { const regexDeps = extractWithRegex(content); const deps = findDependencies(parsedContent, regexDeps); if (deps.length) { logger.trace({ deps }, "Found dependencies in pre-commit config"); return { deps }; } } catch (err) /* istanbul ignore next */ { logger.debug({ packageFile, err }, "Error scanning parsed pre-commit config"); } return null; } //#endregion export { extractPackageFile }; //# sourceMappingURL=extract.js.map