Skip to content

Commit

Permalink
Revert "Apply updates that differ only in their artifactId at the sam…
Browse files Browse the repository at this point in the history
…e time (#32)"

This reverts commit 79a97e5.
  • Loading branch information
fthomas committed Sep 18, 2018
1 parent 99be588 commit 418a4d2
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 143 deletions.
1 change: 0 additions & 1 deletion build.sbt
Expand Up @@ -29,7 +29,6 @@ lazy val core = myCrossProject("core")
Dependencies.catsEffect,
Dependencies.circeParser,
Dependencies.fs2Core,
Dependencies.refined,
Dependencies.scalaTest % Test
),
assembly / test := {}
Expand Down
103 changes: 34 additions & 69 deletions modules/core/src/main/scala/eu/timepit/scalasteward/model/Update.scala
Expand Up @@ -18,91 +18,57 @@ package eu.timepit.scalasteward.model

import cats.data.NonEmptyList
import cats.implicits._
import eu.timepit.refined.types.string.NonEmptyString
import eu.timepit.scalasteward.model.Update.{Group, Single}
import eu.timepit.scalasteward.util

import scala.util.matching.Regex

sealed trait Update extends Product with Serializable {
def groupId: String
def artifactId: String
def currentVersion: String
def newerVersions: NonEmptyList[String]
final case class Update(
groupId: String,
artifactId: String,
currentVersion: String,
newerVersions: NonEmptyList[String]
) {

/** Returns true if the changes of applying `other` would include the changes
* of applying `this`.
*/
def isImpliedBy(other: Update): Boolean =
groupId === other.groupId &&
artifactId =!= other.artifactId &&
artifactId.startsWith(Update.removeIgnorableSuffix(other.artifactId)) &&
currentVersion === other.currentVersion &&
newerVersions === other.newerVersions

def name: String =
if (Update.commonSuffixes.contains(artifactId))
groupId.split('.').lastOption.getOrElse(groupId)
else
artifactId
artifactId match {
case "core" => groupId.split('.').lastOption.getOrElse(groupId)
case _ => artifactId
}

def nextVersion: String =
newerVersions.head

def replaceAllIn(target: String): Option[String] = {
val quotedSearchTerms = searchTerms.map { term =>
def replaceAllIn(str: String): Option[String] = {
def normalize(searchTerm: String): String =
Regex
.quoteReplacement(Update.removeCommonSuffix(term))
.quoteReplacement(Update.removeIgnorableSuffix(searchTerm))
.replace("-", ".?")
}
val searchTerm = quotedSearchTerms.mkString_("(", "|", ")")
val regex = s"(?i)($searchTerm.*?)${Regex.quote(currentVersion)}".r

val regex =
s"(?i)(${normalize(name)}.*?)${Regex.quote(currentVersion)}".r
var updated = false
val result = regex.replaceAllIn(target, m => {
val result = regex.replaceAllIn(str, m => {
updated = true
m.group(1) + nextVersion
})
if (updated) Some(result) else None
}

def searchTerms: NonEmptyList[String] =
this match {
case s: Single => NonEmptyList.one(s.artifactId)
case g: Group => g.artifactIds.concat(g.artifactIdsPrefix.map(_.value).toList)
}

def show: String = {
val artifacts = this match {
case s: Single => s.artifactId
case g: Group => g.artifactIds.mkString_("{", ", ", "}")
}
val versions = (currentVersion :: newerVersions).mkString_("", " -> ", "")
s"$groupId:$artifacts : $versions"
}
def show: String =
s"$groupId:$artifactId : ${(currentVersion :: newerVersions).mkString_("", " -> ", "")}"
}

object Update {
final case class Single(
groupId: String,
artifactId: String,
currentVersion: String,
newerVersions: NonEmptyList[String]
) extends Update

final case class Group(
groupId: String,
artifactIds: NonEmptyList[String],
currentVersion: String,
newerVersions: NonEmptyList[String]
) extends Update {
override def artifactId: String =
artifactIds.head

def artifactIdsPrefix: Option[NonEmptyString] =
util.longestCommonNonEmptyPrefix(artifactIds)
}

///

def apply(
groupId: String,
artifactId: String,
currentVersion: String,
newerVersions: NonEmptyList[String]
): Single =
Single(groupId, artifactId, currentVersion, newerVersions)

def fromString(str: String): Either[Throwable, Single] =
def fromString(str: String): Either[Throwable, Update] =
Either.catchNonFatal {
val regex = """([^\s:]+):([^\s:]+)[^\s]*\s+:\s+([^\s]+)\s+->(.+)""".r
str match {
Expand All @@ -112,9 +78,8 @@ object Update {
}
}

val commonSuffixes: List[String] =
List("core", "server")

def removeCommonSuffix(str: String): String =
util.removeSuffix(str, commonSuffixes)
def removeIgnorableSuffix(str: String): String =
List("-core", "-server")
.find(suffix => str.endsWith(suffix))
.fold(str)(suffix => str.substring(0, str.length - suffix.length))
}
25 changes: 6 additions & 19 deletions modules/core/src/main/scala/eu/timepit/scalasteward/sbt.scala
Expand Up @@ -19,7 +19,6 @@ package eu.timepit.scalasteward
import better.files.File
import cats.effect.IO
import eu.timepit.scalasteward.model.Update
import cats.implicits._

object sbt {
def addGlobalPlugins(home: File): IO[Unit] =
Expand All @@ -39,29 +38,17 @@ object sbt {
io.firejail(sbtCmd :+ ";dependencyUpdates ;reload plugins; dependencyUpdates", dir)
.map(lines => sanitizeUpdates(toUpdates(lines)))

def sanitizeUpdates(updates: List[Update.Single]): List[Update] =
updates.distinct
.groupByNel(update => (update.groupId, update.currentVersion, update.newerVersions))
.values
.map { nel =>
val head = nel.head
if (nel.tail.nonEmpty)
Update.Group(
head.groupId,
nel.map(_.artifactId).sorted,
head.currentVersion,
head.newerVersions
)
else
head
}
.toList
def sanitizeUpdates(updates: List[Update]): List[Update] = {
val distinctUpdates = updates.distinct
distinctUpdates
.filterNot(update => distinctUpdates.exists(other => update.isImpliedBy(other)))
.sortBy(update => (update.groupId, update.artifactId))
}

val sbtCmd: List[String] =
List("sbt", "-no-colors")

def toUpdates(lines: List[String]): List[Update.Single] =
def toUpdates(lines: List[String]): List[Update] =
lines.flatMap { line =>
val trimmed = line.replace("[info]", "").trim
Update.fromString(trimmed).toSeq
Expand Down
20 changes: 0 additions & 20 deletions modules/core/src/main/scala/eu/timepit/scalasteward/util.scala
Expand Up @@ -17,29 +17,9 @@
package eu.timepit.scalasteward

import cats.Monad
import cats.data.NonEmptyList
import cats.implicits._
import eu.timepit.refined.types.string.NonEmptyString

object util {
def ifTrue[F[_]: Monad](fb: F[Boolean])(f: F[Unit]): F[Unit] =
fb.ifM(f, Monad[F].unit)

def longestCommonPrefix(s1: String, s2: String): String = {
var i = 0
val min = math.min(s1.length, s2.length)
while (i < min && s1(i) == s2(i)) i = i + 1
s1.substring(0, i)
}

def longestCommonPrefix(xs: NonEmptyList[String]): String =
xs.reduceLeft(longestCommonPrefix)

def longestCommonNonEmptyPrefix(xs: NonEmptyList[String]): Option[NonEmptyString] =
NonEmptyString.unapply(longestCommonPrefix(xs))

def removeSuffix(target: String, suffixes: List[String]): String =
suffixes
.find(suffix => target.endsWith(suffix))
.fold(target)(suffix => target.substring(0, target.length - suffix.length))
}
Expand Up @@ -106,30 +106,12 @@ class UpdateTest extends FunSuite with Matchers {
.replaceAllIn(original) shouldBe Some(expected)
}

test("replaceAllIn: group with prefix val") {
val original = """ val circe = "0.10.0-M1" """
val expected = """ val circe = "0.10.0-M2" """
Update
.Group(
"io.circe",
Nel.of("circe-generic", "circe-literal", "circe-parser", "circe-testing"),
"0.10.0-M1",
Nel.of("0.10.0-M2")
)
.replaceAllIn(original) shouldBe Some(expected)
}

test("replaceAllIn: group with repeated version") {
val original =
""" "com.pepegar" %% "hammock-core" % "0.8.1",
| "com.pepegar" %% "hammock-circe" % "0.8.1"
""".stripMargin.trim
val expected =
""" "com.pepegar" %% "hammock-core" % "0.8.5",
| "com.pepegar" %% "hammock-circe" % "0.8.5"
""".stripMargin.trim
Update
.Group("com.pepegar", Nel.of("hammock-core", "hammock-circe"), "0.8.1", Nel.of("0.8.5"))
.replaceAllIn(original) shouldBe Some(expected)
test("isImpliedBy") {
val update0 = Update("org.specs2", "specs2-core", "3.9.4", Nel.of("3.9.5"))
val update1 = update0.copy(artifactId = "specs2-scalacheck")
update0.isImpliedBy(update0) shouldBe false
update0.isImpliedBy(update1) shouldBe false
update1.isImpliedBy(update0) shouldBe true
update1.isImpliedBy(update1) shouldBe false
}
}
Expand Up @@ -8,14 +8,7 @@ class sbtTest extends FunSuite with Matchers {
test("sanitizeUpdates") {
val update0 = Update("org.specs2", "specs2-core", "3.9.4", Nel.of("3.9.5"))
val update1 = update0.copy(artifactId = "specs2-scalacheck")
sbt.sanitizeUpdates(List(update0, update1)) shouldBe List(
Update.Group(
"org.specs2",
Nel.of("specs2-core", "specs2-scalacheck"),
"3.9.4",
Nel.of("3.9.5")
)
)
sbt.sanitizeUpdates(List(update0, update1)) shouldBe List(update0)
}

test("toUpdates") {
Expand Down
1 change: 0 additions & 1 deletion project/Dependencies.scala
Expand Up @@ -5,6 +5,5 @@ object Dependencies {
val catsEffect = "org.typelevel" %% "cats-effect" % "1.0.0"
val circeParser = "io.circe" %% "circe-parser" % "0.10.0-M2"
val fs2Core = "co.fs2" %% "fs2-core" % "1.0.0-M5"
val refined = "eu.timepit" %% "refined" % "0.9.2"
val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5"
}

0 comments on commit 418a4d2

Please sign in to comment.