From f2b6af88da4f1784f65a33febc7704250b64c72e Mon Sep 17 00:00:00 2001 From: Jamie Magee <jamie.magee@gmail.com> Date: Tue, 3 Aug 2021 04:38:46 -0700 Subject: [PATCH] refactor(pypi): convert to class-based (#11064) --- lib/datasource/api.ts | 4 +- lib/datasource/metadata.spec.ts | 8 +- lib/datasource/pypi/index.spec.ts | 3 +- lib/datasource/pypi/index.ts | 434 ++++++++++-------- lib/manager/pip_requirements/extract.ts | 4 +- lib/manager/pip_setup/extract.ts | 4 +- lib/manager/pipenv/extract.ts | 4 +- lib/manager/poetry/extract.ts | 4 +- lib/manager/setup-cfg/extract.ts | 8 +- lib/workers/repository/init/vulnerability.ts | 8 +- .../repository/process/lookup/index.spec.ts | 4 +- 11 files changed, 258 insertions(+), 227 deletions(-) diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts index 9ad37f8fa7..b6c3d51137 100644 --- a/lib/datasource/api.ts +++ b/lib/datasource/api.ts @@ -23,7 +23,7 @@ import * as nuget from './nuget'; import { OrbDatasource } from './orb'; import * as packagist from './packagist'; import * as pod from './pod'; -import * as pypi from './pypi'; +import { PypiDatasource } from './pypi'; import * as repology from './repology'; import { RubyVersionDatasource } from './ruby-version'; import * as rubygems from './rubygems'; @@ -61,7 +61,7 @@ api.set('nuget', nuget); api.set('orb', new OrbDatasource()); api.set('packagist', packagist); api.set('pod', pod); -api.set('pypi', pypi); +api.set('pypi', new PypiDatasource()); api.set('repology', repology); api.set('ruby-version', new RubyVersionDatasource()); api.set('rubygems', rubygems); diff --git a/lib/datasource/metadata.spec.ts b/lib/datasource/metadata.spec.ts index c630202485..76b1a1ee3b 100644 --- a/lib/datasource/metadata.spec.ts +++ b/lib/datasource/metadata.spec.ts @@ -2,7 +2,7 @@ import { getName } from '../../test/util'; import * as datasourceMaven from './maven'; import { addMetaData } from './metadata'; import * as datasourceNpm from './npm'; -import * as datasourcePypi from './pypi'; +import { PypiDatasource } from './pypi'; describe(getName(), () => { it('Should do nothing if dep is not specified', () => { @@ -22,7 +22,7 @@ describe(getName(), () => { ], }; - const datasource = datasourcePypi.id; + const datasource = PypiDatasource.id; const lookupName = 'django'; addMetaData(dep, datasource, lookupName); @@ -42,7 +42,7 @@ describe(getName(), () => { ], }; - const datasource = datasourcePypi.id; + const datasource = PypiDatasource.id; const lookupName = 'mkdocs'; addMetaData(dep, datasource, lookupName); @@ -62,7 +62,7 @@ describe(getName(), () => { { version: '2.2.0', releaseTimestamp: '2019-07-16T18:29:00.000Z' }, ], }; - const datasource = datasourcePypi.id; + const datasource = PypiDatasource.id; const lookupName = 'django-filter'; addMetaData(dep, datasource, lookupName); diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts index c635b07517..4e06b1316e 100644 --- a/lib/datasource/pypi/index.spec.ts +++ b/lib/datasource/pypi/index.spec.ts @@ -2,7 +2,7 @@ import { getPkgReleases } from '..'; import * as httpMock from '../../../test/http-mock'; import { getName, loadFixture } from '../../../test/util'; import * as hostRules from '../../util/host-rules'; -import { id as datasource } from '.'; +import { PypiDatasource } from '.'; const res1: any = loadFixture('azure-cli-monitor.json'); const res2: any = loadFixture('azure-cli-monitor-updated.json'); @@ -14,6 +14,7 @@ const dataRequiresPythonResponse = loadFixture( const mixedHyphensResponse = loadFixture('versions-html-mixed-hyphens.html'); const baseUrl = 'https://pypi.org/pypi'; +const datasource = PypiDatasource.id; describe(getName(), () => { describe('getReleases', () => { diff --git a/lib/datasource/pypi/index.ts b/lib/datasource/pypi/index.ts index f68d0afe26..0ca3756a52 100644 --- a/lib/datasource/pypi/index.ts +++ b/lib/datasource/pypi/index.ts @@ -2,240 +2,266 @@ import url from 'url'; import changelogFilenameRegex from 'changelog-filename-regex'; import { logger } from '../../logger'; import { parse } from '../../util/html'; -import { Http } from '../../util/http'; import { ensureTrailingSlash } from '../../util/url'; import * as pep440 from '../../versioning/pep440'; +import { Datasource } from '../datasource'; import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; import type { PypiJSON, PypiJSONRelease, Releases } from './types'; -export const id = 'pypi'; -export const customRegistrySupport = true; -export const defaultRegistryUrls = [ - process.env.PIP_INDEX_URL || 'https://pypi.org/pypi/', -]; -export const defaultVersioning = pep440.id; -export const registryStrategy = 'merge'; -export const caching = true; - const githubRepoPattern = /^https?:\/\/github\.com\/[^\\/]+\/[^\\/]+$/; -const http = new Http(id); -function normalizeName(input: string): string { - return input.toLowerCase().replace(/(-|\.)/g, '_'); -} +export class PypiDatasource extends Datasource { + static readonly id = 'pypi'; -async function getDependency( - packageName: string, - hostUrl: string -): Promise<ReleaseResult | null> { - const lookupUrl = url.resolve(hostUrl, `${packageName}/json`); - const dependency: ReleaseResult = { releases: null }; - logger.trace({ lookupUrl }, 'Pypi api got lookup'); - const rep = await http.getJson<PypiJSON>(lookupUrl); - const dep = rep?.body; - if (!dep) { - logger.trace({ dependency: packageName }, 'pip package not found'); - return null; - } - if (rep.authorization) { - dependency.isPrivate = true; - } - logger.trace({ lookupUrl }, 'Got pypi api result'); - if ( - !(dep.info && normalizeName(dep.info.name) === normalizeName(packageName)) - ) { - logger.warn( - { lookupUrl, lookupName: packageName, returnedName: dep.info.name }, - 'Returned name does not match with requested name' - ); - return null; + constructor() { + super(PypiDatasource.id); } - if (dep.info?.home_page) { - dependency.homepage = dep.info.home_page; - if (githubRepoPattern.exec(dep.info.home_page)) { - dependency.sourceUrl = dep.info.home_page.replace('http://', 'https://'); - } - } + override readonly caching = true; - if (dep.info?.project_urls) { - for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) { - const lower = name.toLowerCase(); - - if ( - !dependency.sourceUrl && - (lower.startsWith('repo') || - lower === 'code' || - lower === 'source' || - githubRepoPattern.exec(projectUrl)) - ) { - dependency.sourceUrl = projectUrl; - } + override readonly customRegistrySupport = true; - if ( - !dependency.changelogUrl && - ([ - 'changelog', - 'change log', - 'changes', - 'release notes', - 'news', - "what's new", - ].includes(lower) || - changelogFilenameRegex.exec(lower)) - ) { - // from https://github.com/pypa/warehouse/blob/418c7511dc367fb410c71be139545d0134ccb0df/warehouse/templates/packaging/detail.html#L24 - dependency.changelogUrl = projectUrl; + override readonly defaultRegistryUrls = [ + process.env.PIP_INDEX_URL || 'https://pypi.org/pypi/', + ]; + + override readonly defaultVersioning = pep440.id; + + override readonly registryStrategy = 'merge'; + + async getReleases({ + lookupName, + registryUrl, + }: GetReleasesConfig): Promise<ReleaseResult | null> { + let dependency: ReleaseResult = null; + const hostUrl = ensureTrailingSlash(registryUrl); + + // not all simple indexes use this identifier, but most do + if (hostUrl.endsWith('/simple/') || hostUrl.endsWith('/+simple/')) { + logger.trace( + { lookupName, hostUrl }, + 'Looking up pypi simple dependency' + ); + dependency = await this.getSimpleDependency(lookupName, hostUrl); + } else { + logger.trace({ lookupName, hostUrl }, 'Looking up pypi api dependency'); + try { + // we need to resolve early here so we can catch any 404s and fallback to a simple lookup + dependency = await this.getDependency(lookupName, hostUrl); + } catch (err) { + if (err.statusCode !== 404) { + throw err; + } + + // error contacting json-style api -- attempt to fallback to a simple-style api + logger.trace( + { lookupName, hostUrl }, + 'Looking up pypi simple dependency via fallback' + ); + dependency = await this.getSimpleDependency(lookupName, hostUrl); } } + return dependency; } - dependency.releases = []; - if (dep.releases) { - const versions = Object.keys(dep.releases); - dependency.releases = versions.map((version) => { - const releases = dep.releases[version] || []; - const { upload_time: releaseTimestamp } = releases[0] || {}; - const isDeprecated = releases.some(({ yanked }) => yanked); - const result: Release = { - version, - releaseTimestamp, - }; - if (isDeprecated) { - result.isDeprecated = isDeprecated; - } - // There may be multiple releases with different requires_python, so we return all in an array - result.constraints = { - python: releases.map(({ requires_python }) => requires_python), - }; - return result; - }); + private static normalizeName(input: string): string { + return input.toLowerCase().replace(/(-|\.)/g, '_'); } - return dependency; -} -function extractVersionFromLinkText( - text: string, - packageName: string -): string | null { - const srcPrefixes = [`${packageName}-`, `${packageName.replace(/-/g, '_')}-`]; - for (const prefix of srcPrefixes) { - const suffix = '.tar.gz'; - if (text.startsWith(prefix) && text.endsWith(suffix)) { - return text.replace(prefix, '').replace(/\.tar\.gz$/, ''); + private async getDependency( + packageName: string, + hostUrl: string + ): Promise<ReleaseResult | null> { + const lookupUrl = url.resolve(hostUrl, `${packageName}/json`); + const dependency: ReleaseResult = { releases: null }; + logger.trace({ lookupUrl }, 'Pypi api got lookup'); + const rep = await this.http.getJson<PypiJSON>(lookupUrl); + const dep = rep?.body; + if (!dep) { + logger.trace({ dependency: packageName }, 'pip package not found'); + return null; + } + if (rep.authorization) { + dependency.isPrivate = true; + } + logger.trace({ lookupUrl }, 'Got pypi api result'); + if ( + !( + dep.info && + PypiDatasource.normalizeName(dep.info.name) === + PypiDatasource.normalizeName(packageName) + ) + ) { + logger.warn( + { lookupUrl, lookupName: packageName, returnedName: dep.info.name }, + 'Returned name does not match with requested name' + ); + return null; } - } - // pep-0427 wheel packages - // {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl. - const wheelPrefix = packageName.replace(/[^\w\d.]+/g, '_') + '-'; - const wheelSuffix = '.whl'; - if ( - text.startsWith(wheelPrefix) && - text.endsWith(wheelSuffix) && - text.split('-').length > 2 - ) { - return text.split('-')[1]; - } + if (dep.info?.home_page) { + dependency.homepage = dep.info.home_page; + if (githubRepoPattern.exec(dep.info.home_page)) { + dependency.sourceUrl = dep.info.home_page.replace( + 'http://', + 'https://' + ); + } + } - return null; -} + if (dep.info?.project_urls) { + for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) { + const lower = name.toLowerCase(); -function cleanSimpleHtml(html: string): string { - return ( - html - .replace(/<\/?pre>/, '') - // Certain simple repositories like artifactory don't escape > and < - .replace( - /data-requires-python="([^"]*?)>([^"]*?)"/g, - 'data-requires-python="$1>$2"' - ) - .replace( - /data-requires-python="([^"]*?)<([^"]*?)"/g, - 'data-requires-python="$1<$2"' - ) - ); -} + if ( + !dependency.sourceUrl && + (lower.startsWith('repo') || + lower === 'code' || + lower === 'source' || + githubRepoPattern.exec(projectUrl)) + ) { + dependency.sourceUrl = projectUrl; + } -async function getSimpleDependency( - packageName: string, - hostUrl: string -): Promise<ReleaseResult | null> { - const lookupUrl = url.resolve(hostUrl, ensureTrailingSlash(packageName)); - const dependency: ReleaseResult = { releases: null }; - const response = await http.get(lookupUrl); - const dep = response?.body; - if (!dep) { - logger.trace({ dependency: packageName }, 'pip package not found'); - return null; - } - if (response.authorization) { - dependency.isPrivate = true; - } - const root = parse(cleanSimpleHtml(dep)); - const links = root.querySelectorAll('a'); - const releases: Releases = {}; - for (const link of Array.from(links)) { - const version = extractVersionFromLinkText(link.text, packageName); - if (version) { - const release: PypiJSONRelease = { - yanked: link.hasAttribute('data-yanked'), - }; - const requiresPython = link.getAttribute('data-requires-python'); - if (requiresPython) { - release.requires_python = requiresPython; - } - if (!releases[version]) { - releases[version] = []; + if ( + !dependency.changelogUrl && + ([ + 'changelog', + 'change log', + 'changes', + 'release notes', + 'news', + "what's new", + ].includes(lower) || + changelogFilenameRegex.exec(lower)) + ) { + // from https://github.com/pypa/warehouse/blob/418c7511dc367fb410c71be139545d0134ccb0df/warehouse/templates/packaging/detail.html#L24 + dependency.changelogUrl = projectUrl; + } } - releases[version].push(release); } - } - const versions = Object.keys(releases); - dependency.releases = versions.map((version) => { - const versionReleases = releases[version] || []; - const isDeprecated = versionReleases.some(({ yanked }) => yanked); - const result: Release = { version }; - if (isDeprecated) { - result.isDeprecated = isDeprecated; + + dependency.releases = []; + if (dep.releases) { + const versions = Object.keys(dep.releases); + dependency.releases = versions.map((version) => { + const releases = dep.releases[version] || []; + const { upload_time: releaseTimestamp } = releases[0] || {}; + const isDeprecated = releases.some(({ yanked }) => yanked); + const result: Release = { + version, + releaseTimestamp, + }; + if (isDeprecated) { + result.isDeprecated = isDeprecated; + } + // There may be multiple releases with different requires_python, so we return all in an array + result.constraints = { + python: releases.map(({ requires_python }) => requires_python), + }; + return result; + }); } - // There may be multiple releases with different requires_python, so we return all in an array - result.constraints = { - python: versionReleases.map(({ requires_python }) => requires_python), - }; - return result; - }); - return dependency; -} + return dependency; + } -export async function getReleases({ - lookupName, - registryUrl, -}: GetReleasesConfig): Promise<ReleaseResult | null> { - let dependency: ReleaseResult = null; - const hostUrl = ensureTrailingSlash(registryUrl); - - // not all simple indexes use this identifier, but most do - if (hostUrl.endsWith('/simple/') || hostUrl.endsWith('/+simple/')) { - logger.trace({ lookupName, hostUrl }, 'Looking up pypi simple dependency'); - dependency = await getSimpleDependency(lookupName, hostUrl); - } else { - logger.trace({ lookupName, hostUrl }, 'Looking up pypi api dependency'); - try { - // we need to resolve early here so we can catch any 404s and fallback to a simple lookup - dependency = await getDependency(lookupName, hostUrl); - } catch (err) { - if (err.statusCode !== 404) { - throw err; + private static extractVersionFromLinkText( + text: string, + packageName: string + ): string | null { + const srcPrefixes = [ + `${packageName}-`, + `${packageName.replace(/-/g, '_')}-`, + ]; + for (const prefix of srcPrefixes) { + const suffix = '.tar.gz'; + if (text.startsWith(prefix) && text.endsWith(suffix)) { + return text.replace(prefix, '').replace(/\.tar\.gz$/, ''); } + } - // error contacting json-style api -- attempt to fallback to a simple-style api - logger.trace( - { lookupName, hostUrl }, - 'Looking up pypi simple dependency via fallback' + // pep-0427 wheel packages + // {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl. + const wheelPrefix = packageName.replace(/[^\w\d.]+/g, '_') + '-'; + const wheelSuffix = '.whl'; + if ( + text.startsWith(wheelPrefix) && + text.endsWith(wheelSuffix) && + text.split('-').length > 2 + ) { + return text.split('-')[1]; + } + + return null; + } + + private static cleanSimpleHtml(html: string): string { + return ( + html + .replace(/<\/?pre>/, '') + // Certain simple repositories like artifactory don't escape > and < + .replace( + /data-requires-python="([^"]*?)>([^"]*?)"/g, + 'data-requires-python="$1>$2"' + ) + .replace( + /data-requires-python="([^"]*?)<([^"]*?)"/g, + 'data-requires-python="$1<$2"' + ) + ); + } + + private async getSimpleDependency( + packageName: string, + hostUrl: string + ): Promise<ReleaseResult | null> { + const lookupUrl = url.resolve(hostUrl, ensureTrailingSlash(packageName)); + const dependency: ReleaseResult = { releases: null }; + const response = await this.http.get(lookupUrl); + const dep = response?.body; + if (!dep) { + logger.trace({ dependency: packageName }, 'pip package not found'); + return null; + } + if (response.authorization) { + dependency.isPrivate = true; + } + const root = parse(PypiDatasource.cleanSimpleHtml(dep)); + const links = root.querySelectorAll('a'); + const releases: Releases = {}; + for (const link of Array.from(links)) { + const version = PypiDatasource.extractVersionFromLinkText( + link.text, + packageName ); - dependency = await getSimpleDependency(lookupName, hostUrl); + if (version) { + const release: PypiJSONRelease = { + yanked: link.hasAttribute('data-yanked'), + }; + const requiresPython = link.getAttribute('data-requires-python'); + if (requiresPython) { + release.requires_python = requiresPython; + } + if (!releases[version]) { + releases[version] = []; + } + releases[version].push(release); + } } + const versions = Object.keys(releases); + dependency.releases = versions.map((version) => { + const versionReleases = releases[version] || []; + const isDeprecated = versionReleases.some(({ yanked }) => yanked); + const result: Release = { version }; + if (isDeprecated) { + result.isDeprecated = isDeprecated; + } + // There may be multiple releases with different requires_python, so we return all in an array + result.constraints = { + python: versionReleases.map(({ requires_python }) => requires_python), + }; + return result; + }); + return dependency; } - return dependency; } diff --git a/lib/manager/pip_requirements/extract.ts b/lib/manager/pip_requirements/extract.ts index f5e357bbea..d225a7790a 100644 --- a/lib/manager/pip_requirements/extract.ts +++ b/lib/manager/pip_requirements/extract.ts @@ -1,7 +1,7 @@ // based on https://www.python.org/dev/peps/pep-0508/#names import { RANGE_PATTERN } from '@renovate/pep440/lib/specifier'; import { getAdminConfig } from '../../config/admin'; -import * as datasourcePypi from '../../datasource/pypi'; +import { PypiDatasource } from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { isSkipComment } from '../../util/ignore'; @@ -69,7 +69,7 @@ export function extractPackageFile( ...dep, depName, currentValue, - datasource: datasourcePypi.id, + datasource: PypiDatasource.id, }; if (currentValue?.startsWith('==')) { dep.currentVersion = currentValue.replace(/^==\s*/, ''); diff --git a/lib/manager/pip_setup/extract.ts b/lib/manager/pip_setup/extract.ts index 762ff892c0..ddcfb0c111 100644 --- a/lib/manager/pip_setup/extract.ts +++ b/lib/manager/pip_setup/extract.ts @@ -1,5 +1,5 @@ import { getAdminConfig } from '../../config/admin'; -import * as datasourcePypi from '../../datasource/pypi'; +import { PypiDatasource } from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { exec } from '../../util/exec'; @@ -122,7 +122,7 @@ export async function extractPackageFile( depName, currentValue, managerData: { lineNumber }, - datasource: datasourcePypi.id, + datasource: PypiDatasource.id, }; return dep; }) diff --git a/lib/manager/pipenv/extract.ts b/lib/manager/pipenv/extract.ts index ddd9107ded..9c6cbe5361 100644 --- a/lib/manager/pipenv/extract.ts +++ b/lib/manager/pipenv/extract.ts @@ -1,7 +1,7 @@ import toml from '@iarna/toml'; import { RANGE_PATTERN } from '@renovate/pep440/lib/specifier'; import is from '@sindresorhus/is'; -import * as datasourcePypi from '../../datasource/pypi'; +import { PypiDatasource } from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { localPathExists } from '../../util/fs'; @@ -77,7 +77,7 @@ function extractFromSection( if (skipReason) { dep.skipReason = skipReason; } else { - dep.datasource = datasourcePypi.id; + dep.datasource = PypiDatasource.id; } if (nestedVersion) { dep.managerData.nestedVersion = nestedVersion; diff --git a/lib/manager/poetry/extract.ts b/lib/manager/poetry/extract.ts index d57ad2c9c5..b957234a62 100644 --- a/lib/manager/poetry/extract.ts +++ b/lib/manager/poetry/extract.ts @@ -1,6 +1,6 @@ import { parse } from '@iarna/toml'; import is from '@sindresorhus/is'; -import * as datasourcePypi from '../../datasource/pypi'; +import { PypiDatasource } from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; import { @@ -65,7 +65,7 @@ function extractFromSection( depType: section, currentValue: currentValue as string, managerData: { nestedVersion }, - datasource: datasourcePypi.id, + datasource: PypiDatasource.id, }; if (dep.depName in poetryLockfile) { dep.lockedVersion = poetryLockfile[dep.depName].version; diff --git a/lib/manager/setup-cfg/extract.ts b/lib/manager/setup-cfg/extract.ts index 6db1579443..3437f0efbe 100644 --- a/lib/manager/setup-cfg/extract.ts +++ b/lib/manager/setup-cfg/extract.ts @@ -1,4 +1,4 @@ -import { id as datasource } from '../../datasource/pypi'; +import { PypiDatasource } from '../../datasource/pypi'; import pep440 from '../../versioning/pep440'; import type { PackageDependency, PackageFile, Result } from '../types'; @@ -41,7 +41,11 @@ function parseDep( currentValue && pep440.isValid(currentValue) ) { - const dep: PackageDependency = { datasource, depName, currentValue }; + const dep: PackageDependency = { + datasource: PypiDatasource.id, + depName, + currentValue, + }; const depType = getDepType(section, record); if (depType) { dep.depType = depType; diff --git a/lib/workers/repository/init/vulnerability.ts b/lib/workers/repository/init/vulnerability.ts index ba0bdab0c6..4bd4a5e871 100644 --- a/lib/workers/repository/init/vulnerability.ts +++ b/lib/workers/repository/init/vulnerability.ts @@ -3,7 +3,7 @@ import { NO_VULNERABILITY_ALERTS } from '../../../constants/error-messages'; import * as datasourceMaven from '../../../datasource/maven'; import { id as npmId } from '../../../datasource/npm'; import * as datasourceNuget from '../../../datasource/nuget'; -import * as datasourcePypi from '../../../datasource/pypi'; +import { PypiDatasource } from '../../../datasource/pypi'; import * as datasourceRubygems from '../../../datasource/rubygems'; import { logger } from '../../../logger'; import { platform } from '../../../platform'; @@ -89,7 +89,7 @@ export async function detectVulnerabilityAlerts( MAVEN: datasourceMaven.id, NPM: npmId, NUGET: datasourceNuget.id, - PIP: datasourcePypi.id, + PIP: PypiDatasource.id, RUBYGEMS: datasourceRubygems.id, }; const datasource = @@ -109,7 +109,7 @@ export async function detectVulnerabilityAlerts( vulnerableRequirements = `< ${firstPatchedVersion}`; } } - if (datasource === datasourcePypi.id) { + if (datasource === PypiDatasource.id) { vulnerableRequirements = vulnerableRequirements.replace(/^= /, '== '); } combinedAlerts[fileName] ||= {}; @@ -179,7 +179,7 @@ export async function detectVulnerabilityAlerts( logger.warn({ err }, 'Error generating vulnerability PR notes'); } const allowedVersions = - datasource === datasourcePypi.id + datasource === PypiDatasource.id ? `==${val.firstPatchedVersion}` : val.firstPatchedVersion; let matchRule: PackageRule = { diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts index d1839fba20..23a0b92142 100644 --- a/lib/workers/repository/process/lookup/index.spec.ts +++ b/lib/workers/repository/process/lookup/index.spec.ts @@ -15,7 +15,7 @@ import * as datasourceGithubReleases from '../../../../datasource/github-release import { id as datasourceGithubTagsId } from '../../../../datasource/github-tags'; import { id as datasourceNpmId } from '../../../../datasource/npm'; import { id as datasourcePackagistId } from '../../../../datasource/packagist'; -import { id as datasourcePypiId } from '../../../../datasource/pypi'; +import { PypiDatasource } from '../../../../datasource/pypi'; import { id as dockerVersioningId } from '../../../../versioning/docker'; import { id as gitVersioningId } from '../../../../versioning/git'; import { id as npmVersioningId } from '../../../../versioning/npm'; @@ -1011,7 +1011,7 @@ describe(getName(), () => { }); it('handles pypi 404', async () => { config.depName = 'foo'; - config.datasource = datasourcePypiId; + config.datasource = PypiDatasource.id; config.packageFile = 'requirements.txt'; config.currentValue = '1.0.0'; httpMock -- GitLab