From e4282ce03a5c0b08ae34b73d59f72835be21bd00 Mon Sep 17 00:00:00 2001
From: TSUYUSATO Kitsune <make.just.on@gmail.com>
Date: Sat, 15 Jan 2022 23:47:37 +0900
Subject: [PATCH] feat(manager/sbt): support `ThisBuild` scoped `scalaVersion`
 (#13544)

* fix(manager/sbt): support `ThisBuild` scoped `scalaVersion`

Using `ThisBuild / scalaVersion` to specify a common `scalaVersion`
across subprojects is a well-known technique such as described in
the official documentation of SBT.

<https://www.scala-sbt.org/1.x/docs/Scopes.html#Build-level+settings>

So, we should support to recognize this syntax also.

* fix(manager/sbt): support `ThisBuild / scalaVersion := variable`

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../sbt/__snapshots__/extract.spec.ts.snap    | 44 +++++++++++++++++++
 lib/manager/sbt/extract.spec.ts               | 33 ++++++++++++++
 lib/manager/sbt/extract.ts                    | 12 +++--
 3 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/lib/manager/sbt/__snapshots__/extract.spec.ts.snap b/lib/manager/sbt/__snapshots__/extract.spec.ts.snap
index 6ef285b041..c554afc830 100644
--- a/lib/manager/sbt/__snapshots__/extract.spec.ts.snap
+++ b/lib/manager/sbt/__snapshots__/extract.spec.ts.snap
@@ -428,6 +428,23 @@ Object {
 }
 `;
 
+exports[`manager/sbt/extract extractPackageFile() extracts deps when scala version is defined in a variable with ThisBuild scope 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "0.0.2",
+      "datasource": "sbt-package",
+      "depName": "org.example:bar",
+      "lookupName": "org.example:bar_2.12",
+      "registryUrls": Array [
+        "https://repo.maven.apache.org/maven2",
+      ],
+    },
+  ],
+  "packageFileVersion": undefined,
+}
+`;
+
 exports[`manager/sbt/extract extractPackageFile() extracts deps when scala version is defined in a variable with a trailing comma 1`] = `
 Object {
   "deps": Array [
@@ -445,6 +462,33 @@ Object {
 }
 `;
 
+exports[`manager/sbt/extract extractPackageFile() extracts deps when scala version is defined with ThisBuild scope 1`] = `
+Object {
+  "deps": Array [
+    Object {
+      "currentValue": "2.12.10",
+      "datasource": "maven",
+      "depName": "scala",
+      "lookupName": "org.scala-lang:scala-library",
+      "registryUrls": Array [
+        "https://repo.maven.apache.org/maven2",
+      ],
+      "separateMinorPatch": true,
+    },
+    Object {
+      "currentValue": "0.0.2",
+      "datasource": "sbt-package",
+      "depName": "org.example:bar",
+      "lookupName": "org.example:bar_2.12",
+      "registryUrls": Array [
+        "https://repo.maven.apache.org/maven2",
+      ],
+    },
+  ],
+  "packageFileVersion": undefined,
+}
+`;
+
 exports[`manager/sbt/extract extractPackageFile() extracts deps when scala version is defined with a trailing comma 1`] = `
 Object {
   "deps": Array [
diff --git a/lib/manager/sbt/extract.spec.ts b/lib/manager/sbt/extract.spec.ts
index 3ec5df2496..62f07a2b6f 100644
--- a/lib/manager/sbt/extract.spec.ts
+++ b/lib/manager/sbt/extract.spec.ts
@@ -154,6 +154,39 @@ describe('manager/sbt/extract', () => {
         deps: [{ lookupName: 'org.example:bar_2.12', currentValue: '0.0.2' }],
       });
     });
+    it('extracts deps when scala version is defined with ThisBuild scope', () => {
+      const content = `
+        ThisBuild / scalaVersion := "2.12.10"
+        libraryDependencies += "org.example" %% "bar" % "0.0.2"
+      `;
+      expect(extractPackageFile(content)).toMatchSnapshot({
+        deps: [
+          {
+            lookupName: 'org.scala-lang:scala-library',
+            currentValue: '2.12.10',
+          },
+          {
+            lookupName: 'org.example:bar_2.12',
+            currentValue: '0.0.2',
+          },
+        ],
+      });
+    });
+    it('extracts deps when scala version is defined in a variable with ThisBuild scope', () => {
+      const content = `
+        val ScalaVersion = "2.12.10"
+        ThisBuild / scalaVersion := ScalaVersion
+        libraryDependencies += "org.example" %% "bar" % "0.0.2"
+      `;
+      expect(extractPackageFile(content)).toMatchSnapshot({
+        deps: [
+          {
+            lookupName: 'org.example:bar_2.12',
+            currentValue: '0.0.2',
+          },
+        ],
+      });
+    });
     it('extract deps from native scala file with private variables', () => {
       expect(
         extractPackageFile(sbtPrivateVariableDependencyFile)
diff --git a/lib/manager/sbt/extract.ts b/lib/manager/sbt/extract.ts
index 7c5e176721..73352f25b3 100644
--- a/lib/manager/sbt/extract.ts
+++ b/lib/manager/sbt/extract.ts
@@ -23,11 +23,13 @@ const isPluginDep = (str: string): boolean =>
 const isStringLiteral = (str: string): boolean => regEx(/^"[^"]*"$/).test(str);
 
 const isScalaVersion = (str: string): boolean =>
-  regEx(/^\s*scalaVersion\s*:=\s*"[^"]*"[\s,]*$/).test(str);
+  regEx(/^\s*(?:ThisBuild\s*\/\s*)?scalaVersion\s*:=\s*"[^"]*"[\s,]*$/).test(
+    str
+  );
 
 const getScalaVersion = (str: string): string =>
   str
-    .replace(regEx(/^\s*scalaVersion\s*:=\s*"/), '')
+    .replace(regEx(/^\s*(?:ThisBuild\s*\/\s*)?scalaVersion\s*:=\s*"/), '')
     .replace(regEx(/"[\s,]*$/), '');
 
 const isPackageFileVersion = (str: string): boolean =>
@@ -66,11 +68,13 @@ const normalizeScalaVersion = (str: string): string => {
 };
 
 const isScalaVersionVariable = (str: string): boolean =>
-  regEx(/^\s*scalaVersion\s*:=\s*[_a-zA-Z][_a-zA-Z0-9]*[\s,]*$/).test(str);
+  regEx(
+    /^\s*(?:ThisBuild\s*\/\s*)?scalaVersion\s*:=\s*[_a-zA-Z][_a-zA-Z0-9]*[\s,]*$/
+  ).test(str);
 
 const getScalaVersionVariable = (str: string): string =>
   str
-    .replace(regEx(/^\s*scalaVersion\s*:=\s*/), '')
+    .replace(regEx(/^\s*(?:ThisBuild\s*\/\s*)?scalaVersion\s*:=\s*/), '')
     .replace(regEx(/[\s,]*$/), '');
 
 const isResolver = (str: string): boolean =>
-- 
GitLab