UNPKG

renovate

Version:

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

167 lines (166 loc) • 7.13 kB
import "../../../constants/error-messages.js"; import { regEx } from "../../../util/regex.js"; import { logger } from "../../../logger/index.js"; import { parseUrl } from "../../../util/url.js"; import { find } from "../../../util/host-rules.js"; import { massage, parse } from "../../../util/toml.js"; import { deleteLocalFile, ensureCacheDir, getSiblingFileName, readLocalFile, writeLocalFile } from "../../../util/fs/index.js"; import { Result } from "../../../util/result.js"; import { getGoogleAuthHostRule } from "../../datasource/util.js"; import { getGitEnvironmentVariables } from "../../../util/git/auth.js"; import { exec } from "../../../util/exec/index.js"; import { PypiDatasource } from "../../datasource/pypi/index.js"; import { Lockfile, PoetryPyProject } from "./schema.js"; import { isNonEmptyArray, isNonEmptyObject, isString } from "@sindresorhus/is"; import { quote } from "shlex"; //#region lib/modules/manager/poetry/artifacts.ts function getPythonConstraint(pyProjectContent, existingLockFileContent) { const pyprojectPythonConstraint = Result.parse(massage(pyProjectContent), PoetryPyProject.transform(({ packageFileContent }) => packageFileContent.deps.find((dep) => dep.depName === "python")?.currentValue)).unwrapOrNull(); if (pyprojectPythonConstraint) { logger.debug("Using python version from pyproject.toml"); return pyprojectPythonConstraint; } const lockfilePythonConstraint = Result.parse(existingLockFileContent, Lockfile.transform(({ pythonVersions }) => pythonVersions)).unwrapOrNull(); if (lockfilePythonConstraint) { logger.debug("Using python version from poetry.lock"); return lockfilePythonConstraint; } return null; } function getPoetryRequirement(pyProjectContent, existingLockFileContent) { const firstLine = existingLockFileContent.split("\n")[0]; const poetryVersionMatch = regEx(/by Poetry ([\d\\.]+)/).exec(firstLine); if (poetryVersionMatch?.[1]) { const poetryVersion = poetryVersionMatch[1]; logger.debug(`Using poetry version ${poetryVersion} from poetry.lock header`); return poetryVersion; } const { val: lockfilePoetryConstraint } = Result.parse(existingLockFileContent, Lockfile.transform(({ poetryConstraint }) => poetryConstraint)).unwrap(); if (lockfilePoetryConstraint) { logger.debug(`Using poetry version ${lockfilePoetryConstraint} from poetry.lock metadata`); return lockfilePoetryConstraint; } const { val: pyprojectPoetryConstraint } = Result.parse(massage(pyProjectContent), PoetryPyProject.transform(({ poetryRequirement }) => poetryRequirement)).unwrap(); if (pyprojectPoetryConstraint) { logger.debug(`Using poetry version ${pyprojectPoetryConstraint} from pyproject.toml`); return pyprojectPoetryConstraint; } return null; } function getPoetrySources(content, fileName) { let pyprojectFile; try { pyprojectFile = parse(massage(content)); } catch (err) { logger.debug({ err }, "Error parsing pyproject.toml file"); return []; } if (!pyprojectFile.tool?.poetry) { logger.debug(`${fileName} contains no poetry section`); return []; } const sources = pyprojectFile.tool?.poetry?.source ?? []; const sourceArray = []; for (const source of sources) if (source.name && source.url) sourceArray.push({ name: source.name, url: source.url }); return sourceArray; } async function getMatchingHostRule(url) { const scopedMatch = find({ hostType: PypiDatasource.id, url }); const hostRule = isNonEmptyObject(scopedMatch) ? scopedMatch : find({ url }); if (hostRule && Object.keys(hostRule).length !== 0) return hostRule; const parsedUrl = parseUrl(url); if (!parsedUrl) { logger.once.debug(`Failed to parse URL ${url}`); return {}; } if (parsedUrl.hostname.endsWith(".pkg.dev")) { const hostRule = await getGoogleAuthHostRule(); if (hostRule && Object.keys(hostRule).length !== 0) return hostRule; logger.once.debug(`Could not get Google access token (url=${url})`); } return {}; } async function getSourceCredentialVars(pyprojectContent, packageFileName) { const poetrySources = getPoetrySources(pyprojectContent, packageFileName); const envVars = {}; for (const source of poetrySources) { const matchingHostRule = await getMatchingHostRule(source.url); const formattedSourceName = source.name.replace(regEx(/(\.|-)+/g), "_").toUpperCase(); if (matchingHostRule.username) envVars[`POETRY_HTTP_BASIC_${formattedSourceName}_USERNAME`] = matchingHostRule.username; if (matchingHostRule.password) envVars[`POETRY_HTTP_BASIC_${formattedSourceName}_PASSWORD`] = matchingHostRule.password; } return envVars; } async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config }) { logger.debug(`poetry.updateArtifacts(${packageFileName})`); const { isLockFileMaintenance } = config; if (!isNonEmptyArray(updatedDeps) && !isLockFileMaintenance) { logger.debug("No updated poetry deps - returning null"); return null; } let lockFileName = getSiblingFileName(packageFileName, "poetry.lock"); let existingLockFileContent = await readLocalFile(lockFileName, "utf8"); if (!existingLockFileContent) { lockFileName = getSiblingFileName(packageFileName, "pyproject.lock"); existingLockFileContent = await readLocalFile(lockFileName, "utf8"); if (!existingLockFileContent) { logger.debug(`No lock file found`); return null; } } logger.debug(`Updating ${lockFileName}`); try { await writeLocalFile(packageFileName, newPackageFileContent); const cmd = []; if (isLockFileMaintenance) { await deleteLocalFile(lockFileName); cmd.push("poetry update --lock --no-interaction"); } else cmd.push(`poetry update --lock --no-interaction ${updatedDeps.map((dep) => dep.depName).filter(isString).map((dep) => quote(dep)).join(" ")}`); const pythonConstraint = config?.constraints?.python ?? getPythonConstraint(newPackageFileContent, existingLockFileContent); const poetryConstraint = config.constraints?.poetry ?? getPoetryRequirement(newPackageFileContent, existingLockFileContent); await exec(cmd, { cwdFile: packageFileName, extraEnv: { ...await getSourceCredentialVars(newPackageFileContent, packageFileName), ...getGitEnvironmentVariables(["poetry"]), PIP_CACHE_DIR: await ensureCacheDir("pip") }, docker: {}, toolConstraints: [{ toolName: "python", constraint: pythonConstraint }, { toolName: "poetry", constraint: poetryConstraint }] }); const newPoetryLockContent = await readLocalFile(lockFileName, "utf8"); if (existingLockFileContent === newPoetryLockContent) { logger.debug(`${lockFileName} is unchanged`); return null; } logger.debug(`Returning updated ${lockFileName}`); return [{ file: { type: "addition", path: lockFileName, contents: newPoetryLockContent } }]; } catch (err) { // istanbul ignore if if (err.message === "temporary-error") throw err; logger.debug({ err }, `Failed to update ${lockFileName} file`); return [{ artifactError: { fileName: lockFileName, stderr: `${String(err.stdout)}\n${String(err.stderr)}` } }]; } } //#endregion export { updateArtifacts }; //# sourceMappingURL=artifacts.js.map