Skip to content
Permalink
Browse files

New StewardPlugin tasks, attempt 2

This contains the changes to StewardPlugin from #851 without the other
changes to the core module. I'm more comfortable digesting the changes
of #851 in piecemeal.

The advantage of this change is that we get rid of the parser for the
dependencyUpdates task, that we get more newer versions from
sbt-updates, and that we don't need to set dependencyUpdatesFailBuild
any more because dependencyUpdatesData is not using it.
  • Loading branch information
fthomas committed Nov 28, 2019
1 parent 55fe542 commit 065c498865dd524490d8b33e2839f43d31d780dc
@@ -103,6 +103,7 @@ lazy val plugin = myCrossProject("plugin")
.settings(noPublishSettings)
.settings(
sbtPlugin := true,
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.0"),
Compile / compile / wartremoverErrors -= Wart.Equals
)

@@ -107,7 +107,7 @@ object SbtAlg {
def getOriginalDependencies(repo: Repo): F[List[Dependency]] =
for {
repoDir <- workspaceAlg.repoDir(repo)
cmd = sbtCmd(List(libraryDependenciesAsJson, reloadPlugins, libraryDependenciesAsJson))
cmd = sbtCmd(List(stewardDependencies, reloadPlugins, stewardDependencies))
lines <- exec(cmd, repoDir)
} yield parser.parseDependencies(lines)

@@ -132,12 +132,7 @@ object SbtAlg {
override def getUpdatesForRepo(repo: Repo): F[List[Update.Single]] =
for {
repoDir <- workspaceAlg.repoDir(repo)
commands = List(
setDependencyUpdatesFailBuild,
dependencyUpdates,
reloadPlugins,
dependencyUpdates
)
commands = List(stewardUpdates, reloadPlugins, stewardUpdates)
updates <- withTemporarySbtDependency(repo) {
exec(sbtCmd(commands), repoDir).map(parser.parseSingleUpdates)
}
@@ -17,11 +17,10 @@
package org.scalasteward.core.sbt

object command {
val dependencyUpdates = "dependencyUpdates"
val libraryDependenciesAsJson = "show libraryDependenciesAsJson"
val stewardUpdates = "show stewardUpdates"
val stewardDependencies = "show stewardDependencies"
val reloadPlugins = "reload plugins"
val scalafix = "scalafix"
val testScalafix = "test:scalafix"
val scalafixEnable = "scalafixEnable"
val setDependencyUpdatesFailBuild = "set every dependencyUpdatesFailBuild := false"
}
@@ -18,7 +18,7 @@ package org.scalasteward.core.sbt.data

import org.scalasteward.core.data.Dependency
import org.scalasteward.core.io.FileData
import org.scalasteward.core.sbt.command.{dependencyUpdates, reloadPlugins}
import org.scalasteward.core.sbt.command.{reloadPlugins, stewardUpdates}
import org.scalasteward.core.util
import scala.collection.mutable.ListBuffer

@@ -31,9 +31,9 @@ final case class ArtificialProject(
def dependencyUpdatesCmd: List[String] = {
val lb = new ListBuffer[String]
if (libraries.nonEmpty)
lb.append(dependencyUpdates)
lb.append(stewardUpdates)
if (plugins.nonEmpty)
lb.append(reloadPlugins, dependencyUpdates)
lb.append(reloadPlugins, stewardUpdates)
lb.toList
}

@@ -17,53 +17,35 @@
package org.scalasteward.core.sbt

import cats.implicits._
import io.circe.Decoder
import io.circe.parser.decode
import org.scalasteward.core.data.{Dependency, GroupId, Update}
import org.scalasteward.core.data.{Dependency, Update}
import org.scalasteward.core.sbt.data.SbtVersion
import org.scalasteward.core.util.Nel

object parser {
def parseBuildProperties(s: String): Option[SbtVersion] =
"""sbt.version\s*=\s*(.+)""".r.findFirstMatchIn(s).map(_.group(1)).map(SbtVersion.apply)

/** Parses a single line of output from sbt-updates' `dependencyUpdates` task. */
def parseSingleUpdate(line: String): Either[String, Update.Single] =
line.split("""\s:\s""") match {
case Array(left, right) =>
val moduleId = left.split(":").map(_.trim)
val versions = right.split("->").map(_.trim)
def msg(part: String) = s"failed to parse $part in '$line'"

/** Parses the output of our own `stewardUpdates` task. */
def parseSingleUpdates(lines: List[String]): List[Update.Single] = {
implicit val updateDecoder: Decoder[Update.Single] =
Decoder.instance { c =>
for {
groupId <- Either
.fromOption(moduleId.headOption.filter(_.nonEmpty), msg("groupId"))
.map(GroupId.apply)
artifactId <- Either.fromOption(moduleId.lift(1), msg("artifactId"))
configurations = moduleId.lift(2)
currentVersion <- Either.fromOption(
versions.headOption.filter(_.nonEmpty),
msg("currentVersion")
)
newerVersionsList = versions
.drop(1)
.filterNot(v => v.startsWith("InvalidVersion") || v === currentVersion)
.toList
newerVersions <- Either.fromOption(Nel.fromList(newerVersionsList), msg("newerVersions"))
} yield Update.Single(groupId, artifactId, currentVersion, newerVersions, configurations)

case _ => Left(s"'$line' must contain ' : ' exactly once")
}
dependency <- c.downField("dependency").as[Dependency]
newerVersions <- c.downField("newerVersions").as[Nel[String]]
} yield dependency.toUpdate.copy(newerVersions = newerVersions)
}

/** Parses the output of sbt-updates' `dependencyUpdates` task. */
def parseSingleUpdates(lines: List[String]): List[Update.Single] =
lines
.flatMap(line => parseSingleUpdate(removeSbtNoise(line)).toList)
.flatMap(line => decode[Update.Single](removeSbtNoise(line)).toList)
.distinct
.sortBy(update => (update.groupId, update.artifactId, update.currentVersion))
}

/** Parses the output of our own `libraryDependenciesAsJson` task. */
/** Parses the output of our own `stewardDependencies` task. */
def parseDependencies(lines: List[String]): List[Dependency] =
lines.flatMap(line => decode[List[Dependency]](removeSbtNoise(line)).getOrElse(List.empty))
lines.flatMap(line => decode[Dependency](removeSbtNoise(line)).toList)

private def removeSbtNoise(s: String): String =
s.replace("[info]", "").trim
@@ -58,7 +58,7 @@ class SbtAlgTest extends AnyFunSuite with Matchers {
"sbt",
"-batch",
"-no-colors",
s";$setDependencyUpdatesFailBuild;$dependencyUpdates;$reloadPlugins;$dependencyUpdates"
s";$stewardUpdates;$reloadPlugins;$stewardUpdates"
),
List("rm", s"$repoDir/project/tmp-sbt-dep.sbt"),
List("read", s"${config.workspace}/repos_v6.json")
@@ -90,7 +90,7 @@ class SbtAlgTest extends AnyFunSuite with Matchers {
"sbt",
"-batch",
"-no-colors",
s";$setDependencyUpdatesFailBuild;$dependencyUpdates;$reloadPlugins;$dependencyUpdates"
s";$stewardUpdates;$reloadPlugins;$stewardUpdates"
),
List("restore", (repoDir / ".sbtopts").toString),
List("restore", (repoDir / ".jvmopts").toString),
@@ -1,6 +1,7 @@
package org.scalasteward.core.sbt.data

import org.scalasteward.core.data.{Dependency, GroupId}
import org.scalasteward.core.sbt.command._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

@@ -34,9 +35,9 @@ class ArtificialProjectTest extends AnyFunSuite with Matchers {

test("dependencyUpdatesCmd") {
project.dependencyUpdatesCmd shouldBe List(
"dependencyUpdates",
"reload plugins",
"dependencyUpdates"
stewardUpdates,
reloadPlugins,
stewardUpdates
)
}

@@ -12,90 +12,12 @@ class parserTest extends AnyFunSuite with Matchers {
parseBuildProperties("sbt.version = 1.2.8") shouldBe Some(SbtVersion("1.2.8"))
}

test("parseSingleUpdate: 1 new version") {
val str = "org.scala-js:sbt-scalajs : 0.6.24 -> 0.6.25"
parseSingleUpdate(str) shouldBe
Right(Update.Single(GroupId("org.scala-js"), "sbt-scalajs", "0.6.24", Nel.of("0.6.25")))
}

test("parseSingleUpdate: 2 new versions") {
val str = "org.scala-lang:scala-library : 2.9.1 -> 2.9.3 -> 2.10.3"
parseSingleUpdate(str) shouldBe
Right(
Update
.Single(GroupId("org.scala-lang"), "scala-library", "2.9.1", Nel.of("2.9.3", "2.10.3"))
)
}

test("parseSingleUpdate: 3 new versions") {
val str = "ch.qos.logback:logback-classic : 0.8 -> 0.8.1 -> 0.9.30 -> 1.0.13"
parseSingleUpdate(str) shouldBe
Right(
Update.Single(
GroupId("ch.qos.logback"),
"logback-classic",
"0.8",
Nel.of("0.8.1", "0.9.30", "1.0.13")
)
)
}

test("parseSingleUpdate: test dependency") {
val str = "org.scalacheck:scalacheck:test : 1.12.5 -> 1.12.6 -> 1.14.0"
parseSingleUpdate(str) shouldBe
Right(
Update
.Single(
GroupId("org.scalacheck"),
"scalacheck",
"1.12.5",
Nel.of("1.12.6", "1.14.0"),
Some("test")
)
)
}

test("parseSingleUpdate: no groupId") {
val str = ":sbt-scalajs : 0.6.24 -> 0.6.25"
parseSingleUpdate(str) shouldBe Left(s"failed to parse groupId in '$str'")
}

test("parseSingleUpdate: no current version") {
val str = "ch.qos.logback:logback-classic : -> 0.8.1 -> 0.9.30 -> 1.0.13"
parseSingleUpdate(str) shouldBe Left(s"failed to parse currentVersion in '$str'")
}

test("parseSingleUpdate: no new versions") {
val str = "ch.qos.logback:logback-classic : 0.8 ->"
parseSingleUpdate(str) shouldBe Left(s"failed to parse newerVersions in '$str'")
}

test("parseSingleUpdate: all new versions are invalid") {
val str =
"bigdataoss:gcs-connector : hadoop2-1.9.16 -> InvalidVersion(hadoop3-2.0.0-SNAPSHOT)"
parseSingleUpdate(str) shouldBe Left(s"failed to parse newerVersions in '$str'")
}

test("parseSingleUpdate: one new version is invalid") {
val str =
"bigdataoss:gcs-connector : hadoop2-1.9.16 -> InvalidVersion(hadoop3-2.0.0-SNAPSHOT) -> 1.9.4-hadoop3"
parseSingleUpdate(str) shouldBe Right(
Update
.Single(GroupId("bigdataoss"), "gcs-connector", "hadoop2-1.9.16", Nel.of("1.9.4-hadoop3"))
)
}

test("parseSingleUpdate: new version is current version") {
val str = "org.scalacheck:scalacheck:test : 1.14.0 -> 1.14.0"
parseSingleUpdate(str) shouldBe Left(s"failed to parse newerVersions in '$str'")
}

test("parseSingleUpdates: 3 updates") {
val str =
"""[info] Found 3 dependency updates for datapackage
|[info] ai.x:diff:test : 1.2.0 -> 1.2.1
|[info] eu.timepit:refined : 0.7.0 -> 0.9.3
|[info] com.geirsson:scalafmt-cli_2.11:scalafmt : 0.3.0 -> 0.3.1 -> 0.6.8 -> 1.5.1
|[info] { "dependency": { "groupId": "ai.x", "artifactId": "diff", "artifactIdCross": "diff_2.11", "version": "1.2.0", "configurations": "test" }, "newerVersions": [ "1.2.1" ] }
|[info] { "dependency": { "groupId": "eu.timepit", "artifactId": "refined", "artifactIdCross": "refined_2.11", "version": "0.7.0" }, "newerVersions": [ "0.9.3" ] }
|[info] { "dependency": { "groupId": "com.geirsson", "artifactId": "scalafmt-cli_2.11", "artifactIdCross": "scalafmt-cli_2.11", "version": "0.3.0", "configurations": "scalafmt" }, "newerVersions": [ "0.3.1", "0.6.8", "1.5.1" ] }
""".stripMargin.trim
parseSingleUpdates(str.linesIterator.toList) shouldBe
List(
@@ -113,16 +35,15 @@ class parserTest extends AnyFunSuite with Matchers {
}

test("parseSingleUpdates: with duplicates") {
val lines = List(
"[info] Found 1 dependency update for refined",
"[info] org.scala-lang:scala-library : 2.12.3 -> 2.12.6",
"[info] Found 2 dependency updates for refined-scalacheck",
"[info] org.scala-lang:scala-library : 2.12.3 -> 2.12.6",
"[info] org.scalacheck:scalacheck : 1.13.5 -> 1.14.0",
"[info] Found 2 dependency updates for refined-pureconfig",
"[info] com.github.pureconfig:pureconfig : 0.8.0 -> 0.9.2",
"[info] org.scala-lang:scala-library : 2.12.3 -> 2.12.6"
)
val lines =
"""|[info] Found 1 dependency update for refined",
|[info] { "dependency": { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.3" }, "newerVersions": [ "2.12.6" ] }
|[info] Found 2 dependency updates for refined-scalacheck",
|[info] { "dependency": { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.3" }, "newerVersions": [ "2.12.6" ] }
|[info] { "dependency": { "groupId": "org.scalacheck", "artifactId": "scalacheck", "artifactIdCross": "scalacheck_2.12", "version": "1.13.5" }, "newerVersions": [ "1.14.0" ] }
|[info] Found 2 dependency updates for refined-pureconfig",
|[info] { "dependency": { "groupId": "com.github.pureconfig", "artifactId": "pureconfig", "artifactIdCross": "pureconfig_2.12", "version": "0.8.0" }, "newerVersions": [ "0.9.2" ] }
|""".stripMargin.linesIterator.toList
parseSingleUpdates(lines) shouldBe
List(
Update.Single(GroupId("com.github.pureconfig"), "pureconfig", "0.8.0", Nel.of("0.9.2")),
@@ -133,10 +54,15 @@ class parserTest extends AnyFunSuite with Matchers {

test("parseDependencies") {
val lines =
"""|[info] core / libraryDependenciesAsJson
|[info] [ { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.7" }, { "groupId": "com.github.pathikrit", "artifactId": "better-files", "artifactIdCross": "better-files_2.12", "version": "3.6.0" }, { "groupId": "org.typelevel", "artifactId": "cats-effect", "artifactIdCross": "cats-effect_2.12", "version": "1.0.0" } ]
|sbt:project> libraryDependenciesAsJson
|[info] [ { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.6" }, { "groupId": "com.dwijnand", "artifactId": "sbt-travisci", "artifactIdCross": "sbt-travisci", "version": "1.1.3", "sbtVersion": "1.0" }, { "groupId": "com.eed3si9n", "artifactId": "sbt-assembly", "artifactIdCross": "sbt-assembly", "version": "0.14.8", "sbtVersion": "1.0", "configurations": "foo" }, { "groupId": "com.geirsson", "artifactId": "sbt-scalafmt", "artifactIdCross": "sbt-scalafmt", "version": "1.6.0-RC4", "sbtVersion": "1.0" } ]
"""|[info] core / stewardDependencies
|[info] { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.7" }
|[info] { "groupId": "com.github.pathikrit", "artifactId": "better-files", "artifactIdCross": "better-files_2.12", "version": "3.6.0" }
|[info] { "groupId": "org.typelevel", "artifactId": "cats-effect", "artifactIdCross": "cats-effect_2.12", "version": "1.0.0" }
|sbt:project> stewardDependencies
|[info] { "groupId": "org.scala-lang", "artifactId": "scala-library", "artifactIdCross": "scala-library", "version": "2.12.6" }
|[info] { "groupId": "com.dwijnand", "artifactId": "sbt-travisci", "artifactIdCross": "sbt-travisci", "version": "1.1.3", "sbtVersion": "1.0" }
|[info] { "groupId": "com.eed3si9n", "artifactId": "sbt-assembly", "artifactIdCross": "sbt-assembly", "version": "0.14.8", "sbtVersion": "1.0", "configurations": "foo" }
|[info] { "groupId": "com.geirsson", "artifactId": "sbt-scalafmt", "artifactIdCross": "sbt-scalafmt", "version": "1.6.0-RC4", "sbtVersion": "1.0" }
|""".stripMargin.linesIterator.toList
parseDependencies(lines) shouldBe List(
Dependency(

0 comments on commit 065c498

Please sign in to comment.
You can’t perform that action at this time.