UNPKG

renovate

Version:

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

133 lines (132 loc) 5.82 kB
import { BUNDLER_INVALID_CREDENTIALS } from "../../../constants/error-messages.js"; import { get, set } from "../../../util/cache/memory/index.js"; import { newlineRegex, regEx } from "../../../util/regex.js"; import { logger } from "../../../logger/index.js"; import { ensureCacheDir, readLocalFile, writeLocalFile } from "../../../util/fs/index.js"; import { exec } from "../../../util/exec/index.js"; import { getRepoStatus } from "../../../util/git/index.js"; import { getBundlerConstraint, getLockFilePath, getRubyConstraint } from "./common.js"; import { findAllAuthenticatable, getAuthenticationHeaderValue } from "./host-rules.js"; import { isNonEmptyStringAndNotWhitespace, isString } from "@sindresorhus/is"; import { quote } from "shlex"; //#region lib/modules/manager/bundler/artifacts.ts const hostConfigVariablePrefix = "BUNDLE_"; function buildBundleHostVariable(hostRule) { // istanbul ignore if: doesn't happen in practice if (!hostRule.resolvedHost) return {}; return { [hostConfigVariablePrefix.concat(hostRule.resolvedHost.toUpperCase().split(".").join("__").split("-").join("___"))]: `${getAuthenticationHeaderValue(hostRule)}` }; } const resolvedPkgRegex = regEx(/(?<pkg>\S+)(?:\s*\([^)]+\)\s*)? was resolved to/); function getResolvedPackages(input) { const lines = input.split(newlineRegex); const result = []; for (const line of lines) { const resolveMatchGroups = line.match(resolvedPkgRegex)?.groups; if (resolveMatchGroups) { const { pkg } = resolveMatchGroups; result.push(pkg); } } return [...new Set(result)]; } async function updateArtifacts(updateArtifact, recursionLimit = 10) { const { packageFileName, updatedDeps, newPackageFileContent, config } = updateArtifact; logger.debug(`bundler.updateArtifacts(${packageFileName})`); const existingError = get("bundlerArtifactsError"); // istanbul ignore if if (existingError) { logger.debug("Aborting Bundler artifacts due to previous failed attempt"); throw new Error(existingError); } const lockFileName = await getLockFilePath(packageFileName); const existingLockFileContent = await readLocalFile(lockFileName, "utf8"); if (!existingLockFileContent) { logger.debug("No Gemfile.lock found"); return null; } const updatedDepNames = updatedDeps.map(({ depName }) => depName).filter(isNonEmptyStringAndNotWhitespace); try { await writeLocalFile(packageFileName, newPackageFileContent); const commands = []; if (config.isLockFileMaintenance) commands.push("bundler lock --update"); else { if (updatedDeps.map((dep) => dep.depName).includes("bundler")) commands.push("bundler lock --update --bundler"); for (const [updateType, updateArg] of Object.entries({ patch: "--patch ", minor: "--minor ", major: "" })) { const deps = updatedDeps.filter((dep) => (dep.updateType ?? "major") === updateType).map((dep) => dep.depName).filter(isString).filter((dep) => dep !== "ruby" && dep !== "bundler"); let additionalArgs = ""; if (config.postUpdateOptions?.includes("bundlerConservative")) additionalArgs = "--conservative "; if (deps.length) { const cmd = `bundler lock ${updateArg}${additionalArgs}--update ${deps.map(quote).join(" ")}`; commands.push(cmd); } } if (updatedDeps.map((dep) => dep.depName).includes("ruby")) commands.push("bundler lock"); } const bundlerHostRulesVariables = findAllAuthenticatable({ hostType: "rubygems" }).reduce((variables, hostRule) => ({ ...variables, ...buildBundleHostVariable(hostRule) }), {}); const bundler = getBundlerConstraint(updateArtifact, existingLockFileContent); const preCommands = ["ruby --version"]; await exec(commands, { cwdFile: lockFileName, extraEnv: { ...bundlerHostRulesVariables, GEM_HOME: await ensureCacheDir("bundler") }, docker: {}, toolConstraints: [{ toolName: "ruby", constraint: await getRubyConstraint(updateArtifact) }, { toolName: "bundler", constraint: bundler }], preCommands }); if (!(await getRepoStatus()).modified.includes(lockFileName)) return null; logger.debug("Returning updated Gemfile.lock"); return [{ file: { type: "addition", path: lockFileName, contents: await readLocalFile(lockFileName) } }]; } catch (err) { if (err.message === "temporary-error") throw err; const output = `${String(err.stdout)}\n${String(err.stderr)}`; if (err.message.includes("fatal: Could not parse object") || output.includes("but that version could not be found")) return [{ artifactError: { fileName: lockFileName, stderr: output } }]; if (err.stdout?.includes("Please supply credentials for this source") || err.stderr?.includes("Authentication is required") || err.stderr?.includes("Please make sure you have the correct access rights")) { logger.debug({ err }, "Gemfile.lock update failed due to missing credentials - skipping branch"); set("bundlerArtifactsError", BUNDLER_INVALID_CREDENTIALS); throw new Error(BUNDLER_INVALID_CREDENTIALS); } const resolveMatches = getResolvedPackages(output).filter((depName) => !updatedDepNames.includes(depName)); if (recursionLimit > 0 && resolveMatches.length && !config.isLockFileMaintenance) { logger.debug({ resolveMatches, updatedDeps }, "Found new resolve matches - reattempting recursively"); return updateArtifacts({ packageFileName, updatedDeps: [...new Set([...updatedDeps, ...resolveMatches.map((match) => ({ depName: match }))])], newPackageFileContent, config }, recursionLimit - 1); } logger.info({ err }, "Gemfile.lock update failed due to an unknown reason"); return [{ artifactError: { fileName: lockFileName, stderr: `${String(err.stdout)}\n${String(err.stderr)}` } }]; } } //#endregion export { updateArtifacts }; //# sourceMappingURL=artifacts.js.map