diff --git a/lib/manager/pip_requirements/__fixtures__/requirements-git-packages.txt b/lib/manager/pip_requirements/__fixtures__/requirements-git-packages.txt new file mode 100644 index 0000000000000000000000000000000000000000..644b5b47d6f808ee27ed7534f66e2e28adf410dd --- /dev/null +++ b/lib/manager/pip_requirements/__fixtures__/requirements-git-packages.txt @@ -0,0 +1,4 @@ +git+ssh://git@github.com/rwxd/python-pip-setup-test.git@v1.1.0 +git+ssh://git@github.com/rwxd/test_package@1.0.0 +git+ssh://git@gitlab.company.com/rwxd/python-package.git@abcde +git+https://peter@github.com/rwxd/python-pip-setup-test.git@v0.9.0 diff --git a/lib/manager/pip_requirements/extract.spec.ts b/lib/manager/pip_requirements/extract.spec.ts index fce32b69a024f67eca8688feb99d95c3f86a3ed9..53c23ebc7f33f117534908a4cf983610114d9722 100644 --- a/lib/manager/pip_requirements/extract.spec.ts +++ b/lib/manager/pip_requirements/extract.spec.ts @@ -11,6 +11,7 @@ const requirements6 = loadFixture('requirements6.txt'); const requirements7 = loadFixture('requirements7.txt'); const requirements8 = loadFixture('requirements8.txt'); const requirementsWithEnvMarkers = loadFixture('requirements-env-markers.txt'); +const requirementsGitPackages = loadFixture('requirements-git-packages.txt'); describe('manager/pip_requirements/extract', () => { beforeEach(() => { @@ -159,5 +160,45 @@ describe('manager/pip_requirements/extract', () => { ], }); }); + it('should handle git packages', () => { + const res = extractPackageFile( + requirementsGitPackages, + 'unused_file_name', + {} + ); + expect(res.deps).toHaveLength(4); + expect(res).toEqual({ + deps: [ + { + depName: 'python-pip-setup-test', + currentValue: 'v1.1.0', + currentVersion: 'v1.1.0', + lookupName: 'git@github.com:rwxd/python-pip-setup-test.git', + datasource: 'git-tags', + }, + { + depName: 'test_package', + currentValue: '1.0.0', + currentVersion: '1.0.0', + lookupName: 'git@github.com:rwxd/test_package', + datasource: 'git-tags', + }, + { + depName: 'python-package', + currentValue: 'abcde', + currentVersion: 'abcde', + lookupName: 'git@gitlab.company.com:rwxd/python-package.git', + datasource: 'git-tags', + }, + { + depName: 'python-pip-setup-test', + currentValue: 'v0.9.0', + currentVersion: 'v0.9.0', + lookupName: 'peter@github.com:rwxd/python-pip-setup-test.git', + datasource: 'git-tags', + }, + ], + }); + }); }); }); diff --git a/lib/manager/pip_requirements/extract.ts b/lib/manager/pip_requirements/extract.ts index 7806601cdb1907fdc45de68f3788925cf251973f..14b5d425be6bd2c05e63235cbd2971ade52d0e15 100644 --- a/lib/manager/pip_requirements/extract.ts +++ b/lib/manager/pip_requirements/extract.ts @@ -1,6 +1,7 @@ // based on https://www.python.org/dev/peps/pep-0508/#names import { RANGE_PATTERN } from '@renovatebot/pep440'; import { GlobalConfig } from '../../config/global'; +import { GitTagsDatasource } from '../../datasource/git-tags'; import { PypiDatasource } from '../../datasource/pypi'; import { logger } from '../../logger'; import { SkipReason } from '../../types'; @@ -11,6 +12,9 @@ import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; export const packagePattern = '[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]'; const extrasPattern = '(?:\\s*\\[[^\\]]+\\])?'; +const packageGitRegex = regEx( + /(?<source>(?:git\+)(git|ssh|https):\/\/(?<gitUrl>(?<user>.*)@(?<hostname>[\w.-]+)(?<delimiter>\/)(?<scmPath>.*\/(?<depName>[\w/-]+))(\.git)?(?:@(?<version>.*))))/ +); const rangePattern: string = RANGE_PATTERN; const specifierPartPattern = `\\s*${rangePattern.replace( @@ -65,12 +69,33 @@ export function extractPackageFile( } const [lineNoEnvMarkers] = line.split(';').map((part) => part.trim()); const lineNoHashes = lineNoEnvMarkers.split(' \\')[0]; - const matches = + const packageMatches = pkgValRegex.exec(lineNoHashes) || pkgRegex.exec(lineNoHashes); - if (!matches) { + const gitPackageMatches = packageGitRegex.exec(lineNoHashes); + if (!packageMatches && !gitPackageMatches) { return null; } - const [, depName, , currVal] = matches; + if (gitPackageMatches) { + const currentVersion = gitPackageMatches.groups.version; + const depName = gitPackageMatches.groups.depName; + + // we need to replace the / with a : + const scmPath = gitPackageMatches.groups.scmPath; + const delimiter = gitPackageMatches.groups.delimiter; + const lookupName = gitPackageMatches.groups.gitUrl + .replace(`${delimiter}${scmPath}`, `:${scmPath}`) + .replace(`@${currentVersion}`, ''); + dep = { + ...dep, + depName, + currentValue: currentVersion, + currentVersion: currentVersion, + lookupName: lookupName, + datasource: GitTagsDatasource.id, + }; + return dep; + } + const [, depName, , currVal] = packageMatches; const currentValue = currVal?.trim(); dep = { ...dep,