diff --git a/.github/scripts/check-cross-version-deps.sc b/.github/scripts/check-cross-version-deps.sc new file mode 100755 index 0000000000..07e0defd87 --- /dev/null +++ b/.github/scripts/check-cross-version-deps.sc @@ -0,0 +1,57 @@ +#!/usr/bin/env -S scala-cli shebang +//> using scala 3 +//> using toolkit default + +val modules = + os.proc(os.pwd / "mill", "-i", "resolve", "__[]") + .call(cwd = os.pwd) + .out + .lines() + +for { module <- modules } { + println(s"Checking for $module...") + val depRegex = "[│└─\\S\\s]+\\s([\\w.-]+):([\\w.-]+):([\\w\\s\\S.-]+)".r + val scalaDepSuffixRegex = "^(.+?)(_[23](?:\\.\\d{2})?)?$".r + val deps = os.proc(os.pwd / "mill", "-i", s"$module.ivyDepsTree") + .call(cwd = os.pwd) + .out + .lines() + .map { case depRegex(org, name, depVersion) => (org, name, depVersion) } + val scalaVersionsByOrgAndName = deps + .groupBy { case (org, scalaDepSuffixRegex(nameWithoutSuffix, _), _) => + s"$org:$nameWithoutSuffix" + } + .map { case (key, entries) => + key -> entries.map { case (_, scalaDepSuffixRegex(_, scalaVersion), _) => + scalaVersion + }.distinct + } + .filter { case (_, scalaVersions) => scalaVersions.head != null } // filter out non-Scala deps + println("Checking for clashing dependency Scala versions...") + val conflictEntries: Map[String, Vector[String]] = + scalaVersionsByOrgAndName + .filter { case (key, scalaVersions) => + if scalaVersions.length == 1 then + println(s"[info] $key${scalaVersions.head} (OK)") + false + else + println( + s"[${Console.RED}error${Console.RESET}] $key: multiple conflicting Scala versions: ${scalaVersions.mkString(", ")}" + ) + true + } + if conflictEntries.nonEmpty then + println(s"${Console.RED}ERROR: Found ${conflictEntries.size} conflicting entries for $module:") + conflictEntries.foreach { + case (key, scalaVersions) => + println(s" $key: multiple conflicting Scala versions: ${scalaVersions.mkString(", ")}") + } + println(Console.RESET) + sys.exit(1) + else println(s"[info] $module OK") +} + +println("Checks completed for:") +modules.foreach(m => println(s" $m")) +println("No conflicts detected.") +sys.exit(0) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index daf53a9ab9..17f799c3a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1493,6 +1493,8 @@ jobs: run: ./mill -i __.checkNativeImageConfFormat - name: Check Ammonite availability run: ./mill -i 'dummy.amm[_].resolvedRunIvyDeps' + - name: Check for cross Scala version conflicts + run: .github/scripts/check-cross-version-deps.sc - name: Scalafix check run: | ./mill -i __.fix --check || ( diff --git a/project/deps.sc b/project/deps.sc index 949222345f..d2a02e7102 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -160,6 +160,7 @@ object Deps { def coursierCli = ivy"io.get-coursier:coursier-cli_2.13:${Versions.coursierCli}" def coursierJvm = ivy"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}" .exclude(("com.github.plokhotnyuk.jsoniter-scala", "jsoniter-scala-core_2.13")) + .exclude("io.get-coursier" -> "dependency_2.13") def coursierLauncher = ivy"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}" .exclude(("ai.kien", "python-native-libs_2.13")) .exclude(("org.scala-lang.modules", "scala-collection-compat_2.13"))