Skip to content

Commit cf82b87

Browse files
Try using sbt-scalafix for Scalafix build migrations
1 parent ea94b76 commit cf82b87

File tree

2 files changed

+225
-13
lines changed

2 files changed

+225
-13
lines changed

modules/core/src/main/scala/org/scalasteward/core/buildtool/sbt/SbtAlg.scala

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ import org.scalasteward.core.buildtool.sbt.command._
2525
import org.scalasteward.core.buildtool.{BuildRoot, BuildToolAlg}
2626
import org.scalasteward.core.coursier.VersionsCache
2727
import org.scalasteward.core.data.{Dependency, Scope, Version}
28-
import org.scalasteward.core.edit.scalafix.{ScalafixCli, ScalafixMigration}
28+
import org.scalasteward.core.edit.scalafix.ScalafixMigration
2929
import org.scalasteward.core.io.process.SlurpOptions
3030
import org.scalasteward.core.io.{FileAlg, FileData, ProcessAlg, WorkspaceAlg}
3131
import org.scalasteward.core.util.Nel
32+
import org.scalasteward.core.buildtool.sbt.scalaStewardSbtScalafix
3233

3334
final class SbtAlg[F[_]](config: Config)(implicit
3435
fileAlg: FileAlg[F],
3536
processAlg: ProcessAlg[F],
36-
scalafixCli: ScalafixCli[F],
3737
workspaceAlg: WorkspaceAlg[F],
3838
versionsCache: VersionsCache[F],
3939
F: Concurrent[F]
@@ -129,18 +129,52 @@ final class SbtAlg[F[_]](config: Config)(implicit
129129
.getVersions(Scope(sbtScalafixDependency, List(config.defaultResolver)), None)
130130
.map(_.lastOption)
131131

132+
private def addScalafixPluginTemporarily(
133+
buildRootDir: File,
134+
pluginVersion: Version,
135+
metaBuilds: Int
136+
): Resource[F, Unit] = {
137+
val plugin = scalaStewardSbtScalafix(pluginVersion)
138+
List
139+
.iterate(buildRootDir / project / project, metaBuilds + 1)(_ / project)
140+
.collectFold(fileAlg.createTemporarily(_, plugin))
141+
}
142+
143+
private def addScalacOptionsTemporarily(
144+
buildRootDir: File,
145+
scalacOptions: Option[Nel[String]],
146+
metaBuilds: Int
147+
): Resource[F, Unit] =
148+
scalacOptions.fold(Resource.unit[F]) { opts =>
149+
val options = scalaStewardScalafixOptions(opts.toList)
150+
List
151+
.iterate(buildRootDir / project, metaBuilds + 1)(_ / project)
152+
.collectFold(fileAlg.createTemporarily(_, options))
153+
}
154+
132155
private def runBuildMigration(buildRoot: BuildRoot, migration: ScalafixMigration): F[Unit] =
133-
for {
134-
buildRootDir <- workspaceAlg.buildRootDir(buildRoot)
135-
projectDir = buildRootDir / project
136-
files0 <- (
137-
fileAlg.walk(buildRootDir, 1).filter(_.extension.contains(".sbt")) ++
138-
fileAlg.walk(projectDir, 3).filter(_.extension.exists(Set(".sbt", ".scala")))
139-
).compile.toList
140-
_ <- Nel.fromList(files0).fold(F.unit) { files1 =>
141-
scalafixCli.runMigration(buildRootDir, files1, migration)
142-
}
143-
} yield ()
156+
OptionT(latestSbtScalafixVersion).foreachF { pluginVersion =>
157+
for {
158+
buildRootDir <- workspaceAlg.buildRootDir(buildRoot)
159+
metaBuilds <- metaBuildsCount(buildRootDir)
160+
_ <- addScalafixPluginTemporarily(buildRootDir, pluginVersion, metaBuilds).surround {
161+
addScalacOptionsTemporarily(buildRootDir, migration.scalacOptions, metaBuilds).surround {
162+
val scalafixCmds = migration.rewriteRules.map(rule => s"$scalafixAll $rule").toList
163+
val slurpOptions = SlurpOptions.ignoreBufferOverflow
164+
val commands = Nel.fromList(
165+
List.fill(metaBuilds + 1)(List(reloadPlugins, scalafixEnable) ++ scalafixCmds).flatten
166+
)
167+
commands.fold(F.unit) { cmds =>
168+
sbt(
169+
cmds,
170+
buildRootDir,
171+
slurpOptions
172+
).void
173+
}
174+
}
175+
}
176+
} yield ()
177+
}
144178

145179
private def sbt(
146180
sbtCommands: Nel[String],

modules/core/src/test/scala/org/scalasteward/core/buildtool/sbt/SbtAlgTest.scala

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,184 @@ class SbtAlgTest extends FunSuite {
125125
assertEquals(state, expected)
126126
}
127127

128+
test("runMigrations: build migration") {
129+
val repo = Repo("sbt-alg", "test-build-migration-1")
130+
val buildRoot = BuildRoot(repo, ".")
131+
val repoDir = workspaceAlg.repoDir(repo).unsafeRunSync()
132+
val migration = ScalafixMigration(
133+
groupId = GroupId("io.github.davidgregory084"),
134+
artifactIds = Nel.of("sbt-tpolecat"),
135+
newVersion = Version("0.5.0"),
136+
rewriteRules = Nel.of("github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"),
137+
target = Some(ScalafixMigration.Target.Build)
138+
)
139+
val initialState = MockState.empty
140+
.addFiles(
141+
workspace / s"store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json" -> sbtScalafixVersionJson
142+
)
143+
.unsafeRunSync()
144+
val state = sbtAlg.runMigration(buildRoot, migration).runS(initialState).unsafeRunSync()
145+
val expected = initialState.copy(
146+
trace = Vector(
147+
Cmd(
148+
"read",
149+
s"$workspace/store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json"
150+
),
151+
Cmd("test", "-d", s"$repoDir/project"),
152+
Cmd("write", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt"),
153+
Cmd.execSandboxed(
154+
repoDir,
155+
"sbt",
156+
"-Dsbt.color=false",
157+
"-Dsbt.log.noformat=true",
158+
"-Dsbt.supershell=false",
159+
"-Dsbt.server.forcestart=true",
160+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"
161+
),
162+
Cmd("rm", "-rf", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt")
163+
)
164+
)
165+
assertEquals(state, expected)
166+
}
167+
168+
test("runMigrations: build migration with meta-build") {
169+
val repo = Repo("sbt-alg", "test-build-migration-2")
170+
val buildRoot = BuildRoot(repo, ".")
171+
val repoDir = workspaceAlg.repoDir(repo).unsafeRunSync()
172+
val migration = ScalafixMigration(
173+
groupId = GroupId("io.github.davidgregory084"),
174+
artifactIds = Nel.of("sbt-tpolecat"),
175+
newVersion = Version("0.5.0"),
176+
rewriteRules = Nel.of("github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"),
177+
target = Some(ScalafixMigration.Target.Build)
178+
)
179+
val initialState = MockState.empty
180+
.addFiles(
181+
repoDir / "project" / "Dependencies.scala" -> "object Dependencies", // put anything at all into the meta-build
182+
workspace / s"store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json" -> sbtScalafixVersionJson
183+
)
184+
.unsafeRunSync()
185+
val state = sbtAlg.runMigration(buildRoot, migration).runS(initialState).unsafeRunSync()
186+
val expected = initialState.copy(
187+
trace = Vector(
188+
Cmd(
189+
"read",
190+
s"$workspace/store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json"
191+
),
192+
Cmd("test", "-d", s"$repoDir/project"),
193+
Cmd("test", "-d", s"$repoDir/project/project"),
194+
Cmd("write", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt"),
195+
Cmd("write", s"$repoDir/project/project/project/scala-steward-sbt-scalafix.sbt"),
196+
Cmd.execSandboxed(
197+
repoDir,
198+
"sbt",
199+
"-Dsbt.color=false",
200+
"-Dsbt.log.noformat=true",
201+
"-Dsbt.supershell=false",
202+
"-Dsbt.server.forcestart=true",
203+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0" +
204+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"
205+
),
206+
Cmd("rm", "-rf", s"$repoDir/project/project/project/scala-steward-sbt-scalafix.sbt"),
207+
Cmd("rm", "-rf", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt")
208+
)
209+
)
210+
assertEquals(state, expected)
211+
}
212+
213+
test("runMigrations: build migration with scalacOptions") {
214+
val repo = Repo("sbt-alg", "test-build-migration-3")
215+
val buildRoot = BuildRoot(repo, ".")
216+
val repoDir = workspaceAlg.repoDir(repo).unsafeRunSync()
217+
val migration = ScalafixMigration(
218+
groupId = GroupId("io.github.davidgregory084"),
219+
artifactIds = Nel.of("sbt-tpolecat"),
220+
newVersion = Version("0.5.0"),
221+
rewriteRules = Nel.of("github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"),
222+
scalacOptions = Some(Nel.of("-P:semanticdb:synthetics:on")),
223+
target = Some(ScalafixMigration.Target.Build)
224+
)
225+
val initialState = MockState.empty
226+
.addFiles(
227+
workspace / s"store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json" -> sbtScalafixVersionJson
228+
)
229+
.unsafeRunSync()
230+
val state = sbtAlg.runMigration(buildRoot, migration).runS(initialState).unsafeRunSync()
231+
val expected = initialState.copy(
232+
trace = Vector(
233+
Cmd(
234+
"read",
235+
s"$workspace/store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json"
236+
),
237+
Cmd("test", "-d", s"$repoDir/project"),
238+
Cmd("write", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt"),
239+
Cmd("write", s"$repoDir/project/scala-steward-scalafix-options.sbt"),
240+
Cmd.execSandboxed(
241+
repoDir,
242+
"sbt",
243+
"-Dsbt.color=false",
244+
"-Dsbt.log.noformat=true",
245+
"-Dsbt.supershell=false",
246+
"-Dsbt.server.forcestart=true",
247+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"
248+
),
249+
Cmd("rm", "-rf", s"$repoDir/project/scala-steward-scalafix-options.sbt"),
250+
Cmd("rm", "-rf", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt")
251+
)
252+
)
253+
assertEquals(state, expected)
254+
}
255+
256+
test("runMigrations: build migration with scalacOptions and meta-build") {
257+
val repo = Repo("sbt-alg", "test-build-migration-4")
258+
val buildRoot = BuildRoot(repo, ".")
259+
val repoDir = workspaceAlg.repoDir(repo).unsafeRunSync()
260+
val migration = ScalafixMigration(
261+
groupId = GroupId("io.github.davidgregory084"),
262+
artifactIds = Nel.of("sbt-tpolecat"),
263+
newVersion = Version("0.5.0"),
264+
rewriteRules = Nel.of("github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"),
265+
scalacOptions = Some(Nel.of("-P:semanticdb:synthetics:on")),
266+
target = Some(ScalafixMigration.Target.Build)
267+
)
268+
val initialState = MockState.empty
269+
.addFiles(
270+
repoDir / "project" / "Dependencies.scala" -> "object Dependencies", // put anything at all into the meta-build
271+
workspace / s"store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json" -> sbtScalafixVersionJson
272+
)
273+
.unsafeRunSync()
274+
val state = sbtAlg.runMigration(buildRoot, migration).runS(initialState).unsafeRunSync()
275+
val expected = initialState.copy(
276+
trace = Vector(
277+
Cmd(
278+
"read",
279+
s"$workspace/store/versions/v2/https/repo1.maven.org/maven2/ch/epfl/scala/sbt-scalafix_2.12_1.0/versions.json"
280+
),
281+
Cmd("test", "-d", s"$repoDir/project"),
282+
Cmd("test", "-d", s"$repoDir/project/project"),
283+
Cmd("write", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt"),
284+
Cmd("write", s"$repoDir/project/project/project/scala-steward-sbt-scalafix.sbt"),
285+
Cmd("write", s"$repoDir/project/scala-steward-scalafix-options.sbt"),
286+
Cmd("write", s"$repoDir/project/project/scala-steward-scalafix-options.sbt"),
287+
Cmd.execSandboxed(
288+
repoDir,
289+
"sbt",
290+
"-Dsbt.color=false",
291+
"-Dsbt.log.noformat=true",
292+
"-Dsbt.supershell=false",
293+
"-Dsbt.server.forcestart=true",
294+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0" +
295+
s";$reloadPlugins;$scalafixEnable;$scalafixAll github:typelevel/sbt-tpolecat/v0_5?sha=v0.5.0"
296+
),
297+
Cmd("rm", "-rf", s"$repoDir/project/project/scala-steward-scalafix-options.sbt"),
298+
Cmd("rm", "-rf", s"$repoDir/project/scala-steward-scalafix-options.sbt"),
299+
Cmd("rm", "-rf", s"$repoDir/project/project/project/scala-steward-sbt-scalafix.sbt"),
300+
Cmd("rm", "-rf", s"$repoDir/project/project/scala-steward-sbt-scalafix.sbt")
301+
)
302+
)
303+
assertEquals(state, expected)
304+
}
305+
128306
private def sbtScalafixVersionJson =
129307
s"""|{
130308
| "updatedAt" : 9999999999999,

0 commit comments

Comments
 (0)