From e80f8848f91bb9425a728889a89ea6a015eee544 Mon Sep 17 00:00:00 2001
From: t-kulmburg <89128736+t-kulmburg@users.noreply.github.com>
Date: Fri, 24 Sep 2021 11:33:25 +0200
Subject: [PATCH] feat(go): support go modules in gitlab subgroups (#11540)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../go/__snapshots__/index.spec.ts.snap       | 40 ++++++++++++++++++
 lib/datasource/go/index.spec.ts               | 32 ++++++++++++++
 lib/datasource/go/index.ts                    | 42 ++++++++++++++-----
 3 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/lib/datasource/go/__snapshots__/index.spec.ts.snap b/lib/datasource/go/__snapshots__/index.spec.ts.snap
index 627971757f..ee720d8d48 100644
--- a/lib/datasource/go/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/go/__snapshots__/index.spec.ts.snap
@@ -689,6 +689,46 @@ Array [
 ]
 `;
 
+exports[`datasource/go/index getReleases support gitlab subgroups 1`] = `
+Object {
+  "releases": Array [
+    Object {
+      "gitRef": "v1.0.0",
+      "version": "v1.0.0",
+    },
+    Object {
+      "gitRef": "v2.0.0",
+      "version": "v2.0.0",
+    },
+  ],
+  "sourceUrl": "https://gitlab.com/group/subgroup/repo",
+}
+`;
+
+exports[`datasource/go/index getReleases support gitlab subgroups 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate, br",
+      "host": "gitlab.com",
+      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/group/subgroup/repo?go-get=1",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate, br",
+      "host": "gitlab.com",
+      "user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/group%2Fsubgroup%2Frepo/repository/tags?per_page=100",
+  },
+]
+`;
+
 exports[`datasource/go/index getReleases support self hosted gitlab private repositories 1`] = `
 Object {
   "releases": Array [
diff --git a/lib/datasource/go/index.spec.ts b/lib/datasource/go/index.spec.ts
index 2ec6448601..68be8a79a4 100644
--- a/lib/datasource/go/index.spec.ts
+++ b/lib/datasource/go/index.spec.ts
@@ -21,6 +21,18 @@ Nothing to see here; <a href="https://godoc.org/golang.org/x/text">move along</a
 </body>
 </html>`;
 
+const res2 = `<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="go-import" content="gitlab.com/group/subgroup git https://gitlab.com/group/subgroup/repo">
+<meta name="go-source" content="gitlab.com/group/subgroup https://gitlab.com/group/subgroup https://gitlab.com/group/subgroup/-/tree/master{/dir} https://gitlab.com/group/subgroup/-/blob/master{/dir}/{file}#L{line}">
+</head>
+<body>
+go get https://gitlab.com/group/subgroup
+</body>
+</html>`;
+
 const resGitLabEE = `<html>
 <head>
 	<meta name="go-import" content="my.custom.domain/golang/myrepo git https://my.custom.domain/golang/myrepo.git" />
@@ -364,6 +376,26 @@ describe('datasource/go/index', () => {
       expect(httpCalls).toHaveLength(6);
       expect(httpCalls).toMatchSnapshot();
     });
+    it('support gitlab subgroups', async () => {
+      httpMock
+        .scope('https://gitlab.com/')
+        .get('/group/subgroup/repo?go-get=1')
+        .reply(200, res2);
+      httpMock
+        .scope('https://gitlab.com/')
+        .get(
+          '/api/v4/projects/group%2Fsubgroup%2Frepo/repository/tags?per_page=100'
+        )
+        .reply(200, [{ name: 'v1.0.0' }, { name: 'v2.0.0' }]);
+      const res = await getPkgReleases({
+        datasource,
+        depName: 'gitlab.com/group/subgroup/repo',
+      });
+      expect(res).toMatchSnapshot();
+      expect(res).not.toBeNull();
+      expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
     it('works for nested modules on github', async () => {
       const packages = [
         { datasource, depName: 'github.com/x/text/a' },
diff --git a/lib/datasource/go/index.ts b/lib/datasource/go/index.ts
index 7dd40a0577..4936c3a721 100644
--- a/lib/datasource/go/index.ts
+++ b/lib/datasource/go/index.ts
@@ -16,7 +16,9 @@ export { id } from './common';
 
 export const customRegistrySupport = false;
 
-const gitlabRegExp = /^(https:\/\/[^/]*gitlab\.[^/]*)\/(.*)$/;
+const gitlabHttpsRegExp =
+  /^(?<httpsRegExpUrl>https:\/\/[^/]*gitlab\.[^/]*)\/(?<httpsRegExpName>.+?)[/]?$/;
+const gitlabRegExp = /^(?<regExpUrl>gitlab\.[^/]*)\/(?<regExpPath>.+?)[/]?$/;
 const bitbucket = new BitBucketTagsDatasource();
 
 async function getDatasource(goModule: string): Promise<DataSource | null> {
@@ -68,12 +70,24 @@ async function getDatasource(goModule: string): Promise<DataSource | null> {
           .replace(/\/$/, ''),
       };
     }
-    const gitlabRes = gitlabRegExp.exec(goSourceUrl);
-    if (gitlabRes) {
+    const gitlabUrl =
+      gitlabHttpsRegExp.exec(goSourceUrl)?.groups?.httpsRegExpUrl;
+    const gitlabUrlName =
+      gitlabHttpsRegExp.exec(goSourceUrl)?.groups?.httpsRegExpName;
+    const gitlabModuleName = gitlabRegExp.exec(goModule)?.groups?.regExpPath;
+
+    if (gitlabUrl && gitlabUrlName) {
+      if (gitlabModuleName?.startsWith(gitlabUrlName)) {
+        return {
+          datasource: gitlab.id,
+          registryUrl: gitlabUrl,
+          lookupName: gitlabModuleName,
+        };
+      }
       return {
         datasource: gitlab.id,
-        registryUrl: gitlabRes[1],
-        lookupName: gitlabRes[2].replace(/\/$/, ''),
+        registryUrl: gitlabUrl,
+        lookupName: gitlabUrlName,
       };
     }
 
@@ -199,7 +213,7 @@ export async function getReleases(
   const nameParts = lookupName.replace(/\/v\d+$/, '').split('/');
   logger.trace({ nameParts, releases: res.releases }, 'go.getReleases');
 
-  // If it has more than 3 parts it's a submodule
+  // If it has more than 3 parts it's a submodule or subgroup (gitlab only)
   if (nameParts.length > 3) {
     const prefix = nameParts.slice(3, nameParts.length).join('/');
     logger.trace(`go.getReleases.prefix:${prefix}`);
@@ -215,10 +229,18 @@ export async function getReleases(
       });
     logger.trace({ submodReleases }, 'go.getReleases');
 
-    return {
-      sourceUrl: res.sourceUrl,
-      releases: submodReleases,
-    };
+    // If not from gitlab -> no subgroups -> must be submodule
+    // If from gitlab and directory one level above has tags -> has to be submodule, since groups can't have tags
+    // If not, it's simply a repo in a subfolder, and the normal tags are used.
+    if (
+      !(source.datasource === gitlab.id) ||
+      (source.datasource === gitlab.id && submodReleases.length)
+    ) {
+      return {
+        sourceUrl: res.sourceUrl,
+        releases: submodReleases,
+      };
+    }
   }
 
   if (res.releases) {
-- 
GitLab