UNPKG

renovate

Version:

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

175 lines (174 loc) • 7.34 kB
import { regEx } from "../../../../util/regex.js"; import { matchRegexOrGlobList } from "../../../../util/string-match.js"; import { logger } from "../../../../logger/index.js"; import { coerceArray } from "../../../../util/array.js"; import { compile } from "../../../../util/template/index.js"; import { readLocalFile } from "../../../../util/fs/index.js"; import { scm } from "../../../../modules/platform/scm.js"; import { getFilteredFileList } from "../../extract/file-match.js"; import { inc } from "semver"; //#region lib/workers/repository/update/branch/bump-versions.ts async function bumpVersions(config) { const bumpVersions = config.bumpVersions; if (!bumpVersions?.length) return; if (!config.updatedPackageFiles?.length && !config.updatedArtifacts?.length) return; const fileList = getFilteredFileList(config, await scm.getFileList()); const packageFileChanges = fileChangeListToMap(config.updatedPackageFiles); const artifactFileChanges = fileChangeListToMap(config.updatedArtifacts); for (const bumpVersionConfig of bumpVersions) await bumpVersion(bumpVersionConfig, config, fileList, packageFileChanges, artifactFileChanges); config.updatedPackageFiles = Object.values(packageFileChanges).flat(); config.updatedArtifacts = Object.values(artifactFileChanges).flat(); } async function bumpVersion(config, branchConfig, fileList, packageFiles, artifactFiles) { const rawBumpType = config.bumpType ?? "patch"; const bumpVersionsDescr = config.name ? `bumpVersions(${config.name})` : "bumpVersions"; const files = []; try { files.push(...getMatchedFiles(bumpVersionsDescr, config.filePatterns, branchConfig, fileList)); } catch (e) { addArtifactError(branchConfig, `Failed to calculate matched files for bumpVersions: ${e.message}`); return; } if (!files.length) { logger.debug(`${bumpVersionsDescr}: filePatterns did not match any files`); return; } logger.trace({ files }, `${bumpVersionsDescr}: Found ${files.length} files to bump versions`); const matchStrings = []; const matchStringsRegexes = []; for (const matchString of config.matchStrings) try { const templated = compile(matchString, branchConfig); matchStrings.push(templated); matchStringsRegexes.push(regEx(templated)); } catch (e) { addArtifactError(branchConfig, `Failed to compile matchString for ${bumpVersionsDescr}: ${e.message}`, matchString); } logger.trace({ matchStrings }, `${bumpVersionsDescr}: Compiled matchStrings`); for (const filePath of files) { let fileBumped = false; const fileContents = await getFileContent(bumpVersionsDescr, filePath, packageFiles, artifactFiles); if (!fileContents) continue; for (const matchStringRegex of matchStringsRegexes) { const regexResult = matchStringRegex.exec(fileContents); if (!regexResult) continue; const version = regexResult.groups?.version; if (!version) { logger.debug({ file: filePath }, `${bumpVersionsDescr}: No version found`); continue; } let newVersion = null; try { const bumpType = compile(rawBumpType, branchConfig); if (bumpType === "sync") if (branchConfig.upgrades?.length) { newVersion = branchConfig.upgrades[0].newVersion ?? null; if (!newVersion) { logger.debug({ file: filePath }, `${bumpVersionsDescr}: No newVersion found in branch upgrades for sync type`); continue; } } else { logger.debug({ file: filePath }, `${bumpVersionsDescr}: No upgrades found in branch config for sync type`); continue; } else { const parts = regEx("^(?<major>\\d+)(?:\\.(?<minor>\\d+))?$").exec(version); if (parts?.groups) { const { major, minor } = parts.groups; if (bumpType === "major") newVersion = `${parseInt(major, 10) + 1}${minor ? `.0` : ""}`; else if (bumpType === "minor") newVersion = `${major}.${parseInt(minor, 10) + 1}`; else throw new Error(`Unsupported bump type for {major}.{minor} version: ${bumpType}`); } else newVersion = inc(version, bumpType); } } catch (e) { addArtifactError(branchConfig, `Failed to calculate new version for ${bumpVersionsDescr}: ${e.message}`, filePath); } if (!newVersion) { logger.debug({ file: filePath }, `${bumpVersionsDescr}: Could not bump version`); continue; } const newFileContents = fileContents.toString().replace(matchStringRegex, (match, ...groups) => { const { version } = groups.pop(); return match.replace(version, newVersion); }); if (packageFiles[filePath]) packageFiles[filePath].push({ type: "addition", path: filePath, contents: newFileContents }); else { artifactFiles[filePath] ??= []; artifactFiles[filePath].push({ type: "addition", path: filePath, contents: newFileContents }); } fileBumped = true; } if (!fileBumped) logger.debug({ file: filePath }, `${bumpVersionsDescr}: No match found for bumping version`); } } /** * Get files that match ANY of the fileMatches pattern. fileMatches are compiled with the branchConfig. * @param bumpVersionsDescr log description for the bump version config * @param filePatternTemplates list of regex patterns * @param branchConfig compile metadata * @param fileList list of files to match against */ function getMatchedFiles(bumpVersionsDescr, filePatternTemplates, branchConfig, fileList) { const filePatterns = []; for (const filePatternTemplateElement of filePatternTemplates) { const filePattern = compile(filePatternTemplateElement, branchConfig); filePatterns.push(filePattern); } logger.trace({ filePatterns }, `${bumpVersionsDescr}: Compiled filePatterns`); const files = []; for (const file of fileList) if (matchRegexOrGlobList(file, filePatterns)) files.push(file); return files; } function fileChangeListToMap(list) { const record = {}; for (const fileChange of coerceArray(list)) { record[fileChange.path] ??= []; record[fileChange.path].push(fileChange); } return record; } function addArtifactError(branchConfig, message, fileName) { branchConfig.artifactErrors ??= []; branchConfig.artifactErrors.push({ stderr: message, fileName }); } async function getFileContent(bumpVersionsDescr, filePath, packageFiles, artifactFiles) { const packageFileChanges = parseFileChanges(filePath, packageFiles); const artifactFileChanges = parseFileChanges(filePath, artifactFiles); if (packageFileChanges.state === "deleted" || artifactFileChanges.state === "deleted") return null; if (packageFileChanges.state === "modified") { const lastChange = packageFileChanges.content; if (lastChange) return lastChange; } if (artifactFileChanges.state === "modified") { const lastChange = artifactFileChanges.content; if (lastChange) return lastChange; } try { return await readLocalFile(filePath, "utf8"); } catch (e) { logger.warn({ file: filePath }, `${bumpVersionsDescr}: Could not read file: ${e.message}`); return null; } } function parseFileChanges(filePath, changeRecord) { const changes = coerceArray(changeRecord[filePath]); if (!changes.length) return { state: "unmodified" }; const lastChange = changes[changes.length - 1]; if (lastChange.type === "deletion") return { state: "deleted" }; return { state: "modified", content: lastChange.contents?.toString() ?? null }; } //#endregion export { bumpVersions }; //# sourceMappingURL=bump-versions.js.map