UNPKG

renovate

Version:

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

327 lines • 11.9 kB
"use strict"; 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