renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
204 lines • 9.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPythonConstraint = getPythonConstraint;
exports.getPoetryRequirement = getPoetryRequirement;
exports.updateArtifacts = updateArtifacts;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const shlex_1 = require("shlex");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const exec_1 = require("../../../util/exec");
const fs_1 = require("../../../util/fs");
const auth_1 = require("../../../util/git/auth");
const host_rules_1 = require("../../../util/host-rules");
const regex_1 = require("../../../util/regex");
const result_1 = require("../../../util/result");
const toml_1 = require("../../../util/toml");
const url_1 = require("../../../util/url");
const pypi_1 = require("../../datasource/pypi");
const util_1 = require("../../datasource/util");
const schema_1 = require("./schema");
function getPythonConstraint(pyProjectContent, existingLockFileContent) {
// Read Python version from `pyproject.toml` first as it could have been updated
const pyprojectPythonConstraint = result_1.Result.parse((0, toml_1.massage)(pyProjectContent), schema_1.PoetryPyProject.transform(({ packageFileContent }) => packageFileContent.deps.find((dep) => dep.depName === 'python')
?.currentValue)).unwrapOrNull();
if (pyprojectPythonConstraint) {
logger_1.logger.debug('Using python version from pyproject.toml');
return pyprojectPythonConstraint;
}
const lockfilePythonConstraint = result_1.Result.parse(existingLockFileContent, schema_1.Lockfile.transform(({ pythonVersions }) => pythonVersions)).unwrapOrNull();
if (lockfilePythonConstraint) {
logger_1.logger.debug('Using python version from poetry.lock');
return lockfilePythonConstraint;
}
return null;
}
function getPoetryRequirement(pyProjectContent, existingLockFileContent) {
// Read Poetry version from first line of poetry.lock
const firstLine = existingLockFileContent.split('\n')[0];
const poetryVersionMatch = (0, regex_1.regEx)(/by Poetry ([\d\\.]+)/).exec(firstLine);
if (poetryVersionMatch?.[1]) {
const poetryVersion = poetryVersionMatch[1];
logger_1.logger.debug(`Using poetry version ${poetryVersion} from poetry.lock header`);
return poetryVersion;
}
const { val: lockfilePoetryConstraint } = result_1.Result.parse(existingLockFileContent, schema_1.Lockfile.transform(({ poetryConstraint }) => poetryConstraint)).unwrap();
if (lockfilePoetryConstraint) {
logger_1.logger.debug(`Using poetry version ${lockfilePoetryConstraint} from poetry.lock metadata`);
return lockfilePoetryConstraint;
}
const { val: pyprojectPoetryConstraint } = result_1.Result.parse((0, toml_1.massage)(pyProjectContent), schema_1.PoetryPyProject.transform(({ poetryRequirement }) => poetryRequirement)).unwrap();
if (pyprojectPoetryConstraint) {
logger_1.logger.debug(`Using poetry version ${pyprojectPoetryConstraint} from pyproject.toml`);
return pyprojectPoetryConstraint;
}
return null;
}
function getPoetrySources(content, fileName) {
let pyprojectFile;
try {
pyprojectFile = (0, toml_1.parse)((0, toml_1.massage)(content));
}
catch (err) {
logger_1.logger.debug({ err }, 'Error parsing pyproject.toml file');
return [];
}
if (!pyprojectFile.tool?.poetry) {
logger_1.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 = (0, host_rules_1.find)({ hostType: pypi_1.PypiDatasource.id, url });
const hostRule = is_1.default.nonEmptyObject(scopedMatch) ? scopedMatch : (0, host_rules_1.find)({ url });
if (hostRule && Object.keys(hostRule).length !== 0) {
return hostRule;
}
const parsedUrl = (0, url_1.parseUrl)(url);
if (!parsedUrl) {
logger_1.logger.once.debug(`Failed to parse URL ${url}`);
return {};
}
if (parsedUrl.hostname.endsWith('.pkg.dev')) {
const hostRule = await (0, util_1.getGoogleAuthHostRule)();
if (hostRule && Object.keys(hostRule).length !== 0) {
return hostRule;
}
logger_1.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((0, regex_1.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_1.logger.debug(`poetry.updateArtifacts(${packageFileName})`);
const { isLockFileMaintenance } = config;
if (!is_1.default.nonEmptyArray(updatedDeps) && !isLockFileMaintenance) {
logger_1.logger.debug('No updated poetry deps - returning null');
return null;
}
// Try poetry.lock first
let lockFileName = (0, fs_1.getSiblingFileName)(packageFileName, 'poetry.lock');
let existingLockFileContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
if (!existingLockFileContent) {
// Try pyproject.lock next
lockFileName = (0, fs_1.getSiblingFileName)(packageFileName, 'pyproject.lock');
existingLockFileContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
if (!existingLockFileContent) {
logger_1.logger.debug(`No lock file found`);
return null;
}
}
logger_1.logger.debug(`Updating ${lockFileName}`);
try {
await (0, fs_1.writeLocalFile)(packageFileName, newPackageFileContent);
const cmd = [];
if (isLockFileMaintenance) {
await (0, fs_1.deleteLocalFile)(lockFileName);
cmd.push('poetry update --lock --no-interaction');
}
else {
cmd.push(`poetry update --lock --no-interaction ${updatedDeps
.map((dep) => dep.depName)
.filter(is_1.default.string)
.map((dep) => (0, shlex_1.quote)(dep))
.join(' ')}`);
}
const pythonConstraint = config?.constraints?.python ??
getPythonConstraint(newPackageFileContent, existingLockFileContent);
const poetryConstraint = config.constraints?.poetry ??
getPoetryRequirement(newPackageFileContent, existingLockFileContent);
const extraEnv = {
...(await getSourceCredentialVars(newPackageFileContent, packageFileName)),
...(0, auth_1.getGitEnvironmentVariables)(['poetry']),
PIP_CACHE_DIR: await (0, fs_1.ensureCacheDir)('pip'),
};
const execOptions = {
cwdFile: packageFileName,
extraEnv,
docker: {},
toolConstraints: [
{ toolName: 'python', constraint: pythonConstraint },
{ toolName: 'poetry', constraint: poetryConstraint },
],
};
await (0, exec_1.exec)(cmd, execOptions);
const newPoetryLockContent = await (0, fs_1.readLocalFile)(lockFileName, 'utf8');
if (existingLockFileContent === newPoetryLockContent) {
logger_1.logger.debug(`${lockFileName} is unchanged`);
return null;
}
logger_1.logger.debug(`Returning updated ${lockFileName}`);
return [
{
file: {
type: 'addition',
path: lockFileName,
contents: newPoetryLockContent,
},
},
];
}
catch (err) {
// istanbul ignore if
if (err.message === error_messages_1.TEMPORARY_ERROR) {
throw err;
}
logger_1.logger.debug({ err }, `Failed to update ${lockFileName} file`);
return [
{
artifactError: {
lockFile: lockFileName,
stderr: `${String(err.stdout)}\n${String(err.stderr)}`,
},
},
];
}
}
//# sourceMappingURL=artifacts.js.map