UNPKG

renovate

Version:

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

231 lines (230 loc) • 10.7 kB
import { regEx } from "../../../util/regex.js"; import { GlobalConfig } from "../../../config/global.js"; import { logger } from "../../../logger/index.js"; import { hashStream } from "../../../util/hash.js"; import { chmodLocalFile, deleteLocalFile, readLocalFile, statLocalFile, writeLocalFile } from "../../../util/fs/index.js"; import api from "../../versioning/maven/index.js"; import { withCache } from "../../../util/cache/package/with-cache.js"; import { Http } from "../../../util/http/index.js"; import { exec } from "../../../util/exec/index.js"; import { getRepoStatus } from "../../../util/git/index.js"; import { isTruthy } from "@sindresorhus/is"; import upath from "upath"; import os from "node:os"; //#region lib/modules/manager/maven-wrapper/artifacts.ts const http = new Http("maven-wrapper"); const DEFAULT_MAVEN_REPO_URL = "https://repo.maven.apache.org/maven2"; function getChecksumFromUrl(url) { return withCache({ namespace: "url-sha256", key: url, ttlMinutes: 4320 }, () => hashStream(http.stream(url), "sha256")); } function getDistributionUrl(content) { return regEx(/^distributionUrl\s*=\s*(?<distributionUrl>.+)$/m).exec(content)?.groups?.distributionUrl?.replace(/\\:/g, ":").trim() ?? null; } function getWrapperUrl(content) { return regEx(/^wrapperUrl\s*=\s*(?<wrapperUrl>.+)$/m).exec(content)?.groups?.wrapperUrl?.replace(/\\:/g, ":").trim() ?? null; } function constructWrapperUrl(version, repoUrl = DEFAULT_MAVEN_REPO_URL) { return `${repoUrl}/org/apache/maven/wrapper/maven-wrapper/${version}/maven-wrapper-${version}.jar`; } function getWrapperVersion(content) { return regEx(/^wrapperVersion\s*=\s*(?<wrapperVersion>.+)$/m).exec(content)?.groups?.wrapperVersion?.trim() ?? null; } function getDistributionType(content) { return regEx(/^distributionType\s*=\s*(?<distributionType>.+)$/m).exec(content)?.groups?.distributionType?.trim() ?? null; } function getChecksumValue(content, key) { return regEx(`^${key}\\s*=\\s*(?<value>.+)$`, "m").exec(content)?.groups?.value?.trim() ?? null; } function addChecksumAfterLine(content, lineRegex, key, value) { return content.replace(regEx(lineRegex), (lineMatch) => `${lineMatch}\n${key}=${value}`); } async function updateChecksums(content, updatedDeps, addDistributionChecksum, addWrapperChecksum, originalContent) { let updatedContent = content; if (addDistributionChecksum) { const existingChecksum = getChecksumValue(updatedContent, "distributionSha256Sum"); const fallbackChecksum = getChecksumValue(originalContent, "distributionSha256Sum"); const distUrl = getDistributionUrl(updatedContent); if (distUrl) try { const checksum = await getChecksumFromUrl(distUrl); if (existingChecksum) updatedContent = updatedContent.replace(regEx(/distributionSha256Sum=.*/), `distributionSha256Sum=${checksum}`); else updatedContent = addChecksumAfterLine(updatedContent, /^(distributionUrl\s*=\s*.+)$/m, "distributionSha256Sum", checksum); } catch (err) { logger.warn({ err, url: distUrl }, "Failed to fetch distribution checksum"); if (!existingChecksum && fallbackChecksum) updatedContent = addChecksumAfterLine(updatedContent, /^(distributionUrl\s*=\s*.+)$/m, "distributionSha256Sum", fallbackChecksum); } } if (addWrapperChecksum) { const existingChecksum = getChecksumValue(updatedContent, "wrapperSha256Sum"); const fallbackChecksum = getChecksumValue(originalContent, "wrapperSha256Sum"); let wrapperUrl = getWrapperUrl(updatedContent); if (!wrapperUrl) { const wrapperVersion = getWrapperVersion(updatedContent); if (wrapperVersion) wrapperUrl = constructWrapperUrl(wrapperVersion, getCustomMavenWrapperRepoUrl(updatedDeps) ?? DEFAULT_MAVEN_REPO_URL); } if (wrapperUrl) try { const checksum = await getChecksumFromUrl(wrapperUrl); if (existingChecksum) updatedContent = updatedContent.replace(regEx(/wrapperSha256Sum=.*/), `wrapperSha256Sum=${checksum}`); else updatedContent = addChecksumAfterLine(updatedContent, /^(wrapperUrl\s*=\s*.+|wrapperVersion\s*=\s*.+)$/m, "wrapperSha256Sum", checksum); } catch (err) { logger.warn({ err, url: wrapperUrl }, "Failed to fetch wrapper checksum"); if (!existingChecksum && fallbackChecksum) updatedContent = addChecksumAfterLine(updatedContent, /^(wrapperUrl\s*=\s*.+|wrapperVersion\s*=\s*.+)$/m, "wrapperSha256Sum", fallbackChecksum); } } return updatedContent; } async function addIfUpdated(status, fileProjectPath) { if (status.modified.includes(fileProjectPath)) return { file: { type: "addition", path: fileProjectPath, contents: await readLocalFile(fileProjectPath) } }; return null; } async function updateArtifacts({ packageFileName, newPackageFileContent, updatedDeps, config }) { try { logger.debug({ updatedDeps }, "maven-wrapper.updateArtifacts()"); const hasMavenUpdate = updatedDeps.some((dep) => dep.depName === "maven"); const hasWrapperUpdate = updatedDeps.some((dep) => dep.depName === "maven-wrapper"); const hadDistributionChecksum = newPackageFileContent.includes("distributionSha256Sum="); const hadWrapperChecksum = newPackageFileContent.includes("wrapperSha256Sum="); const originalDistributionType = getDistributionType(newPackageFileContent); if (!hasWrapperUpdate && !(hasMavenUpdate && hadDistributionChecksum)) { logger.debug("No Maven wrapper or distribution checksum updates - skipping Artifacts update"); return null; } let cmd = null; if (hasWrapperUpdate) { cmd = await createWrapperCommand(packageFileName, originalDistributionType); if (!cmd) { logger.info("No mvnw found - skipping Artifacts update"); return null; } } if (hasWrapperUpdate && (hadWrapperChecksum || hadDistributionChecksum)) { const jarPath = packageFileName.replace("maven-wrapper.properties", "maven-wrapper.jar"); if (jarPath.endsWith("maven-wrapper.jar")) try { await deleteLocalFile(jarPath); logger.debug({ jarPath }, "Deleted old maven-wrapper.jar"); } catch {} } let contentToWrite = newPackageFileContent; if (hasWrapperUpdate && (hadWrapperChecksum || hadDistributionChecksum)) contentToWrite = contentToWrite.replace(regEx(/^distributionSha256Sum=.*\n?/m), "").replace(regEx(/^wrapperSha256Sum=.*\n?/m), ""); await writeLocalFile(packageFileName, contentToWrite); if (hasWrapperUpdate && cmd) { const extraEnv = getExtraEnvOptions(updatedDeps); await executeWrapperCommand(cmd, config, packageFileName, extraEnv); } if (hadDistributionChecksum || hadWrapperChecksum) { const currentContent = await readLocalFile(packageFileName, "utf8"); if (currentContent) await writeLocalFile(packageFileName, await updateChecksums(currentContent, updatedDeps, hadDistributionChecksum, hadWrapperChecksum, newPackageFileContent)); } const updateArtifactsResult = (await getUpdatedArtifacts(await getRepoStatus(), [ ".mvn/wrapper/maven-wrapper.properties", ".mvn/wrapper/maven-wrapper.jar", ".mvn/wrapper/MavenWrapperDownloader.java", "mvnw", "mvnw.cmd" ].map((filename) => packageFileName.replace(".mvn/wrapper/maven-wrapper.properties", "") + filename))).filter(isTruthy); logger.debug({ files: updateArtifactsResult.map((r) => r.file?.path) }, `Returning updated maven-wrapper files`); return updateArtifactsResult; } catch (err) { logger.debug({ err }, "Error setting new Maven Wrapper release value"); return [{ artifactError: { fileName: packageFileName, stderr: err.message } }]; } } async function getUpdatedArtifacts(status, artifactFileNames) { const updatedResults = []; for (const artifactFileName of artifactFileNames) { const updatedResult = await addIfUpdated(status, artifactFileName); if (updatedResult !== null) updatedResults.push(updatedResult); } return updatedResults; } /** * Find compatible java version for maven. * see https://maven.apache.org/developers/compatibility-plan.html * @param mavenWrapperVersion current maven version * @returns A Java semver range */ function getJavaConstraint(mavenWrapperVersion) { const major = mavenWrapperVersion ? api.getMajor(mavenWrapperVersion) : null; if (major && major >= 3) return "^17.0.0"; return "^8.0.0"; } async function executeWrapperCommand(cmd, config, packageFileName, extraEnv) { logger.debug(`Updating maven wrapper: "${cmd}"`); const { wrapperFullyQualifiedPath } = getMavenPaths(packageFileName); const execOptions = { cwdFile: wrapperFullyQualifiedPath, docker: {}, extraEnv, toolConstraints: [{ toolName: "java", constraint: config.constraints?.java ?? getJavaConstraint(config.currentValue) }] }; try { await exec(cmd, execOptions); } catch (err) { logger.error({ err }, "Error executing maven wrapper update command."); throw err; } } function getExtraEnvOptions(deps) { const customMavenWrapperUrl = getCustomMavenWrapperRepoUrl(deps); if (customMavenWrapperUrl) return { MVNW_REPOURL: customMavenWrapperUrl }; return {}; } function getCustomMavenWrapperRepoUrl(deps) { const replaceString = deps.find((dep) => dep.depName === "maven-wrapper")?.replaceString; if (!replaceString) return null; const match = regEx(/^(.*?)\/org\/apache\/maven\/wrapper\//).exec(replaceString); if (!match) return null; return match[1] === DEFAULT_MAVEN_REPO_URL ? null : match[1]; } async function createWrapperCommand(packageFileName, distributionType) { const { wrapperExecutableFileName, localProjectDir, wrapperFullyQualifiedPath } = getMavenPaths(packageFileName); const args = `wrapper:wrapper -Dtype=${distributionType ?? "script"}`; return await prepareCommand(wrapperExecutableFileName, localProjectDir, await statLocalFile(wrapperFullyQualifiedPath), args); } function mavenWrapperFileName() { if (os.platform() === "win32" && GlobalConfig.get("binarySource") !== "docker") return "mvnw.cmd"; return "./mvnw"; } function getMavenPaths(packageFileName) { const wrapperExecutableFileName = mavenWrapperFileName(); const localProjectDir = upath.join(upath.dirname(packageFileName), packageFileName.includes("mvnw") ? "." : "../../"); return { wrapperExecutableFileName, localProjectDir, wrapperFullyQualifiedPath: upath.join(localProjectDir, wrapperExecutableFileName) }; } async function prepareCommand(fileName, cwd, pathFileStats, args) { /* v8 ignore next -- hard to test */ if (pathFileStats?.isFile() === true) { if (os.platform() !== "win32" && (pathFileStats.mode & 1) === 0) { logger.warn("Maven wrapper is missing the executable bit"); await chmodLocalFile(upath.join(cwd, fileName), pathFileStats.mode | 73); } if (args === null) return fileName; return `${fileName} ${args}`; } return null; } //#endregion export { updateArtifacts }; //# sourceMappingURL=artifacts.js.map