UNPKG

renovate

Version:

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

180 lines (179 loc) • 7.1 kB
import { cleanDatasourceKeys } from "../../../util/cache/memory/index.js"; import { logger } from "../../../logger/index.js"; import { isNotNullOrUndefined } from "../../../util/array.js"; import { instrument } from "../../../instrumentation/index.js"; import { getCache } from "../../../util/cache/repository/index.js"; import { checkGithubToken } from "../../../util/check-token.js"; import { scm } from "../../../modules/platform/scm.js"; import { hashMap } from "../../../modules/manager/fingerprint.generated.js"; import "../../../modules/manager/index.js"; import { fingerprint } from "../../../util/fingerprint.js"; import { Vulnerabilities } from "./vulnerabilities.js"; import { generateFingerprintConfig } from "../extract/extract-fingerprint-config.js"; import { extractAllDependencies } from "../extract/index.js"; import { branchifyUpgrades } from "../updates/branchify.js"; import { fetchUpdates } from "./fetch.js"; import { calculateLibYears } from "./libyear.js"; import { sortBranches } from "./sort.js"; import { writeUpdates } from "./write.js"; import { isNonEmptyArray } from "@sindresorhus/is"; // istanbul ignore next function extractStats(packageFiles) { if (!packageFiles) return null; const stats = { managers: {}, total: { fileCount: 0, depCount: 0 } }; for (const [manager, managerPackageFiles] of Object.entries(packageFiles)) { const fileCount = managerPackageFiles.length; let depCount = 0; for (const file of managerPackageFiles) depCount += file.deps.length; stats.managers[manager] = { fileCount, depCount }; stats.total.fileCount += fileCount; stats.total.depCount += depCount; } return stats; } function isCacheExtractValid(baseBranchSha, configHash, cachedExtract) { if (!cachedExtract) return false; if (!cachedExtract.revision) { logger.debug("Cached extract is missing revision, so cannot be used"); return false; } if (cachedExtract.revision !== 1) { logger.debug(`Extract cache revision has changed (old=${cachedExtract.revision}, new=1)`); return false; } if (!(cachedExtract.sha && cachedExtract.configHash)) return false; if (cachedExtract.sha !== baseBranchSha) { logger.debug(`Cached extract result cannot be used due to base branch SHA change (old=${cachedExtract.sha}, new=${baseBranchSha})`); return false; } if (cachedExtract.configHash !== configHash) { logger.debug("Cached extract result cannot be used due to config change"); return false; } if (!cachedExtract.extractionFingerprints) { logger.debug("Cached extract is missing extractionFingerprints, so cannot be used"); return false; } const changedManagers = /* @__PURE__ */ new Set(); for (const [manager, fingerprint] of Object.entries(cachedExtract.extractionFingerprints)) if (fingerprint !== hashMap.get(manager)) changedManagers.add(manager); if (changedManagers.size > 0) { logger.debug({ changedManagers: [...changedManagers] }, "Manager fingerprint(s) have changed, extract cache cannot be reused"); return false; } logger.debug(`Cached extract for sha=${baseBranchSha} is valid and can be used`); return true; } async function extract(config, overwriteCache = true) { logger.debug("extract()"); const { baseBranch } = config; const baseBranchSha = await scm.getBranchCommit(baseBranch); let packageFiles; const cache = getCache(); cache.scan ??= {}; const cachedExtract = cache.scan[baseBranch]; const configHash = instrument("fingerprint", () => fingerprint(generateFingerprintConfig(config))); // istanbul ignore if if (overwriteCache && isCacheExtractValid(baseBranchSha, configHash, cachedExtract)) { packageFiles = cachedExtract.packageFiles; try { for (const files of Object.values(packageFiles)) for (const file of files) for (const dep of file.deps) delete dep.updates; logger.debug("Deleted cached dep updates"); } catch (err) { logger.info({ err }, "Error deleting cached dep updates"); } } else { await instrument("checkoutBranch", async () => await scm.checkoutBranch(baseBranch)); const extractResult = await instrument("extractAllDependencies", async () => await extractAllDependencies(config) || {}); packageFiles = extractResult.packageFiles; const { extractionFingerprints } = extractResult; if (overwriteCache) cache.scan[baseBranch] = { revision: 1, sha: baseBranchSha, configHash, extractionFingerprints, packageFiles }; const baseBranches = isNonEmptyArray(config.baseBranches) ? config.baseBranches : [baseBranch]; Object.keys(cache.scan).forEach((branchName) => { if (!baseBranches.includes(branchName)) delete cache.scan[branchName]; }); } const stats = extractStats(packageFiles); logger.info({ baseBranch: config.baseBranch, stats }, `Dependency extraction complete`); logger.trace({ config: packageFiles }, "packageFiles"); checkGithubToken(packageFiles); return packageFiles; } async function fetchVulnerabilities(config, packageFiles) { if (config.osvVulnerabilityAlerts) { logger.debug("fetchVulnerabilities() - osvVulnerabilityAlerts=true"); try { await (await Vulnerabilities.create()).appendVulnerabilityPackageRules(config, packageFiles); } catch (err) { logger.warn({ err }, "Unable to read vulnerability information"); } } } async function lookup(config, packageFiles) { await fetchVulnerabilities(config, packageFiles); await fetchUpdates(config, packageFiles); await fetchVulnerabilities(config, packageFiles); cleanDatasourceKeys(); calculateLibYears(config, packageFiles); const { branches, branchList } = await branchifyUpgrades(config, packageFiles); reportMaliciousSkippedDependencies(packageFiles); logger.debug({ baseBranch: config.baseBranch, config: packageFiles }, "packageFiles with updates"); sortBranches(branches); return { branches, branchList, packageFiles }; } function reportMaliciousSkippedDependencies(allPackageFiles) { if (allPackageFiles === void 0) return; for (const [manager, packageFiles] of Object.entries(allPackageFiles)) for (const packageFile of packageFiles) for (const dep of packageFile.deps) if (dep.skipReason === "malicious-version-in-use") { logger.warn({ packageFile: packageFile.packageFile, depName: dep.depName, packageName: dep.packageName, manager, datasource: dep.datasource }, `Dependency ${dep.depName} is currently using a malicious version`); delete dep.skipReason; delete dep.skipStage; } else if (dep.skipReason === "malicious-update-proposed") { const newVersions = dep.updates?.map((u) => u.newVersion ?? u.newValue).filter(isNotNullOrUndefined); logger.warn({ packageFile: packageFile.packageFile, depName: dep.depName, packageName: dep.packageName, manager, datasource: dep.datasource, newVersions }, `Dependency ${dep.depName} has update(s) proposed which would update you to a malicious version - skipping`); } } async function update(config, branches) { let res; if (config.repoIsOnboarded) res = await writeUpdates(config, branches); return res; } //#endregion export { extract, lookup, update }; //# sourceMappingURL=extract-update.js.map