Repository: scala-steward-org/scala-steward (scala)

Issue:
Support Scalafix migrations in Scala CLI projects
Scala CLI 1.5.4+ will contain a `scalafix` command (https://github.com/VirtusLab/scala-cli/pull/2968) which we should be able to use for implementing [`ScalaCliAlg#runMigration`](https://github.com/scala-steward-org/scala-steward/blob/0b783fe1e58f03934295888ccad8d78926d88af5/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala) which is currently a NOP.

Full support for Scalafix migrations is currently only available for sbt projects. The corresponding [code in SbtAlg](https://github.com/scala-steward-org/scala-steward/blob/0b783fe1e58f03934295888ccad8d78926d88af5/modules/core/src/main/scala/org/scalasteward/core/buildtool/sbt/SbtAlg.scala#L105-L145) can be used as inspiration for supporting them in Scala CLI.

Pull request:
This adds support for running Scalafix migrations in Scala CLI builds by using the `scala-cli --power fix --scalafix-rules ...` command that was added to Scala CLI 1.6.0.

Example PR: https://github.com/scala-steward-org/test-repo-1/pull/115 

Closes: #3486

Patch:
diff --git a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala
index fc706d526a..71ffee1712 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlg.scala
@@ -21,6 +21,7 @@ import cats.syntax.all.*
 import org.scalasteward.core.buildtool.sbt.SbtAlg
 import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg}
 import org.scalasteward.core.data.Scope
+import org.scalasteward.core.edit.scalafix.ScalafixMigration
 import org.scalasteward.core.git.GitAlg
 import org.scalasteward.core.io.process.SlurpOptions
 import org.scalasteward.core.io.{FileAlg, ProcessAlg, WorkspaceAlg}
@@ -91,6 +92,12 @@ final class ScalaCliAlg[F[_]](implicit
       _ <- fileAlg.deleteForce(buildRootDir / exportDir)
     } yield dependencies
 
-  override protected val scalafixIssue: Option[String] =
-    Some("https://github.com/scala-steward-org/scala-steward/issues/3486")
+  override def runMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] =
+    for {
+      buildRootDir <- workspaceAlg.buildRootDir(buildRoot)
+      cmd = Nel.of("scala-cli", "--power", "fix", "--scalafix-rules") :::
+        migration.rewriteRules.append(buildRootDir.pathAsString)
+      slurpOptions = SlurpOptions.ignoreBufferOverflow
+      _ <- processAlg.execSandboxed(cmd, buildRootDir, slurpOptions = slurpOptions)
+    } yield ()
 }


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlgTest.scala b/modules/core/src/test/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlgTest.scala
index 6419c86a12..0ad9aff9c3 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlgTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/buildtool/scalacli/ScalaCliAlgTest.scala
@@ -7,7 +7,7 @@ import org.scalasteward.core.buildtool.sbt.command.*
 import org.scalasteward.core.data.{GroupId, Repo, Version}
 import org.scalasteward.core.edit.scalafix.ScalafixMigration
 import org.scalasteward.core.mock.MockContext.context.*
-import org.scalasteward.core.mock.MockState.TraceEntry.{Cmd, Log}
+import org.scalasteward.core.mock.MockState.TraceEntry.Cmd
 import org.scalasteward.core.mock.{MockEffOps, MockState}
 import org.scalasteward.core.util.Nel
 
@@ -84,8 +84,9 @@ class ScalaCliAlgTest extends CatsEffectSuite {
   }
 
   test("runMigration") {
-    val repo = Repo("user", "repo")
+    val repo = Repo("scala-cli-alg", "test-runMigration")
     val buildRoot = BuildRoot(repo, ".")
+    val buildRootDir = workspaceAlg.buildRootDir(buildRoot).unsafeRunSync()
     val migration = ScalafixMigration(
       GroupId("co.fs2"),
       Nel.of("fs2-core"),
@@ -94,6 +95,19 @@ class ScalaCliAlgTest extends CatsEffectSuite {
       signoffCommits = None
     )
     val obtained = scalaCliAlg.runMigration(buildRoot, migration).runS(MockState.empty)
-    assertIO(obtained.map(_.trace.collect { case Log(_) => () }.size), 1)
+    val expected = MockState.empty.copy(trace =
+      Vector(
+        Cmd.execSandboxed(
+          buildRootDir,
+          "scala-cli",
+          "--power",
+          "fix",
+          "--scalafix-rules",
+          "github:functional-streams-for-scala/fs2/v1?sha=v1.0.5",
+          buildRootDir.pathAsString
+        )
+      )
+    )
+    assertIO(obtained, expected)
   }
 }