renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
327 lines • 11.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.allowedOptions = exports.optionsWithArguments = exports.disallowedPipOptions = exports.constraintLineRegex = void 0;
exports.getPythonVersionConstraint = getPythonVersionConstraint;
exports.getPipToolsVersionConstraint = getPipToolsVersionConstraint;
exports.getUvVersionConstraint = getUvVersionConstraint;
exports.getToolVersionConstraint = getToolVersionConstraint;
exports.getExecOptions = getExecOptions;
exports.extractHeaderCommand = extractHeaderCommand;
exports.extractPythonVersion = extractPythonVersion;
exports.getRegistryCredVarsFromPackageFiles = getRegistryCredVarsFromPackageFiles;
exports.matchManager = matchManager;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const shlex_1 = require("shlex");
const upath_1 = tslib_1.__importDefault(require("upath"));
const logger_1 = require("../../../logger");
const array_1 = require("../../../util/array");
const fs_1 = require("../../../util/fs");
const util_1 = require("../../../util/fs/util");
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const regex_1 = require("../../../util/regex");
function getPythonVersionConstraint(config, extractedPythonVersion) {
const { constraints = {} } = config;
const { python } = constraints;
if (python) {
logger_1.logger.debug('Using python constraint from config');
return python;
}
if (extractedPythonVersion) {
logger_1.logger.debug('Using python constraint extracted from the lock file');
return `==${extractedPythonVersion}`;
}
return undefined;
}
function getPipToolsVersionConstraint(config) {
const { constraints = {} } = config;
const { pipTools } = constraints;
if (is_1.default.string(pipTools)) {
logger_1.logger.debug('Using pipTools constraint from config');
return pipTools;
}
return '';
}
function getUvVersionConstraint(config) {
const { constraints = {} } = config;
const { uv } = constraints;
if (is_1.default.string(uv)) {
logger_1.logger.debug('Using uv constraint from config');
return uv;
}
return '';
}
function getToolVersionConstraint(config, commandType) {
if (commandType === 'uv') {
return {
toolName: 'uv',
constraint: getUvVersionConstraint(config),
};
}
return {
toolName: 'pip-tools',
constraint: getPipToolsVersionConstraint(config),
};
}
async function getExecOptions(config, commandType, cwd, extraEnv, extractedPythonVersion) {
const constraint = getPythonVersionConstraint(config, extractedPythonVersion);
const execOptions = {
cwd: (0, util_1.ensureLocalPath)(cwd),
docker: {},
toolConstraints: [
{
toolName: 'python',
constraint,
},
getToolVersionConstraint(config, commandType),
],
extraEnv: {
PIP_CACHE_DIR: await (0, fs_1.ensureCacheDir)('pip'),
PIP_NO_INPUT: 'true', // ensure pip doesn't block forever waiting for credentials on stdin
PIP_KEYRING_PROVIDER: 'import',
PYTHON_KEYRING_BACKEND: 'keyrings.envvars.keyring.EnvvarsKeyring',
...extraEnv,
},
};
return execOptions;
}
exports.constraintLineRegex = (0, regex_1.regEx)(/^(#.*?\r?\n)+# {4}(?<command>\S*)(?<arguments> .*?)?\r?\n/);
exports.disallowedPipOptions = [
'--no-header', // header is required by this manager
];
const commonOptionsWithArguments = [
'--output-file',
'--extra',
'--extra-index-url',
];
const pipOptionsWithArguments = [
'--resolver',
'--constraint',
...commonOptionsWithArguments,
];
const uvOptionsWithArguments = [
'--constraints',
'--python-version',
'--no-emit-package',
...commonOptionsWithArguments,
];
exports.optionsWithArguments = [
...pipOptionsWithArguments,
...uvOptionsWithArguments,
];
const allowedCommonOptions = [
'-v',
'--generate-hashes',
'--emit-index-url',
'--index-url',
];
exports.allowedOptions = {
'pip-compile': [
'--all-extras',
'--allow-unsafe',
'--generate-hashes',
'--no-emit-index-url',
'--strip-extras',
...allowedCommonOptions,
...pipOptionsWithArguments,
],
uv: [
'--no-strip-extras',
'--universal',
...allowedCommonOptions,
...uvOptionsWithArguments,
],
custom: [],
};
// TODO(not7cd): test on all correct headers, even with CUSTOM_COMPILE_COMMAND
function extractHeaderCommand(content, fileName) {
const compileCommand = exports.constraintLineRegex.exec(content);
if (compileCommand?.groups === undefined) {
throw new Error(`Failed to extract command from header in ${fileName} ${content}`);
}
logger_1.logger.trace(`pip-compile: found header in ${fileName}: \n${compileCommand[0]}`);
const command = compileCommand.groups.command;
const argv = [command];
let commandType;
if (command === 'pip-compile') {
commandType = 'pip-compile';
}
else if (command === 'uv') {
commandType = 'uv';
}
else {
commandType = 'custom';
}
if (compileCommand.groups.arguments) {
argv.push(...(0, shlex_1.split)(compileCommand.groups.arguments));
}
logger_1.logger.debug({ fileName, argv, commandType }, `pip-compile: extracted command from header`);
const result = {
argv,
command,
commandType,
outputFile: '',
sourceFiles: [],
};
for (const arg of argv.slice(1)) {
if (commandType === 'uv' && ['pip', 'compile'].includes(arg)) {
continue;
}
// TODO(not7cd): check for "--option -- argument" case
if (!arg.startsWith('-')) {
result.sourceFiles.push(arg);
continue;
}
throwForDisallowedOption(arg);
throwForNoEqualSignInOptionWithArgument(arg);
throwForUnknownOption(commandType, arg);
if (arg.includes('=')) {
const [option, value] = arg.split('=');
if (option === '--extra') {
result.extra = result.extra ?? [];
result.extra.push(value);
}
else if (option === '--extra-index-url') {
result.extraIndexUrl = result.extraIndexUrl ?? [];
result.extraIndexUrl.push(value);
// TODO: add to secrets? next PR
}
else if (['--constraint', '--constraints'].includes(option)) {
result.constraintsFiles = result.constraintsFiles ?? [];
result.constraintsFiles.push(value);
}
else if (option === '--output-file') {
if (result.outputFile) {
throw new Error('Cannot use multiple --output-file options');
}
result.outputFile = upath_1.default.normalize(value);
}
else if (option === '--python-version') {
result.pythonVersion = value;
}
else if (option === '--index-url') {
if (result.indexUrl) {
throw new Error('Cannot use multiple --index-url options');
}
result.indexUrl = value;
// TODO: add to secrets? next PR
}
else {
logger_1.logger.debug({ option }, `pip-compile: option not handled`);
}
continue;
}
if (arg === '--no-emit-index-url') {
result.noEmitIndexUrl = true;
continue;
}
if (arg === '--emit-index-url') {
result.emitIndexUrl = true;
continue;
}
if (arg === '--all-extras') {
result.allExtras = true;
continue;
}
logger_1.logger.debug({ option: arg }, `pip-compile: option not handled`);
}
logger_1.logger.trace({
...result,
}, 'Parsed pip-compile command from header');
if (result.noEmitIndexUrl && result.emitIndexUrl) {
throw new Error('Cannot use both --no-emit-index-url and --emit-index-url');
}
if (result.sourceFiles.length === 0) {
throw new Error('No source files detected in command, pass at least one package file explicitly');
}
return result;
}
const pythonVersionRegex = (0, regex_1.regEx)(/^(#.*?\r?\n)*# This file is autogenerated by pip-compile with Python (?<pythonVersion>\d+(\.\d+)*)\s/, 'i');
function extractPythonVersion(content, fileName) {
const match = pythonVersionRegex.exec(content);
if (match?.groups === undefined) {
logger_1.logger.warn({ fileName, content }, 'pip-compile: failed to extract Python version from header in file');
return undefined;
}
logger_1.logger.trace(`pip-compile: found Python version header in ${fileName}: \n${match[0]}`);
const { pythonVersion } = match.groups;
logger_1.logger.debug({ fileName, pythonVersion }, `pip-compile: extracted Python version from header`);
return pythonVersion;
}
function throwForDisallowedOption(arg) {
if (exports.disallowedPipOptions.includes(arg)) {
throw new Error(`Option ${arg} not allowed for this manager`);
}
}
function throwForNoEqualSignInOptionWithArgument(arg) {
if (exports.optionsWithArguments.includes(arg)) {
throw new Error(`Option ${arg} must have equal sign '=' separating it's argument`);
}
}
function throwForUnknownOption(commandType, arg) {
if (arg.includes('=')) {
const [option] = arg.split('=');
if (exports.allowedOptions[commandType].includes(option)) {
return;
}
}
if (exports.allowedOptions[commandType].includes(arg)) {
return;
}
throw new Error(`Option ${arg} not supported (yet)`);
}
function getRegistryCredEnvVars(url, index) {
const hostRule = hostRules.find({ url: url.href });
logger_1.logger.debug(hostRule, `Found host rule for url ${url.href}`);
const ret = {};
if (!!hostRule.username || !!hostRule.password) {
ret[`KEYRING_SERVICE_NAME_${index}`] = url.hostname;
ret[`KEYRING_SERVICE_USERNAME_${index}`] = hostRule.username ?? '';
ret[`KEYRING_SERVICE_PASSWORD_${index}`] = hostRule.password ?? '';
}
return ret;
}
function cleanUrl(url) {
try {
// Strip everything but protocol, host, and port
const urlObj = new URL(url);
return new URL(urlObj.origin);
}
catch {
return null;
}
}
function getRegistryCredVarsFromPackageFiles(packageFiles) {
const urls = [];
for (const packageFile of packageFiles) {
urls.push(...(packageFile.registryUrls ?? []), ...(packageFile.additionalRegistryUrls ?? []));
}
logger_1.logger.debug(urls, 'Extracted registry URLs from package files');
const uniqueHosts = new Set(urls.map(cleanUrl).filter(array_1.isNotNullOrUndefined));
let allCreds = {};
for (const [index, host] of [...uniqueHosts].entries()) {
const hostCreds = getRegistryCredEnvVars(host, index);
allCreds = {
...allCreds,
...hostCreds,
};
}
return allCreds;
}
function matchManager(filename) {
if (filename.endsWith('setup.py')) {
return 'pip_setup';
}
if (filename.endsWith('setup.cfg')) {
return 'setup-cfg';
}
if (filename.endsWith('pyproject.toml')) {
return 'pep621';
}
// naive, could be improved, maybe use pip_requirements.managerFilePatterns
if (filename.endsWith('.in') || filename.endsWith('.txt')) {
return 'pip_requirements';
}
return 'unknown';
}
//# sourceMappingURL=common.js.map