From 773a111c4987b9ab73cfe7f1033340857df52416 Mon Sep 17 00:00:00 2001
From: t-kulmburg <89128736+t-kulmburg@users.noreply.github.com>
Date: Mon, 6 Sep 2021 19:49:04 +0200
Subject: [PATCH] feat(gradle): support @<ext> file extentions (#11541)

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/manager/gradle/shallow/extract.spec.ts | 75 ++++++++++++++++++++++
 lib/manager/gradle/shallow/parser.ts       | 32 ++++-----
 lib/manager/gradle/shallow/utils.ts        | 28 +++++++-
 lib/manager/types.ts                       |  1 +
 4 files changed, 119 insertions(+), 17 deletions(-)

diff --git a/lib/manager/gradle/shallow/extract.spec.ts b/lib/manager/gradle/shallow/extract.spec.ts
index 55f36525df..e070e7371f 100644
--- a/lib/manager/gradle/shallow/extract.spec.ts
+++ b/lib/manager/gradle/shallow/extract.spec.ts
@@ -68,6 +68,81 @@ describe('manager/gradle/shallow/extract', () => {
     ]);
   });
 
+  it('works with file-ext', async () => {
+    mockFs({
+      'gradle.properties': '',
+      'build.gradle': 'url "https://example.com"; "foo:bar:1.2.3@zip"',
+      'settings.gradle': null,
+    });
+
+    const res = await extractAllPackageFiles({} as never, [
+      'build.gradle',
+      'gradle.properties',
+      'settings.gradle',
+    ]);
+
+    expect(res).toMatchObject([
+      {
+        packageFile: 'gradle.properties',
+        deps: [],
+      },
+      {
+        packageFile: 'build.gradle',
+        deps: [
+          {
+            depName: 'foo:bar',
+            currentValue: '1.2.3',
+            registryUrls: [
+              'https://repo.maven.apache.org/maven2',
+              'https://example.com',
+            ],
+          },
+        ],
+      },
+      {
+        datasource: 'maven',
+        deps: [],
+        packageFile: 'settings.gradle',
+      },
+    ]);
+  });
+
+  it('works with file-ext-var', async () => {
+    mockFs({
+      'gradle.properties': 'baz=1.2.3',
+      'build.gradle': 'url "https://example.com"; "foo:bar:$baz@zip"',
+      'settings.gradle': null,
+    });
+
+    const res = await extractAllPackageFiles({} as never, [
+      'build.gradle',
+      'gradle.properties',
+      'settings.gradle',
+    ]);
+
+    expect(res).toMatchObject([
+      {
+        packageFile: 'gradle.properties',
+        deps: [
+          {
+            depName: 'foo:bar',
+            currentValue: '1.2.3',
+            registryUrls: [
+              'https://repo.maven.apache.org/maven2',
+              'https://example.com',
+            ],
+          },
+        ],
+      },
+      { packageFile: 'build.gradle', deps: [] },
+      {
+        datasource: 'maven',
+        deps: [],
+        packageFile: 'settings.gradle',
+      },
+    ]);
+  });
+
   it('inherits gradle variables', async () => {
     const fsMock = {
       'gradle.properties': 'foo=1.0.0',
diff --git a/lib/manager/gradle/shallow/parser.ts b/lib/manager/gradle/shallow/parser.ts
index 66628ed66c..b1d16f5b80 100644
--- a/lib/manager/gradle/shallow/parser.ts
+++ b/lib/manager/gradle/shallow/parser.ts
@@ -141,21 +141,21 @@ function processDepInterpolation({
   if (interpolationResult && isDependencyString(interpolationResult)) {
     const dep = parseDependencyString(interpolationResult);
     if (dep) {
-      const lastChild = token.children[token.children.length - 1];
-      const lastChildValue = lastChild?.value;
-      const variable = variables[lastChildValue];
-      if (
-        lastChild?.type === TokenType.Variable &&
-        variable &&
-        variable?.value === dep.currentValue
-      ) {
-        dep.managerData = {
-          fileReplacePosition: variable.fileReplacePosition,
-          packageFile: variable.packageFile,
-        };
-        dep.groupName = variable.key;
-        return { deps: [dep] };
-      }
+      token.children.forEach((child) => {
+        const variable = variables[child.value];
+        if (
+          child?.type === TokenType.Variable &&
+          variable &&
+          variable?.value === dep.currentValue
+        ) {
+          dep.managerData = {
+            fileReplacePosition: variable.fileReplacePosition,
+            packageFile: variable.packageFile,
+          };
+          dep.groupName = variable.key;
+        }
+      });
+      return { deps: [dep] };
     }
   }
   return null;
@@ -273,6 +273,7 @@ const matcherConfigs: SyntaxMatchConfig[] = [
   },
   {
     // 'foo.bar:baz:1.2.3'
+    // 'foo.bar:baz:1.2.3@ext'
     matchers: [
       {
         matchType: TokenType.String,
@@ -283,6 +284,7 @@ const matcherConfigs: SyntaxMatchConfig[] = [
   },
   {
     // "foo.bar:baz:${bazVersion}"
+    // "foo.bar:baz:${bazVersion}@ext"
     matchers: [
       {
         matchType: TokenType.StringInterpolation,
diff --git a/lib/manager/gradle/shallow/utils.ts b/lib/manager/gradle/shallow/utils.ts
index 75bf22405d..e1da8b2a79 100644
--- a/lib/manager/gradle/shallow/utils.ts
+++ b/lib/manager/gradle/shallow/utils.ts
@@ -23,7 +23,23 @@ export function isDependencyString(input: string): boolean {
   if (split?.length !== 3) {
     return false;
   }
-  const [groupId, artifactId, versionPart] = split;
+  // eslint-disable-next-line prefer-const
+  let [tempGroupId, tempArtifactId, tempVersionPart] = split;
+  if (
+    tempVersionPart !== versionLikeSubstring(tempVersionPart) &&
+    tempVersionPart.includes('@')
+  ) {
+    const versionSplit = tempVersionPart?.split('@');
+    if (versionSplit?.length !== 2) {
+      return false;
+    }
+    [tempVersionPart] = versionSplit;
+  }
+  const [groupId, artifactId, versionPart] = [
+    tempGroupId,
+    tempArtifactId,
+    tempVersionPart,
+  ];
   return (
     groupId &&
     artifactId &&
@@ -40,10 +56,18 @@ export function parseDependencyString(
   if (!isDependencyString(input)) {
     return null;
   }
-  const [groupId, artifactId, currentValue] = input?.split(':');
+  const [groupId, artifactId, FullValue] = input?.split(':');
+  if (FullValue === versionLikeSubstring(FullValue)) {
+    return {
+      depName: `${groupId}:${artifactId}`,
+      currentValue: FullValue,
+    };
+  }
+  const [currentValue, dataType] = FullValue?.split('@');
   return {
     depName: `${groupId}:${artifactId}`,
     currentValue,
+    dataType,
   };
 }
 
diff --git a/lib/manager/types.ts b/lib/manager/types.ts
index fb2701cd6c..657a8d8530 100644
--- a/lib/manager/types.ts
+++ b/lib/manager/types.ts
@@ -100,6 +100,7 @@ export interface Package<T> extends ManagerData<T> {
   repo?: string;
   target?: string;
   versioning?: string;
+  dataType?: string;
 
   // npm manager
   bumpVersion?: ReleaseType | string;
-- 
GitLab