From 150d01a4bbda47d45db0e1e04c1143f661b2fe05 Mon Sep 17 00:00:00 2001
From: rwxd <rwxd@pm.me>
Date: Tue, 18 Jan 2022 22:18:48 +0100
Subject: [PATCH] feat(pip_requirements): added support for packages from a git
 repository (#13414)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../requirements-git-packages.txt             |  4 ++
 lib/manager/pip_requirements/extract.spec.ts  | 41 +++++++++++++++++++
 lib/manager/pip_requirements/extract.ts       | 31 ++++++++++++--
 3 files changed, 73 insertions(+), 3 deletions(-)
 create mode 100644 lib/manager/pip_requirements/__fixtures__/requirements-git-packages.txt

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 0000000000..644b5b47d6
--- /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 fce32b69a0..53c23ebc7f 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 7806601cdb..14b5d425be 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,
-- 
GitLab