diff --git a/bleep-cli/src/scala/bleep/Main.scala b/bleep-cli/src/scala/bleep/Main.scala index ec83482e8..32d627472 100644 --- a/bleep-cli/src/scala/bleep/Main.scala +++ b/bleep-cli/src/scala/bleep/Main.scala @@ -122,6 +122,17 @@ object Main { val watch = Opts.flag("watch", "start in watch mode", "w").orFalse + val updateAsScalaSteward = Opts + .flag( + "steward", + "Use same upgrade strategy as Scala Steward. Updates to the latest patch version at the same minor and major version. If the dependency is already on the latest patch version, it updates to the latest minor version at the same major version. And if the dependency is already on the latest minor version, it updates to the latest major version." + ) + .orFalse + + val updateWithPrerelease = Opts.flag("prerelease", "Allow upgrading to prerelease version if there is any.").orFalse + + val updateSingleOrgOrModule = Opts.argument[String]("The dependency to update, alternatively only the organization name can be passed") + lazy val ret: Opts[BleepBuildCommand] = { val allCommands = List( List[Opts[BleepBuildCommand]]( @@ -140,7 +151,14 @@ object Main { Opts(commands.BuildReinferTemplates(Set.empty)) ), Opts.subcommand("update-deps", "updates to newest versions of all dependencies")( - Opts(commands.BuildUpdateDeps) + (updateAsScalaSteward, updateWithPrerelease).mapN { case (sw, prerelease) => + commands.BuildUpdateDeps.apply(sw, prerelease, None) + } + ), + Opts.subcommand("update-dep", "update a single dependency or dependencies of a single organization to newest version(s)")( + (updateSingleOrgOrModule, updateAsScalaSteward, updateWithPrerelease).mapN { case (singleDep, sw, prerelease) => + commands.BuildUpdateDeps.apply(sw, prerelease, Some(singleDep)) + } ), Opts.subcommand( "move-files-into-bleep-layout", diff --git a/bleep-cli/src/scala/bleep/commands/BuildUpdateDeps.scala b/bleep-cli/src/scala/bleep/commands/BuildUpdateDeps.scala index 47621b00f..9ff6b1a65 100644 --- a/bleep-cli/src/scala/bleep/commands/BuildUpdateDeps.scala +++ b/bleep-cli/src/scala/bleep/commands/BuildUpdateDeps.scala @@ -2,6 +2,7 @@ package bleep package commands import bleep.internal.writeYamlLogged +import bleep.internal.Version import bleep.rewrites.{normalizeBuild, UpgradeDependencies} import coursier.Repository import coursier.cache.FileCache @@ -13,10 +14,14 @@ import scala.collection.immutable import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext, Future} import scala.util.Success +import cats.parse.Parser +import bleep.model.Build +import bleep.model.{Dep, VersionCombo} + +case class BuildUpdateDeps(scalaStewardMode: Boolean, allowPrerelease: Boolean, singleDep: Option[String]) extends BleepBuildCommand { -case object BuildUpdateDeps extends BleepBuildCommand { override def run(started: Started): Either[BleepException, Unit] = { - val build = started.build.requireFileBacked("command update-deps") + val build: Build.FileBacked = started.build.requireFileBacked("command update-deps") // a bleep dependency may be instantiated into several different coursier dependencies // depending on which scala versions and platforms are plugging in // collect all instantiations into this structure @@ -31,12 +36,16 @@ case object BuildUpdateDeps extends BleepBuildCommand { Await.result(fetchAllVersions(fileCache, repos, allDeps), Duration.Inf) } - val upgrades: Map[UpgradeDependencies.ContextualDep, model.Dep] = - foundByDep.flatMap { case (tuple @ (bleepDep, _), (_, version)) => - val latest = version.latest - if (latest == bleepDep.version) None - else Some(tuple -> bleepDep.withVersion(latest)) - } + DependencyUpgrader.depsToUpgrade(singleDep, foundByDep, scalaStewardMode, allowPrerelease).flatMap { toUpdate => + runUpdates(started, build, toUpdate) + } + } + + private def runUpdates( + started: Started, + build: Build.FileBacked, + upgrades: Map[(Dep, VersionCombo), Dep] + ): Right[BleepException, Unit] = { val newBuild = UpgradeDependencies(new UpgradeLogger(started.logger), upgrades)(build, started.buildPaths) val newBuild1 = normalizeBuild(newBuild, started.buildPaths) @@ -87,4 +96,62 @@ case object BuildUpdateDeps extends BleepBuildCommand { } case Nil => Future.successful(None) } + +} + +object DependencyUpgrader { + private val sepParser = Parser.char(':').rep.void + + val singleDepParser = Parser.anyChar.repUntil(sepParser).string ~ (sepParser *> Parser.anyChar.repUntil(Parser.end).string).? + + def depsToUpgrade( + singleDep: Option[String], + foundByDep: Map[UpgradeDependencies.ContextualDep, (Dependency, Versions)], + scalaStewardMode: Boolean, + allowPrerelease: Boolean + ) = { + val singleDepParsed = singleDep.map(dep => singleDepParser.parseAll(dep)) + + singleDepParsed match { + case None => Right(findUpgrades(foundByDep, scalaStewardMode, allowPrerelease)) + case Some(parsedDep) => + filterDependencies(foundByDep, parsedDep, singleDep.getOrElse("")).map { filtered => + findUpgrades(filtered, scalaStewardMode, allowPrerelease) + } + } + } + + def filterDependencies( + foundByDep: Map[UpgradeDependencies.ContextualDep, (Dependency, Versions)], + parsedDep: Either[Parser.Error, (String, Option[String])], + depName: String + ): Either[BleepException.Text, Map[UpgradeDependencies.ContextualDep, (Dependency, Versions)]] = + parsedDep match { + case Left(_) => Left(new BleepException.Text(s"${depName} is not a valid dependency name")) + case Right((org, module)) => + val toUpdate = foundByDep.filter { case ((bleepDep, _), _) => + module.map(bleepDep.baseModuleName.value.equalsIgnoreCase).getOrElse(true) && bleepDep.organization.value.equalsIgnoreCase(org) + } + if (toUpdate.isEmpty) { + Left(new BleepException.Text(s"$depName is not a dependency identifier in this build")) + } else { + Right(toUpdate) + } + } + + def findUpgrades( + foundByDep: Map[UpgradeDependencies.ContextualDep, (Dependency, Versions)], + scalaStewardMode: Boolean, + allowPrerelease: Boolean + ): Map[(Dep, VersionCombo), Dep] = + foundByDep.flatMap { case (tuple @ (bleepDep, _), (_, coursierVersion)) => + val version = Version(bleepDep.version) + val latest = + if (scalaStewardMode) { + version.selectNext(coursierVersion.available.map(Version.apply), allowPrerelease) + } else { + version.selectLatest(coursierVersion.available.map(Version.apply), allowPrerelease) + } + latest.map(latestV => tuple -> bleepDep.withVersion(latestV.value)) + } } diff --git a/bleep-cli/src/scala/bleep/internal/Version.scala b/bleep-cli/src/scala/bleep/internal/Version.scala new file mode 100644 index 000000000..04c20d130 --- /dev/null +++ b/bleep-cli/src/scala/bleep/internal/Version.scala @@ -0,0 +1,240 @@ +/* + * Copyright 2018-2023 Scala Steward contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package bleep + +package internal + +import bleep.internal.Version.startsWithDate +import cats.Order +import cats.implicits._ +import cats.parse.{Numbers, Parser, Rfc5234} + +final case class Version(value: String) { + override def toString: String = value + + private val components: List[Version.Component] = + Version.Component.parse(value) + + val alnumComponents: List[Version.Component] = + components.filter(_.isAlphanumeric) + + /** Select the newest possible version for the dependency */ + def selectLatest(versions: List[Version], preRelease: Boolean): Option[Version] = { + def recur(current: Version): Version = + current.selectNext(versions, false) match { + case None => current + case Some(value) => recur(value) + } + + val next: Option[Version] = selectNext(versions, false).map(recur) + + if (preRelease) + next.flatMap(_.selectNext(versions, true)).orElse(next) + else next + } + + /** Selects the next version from a list of potentially newer versions. + * + * Implements the scheme described in this FAQ: + * https://github.com/scala-steward-org/scala-steward/blob/main/docs/faq.md#how-does-scala-steward-decide-what-version-it-is-updating-to + */ + def selectNext(versions: List[Version], allowPreReleases: Boolean = false): Option[Version] = { + val cutoff = alnumComponentsWithoutPreRelease.length - 1 + val newerVersionsByCommonPrefix = + if (this.isPreRelease && allowPreReleases) { + versions.groupBy(_ => List.empty) + } else { + versions + .filter(_ > this) + .groupBy(_.alnumComponents.zip(alnumComponents).take(cutoff).takeWhile { case (c1, c2) => + c1 === c2 + }) + } + + newerVersionsByCommonPrefix.toList + .sortBy { case (commonPrefix, _) => commonPrefix.length } + .flatMap { case (commonPrefix, vs) => + val sameSeries = cutoff === commonPrefix.length + + val preReleasesFilter: Version => Boolean = v => + // Do not select snapshots if we are not already on snapshot + v.isSnapshot && !isSnapshot + + val releasesFilter: Version => Boolean = v => + // Do not select pre-releases of different series. + (v.isPreRelease && !sameSeries) || + // Do not select pre-releases of the same series if this is not a pre-release. + (v.isPreRelease && !isPreRelease && sameSeries) || + // Do not select versions with pre-release identifiers whose order is smaller + // than the order of possible pre-release identifiers in this version. This, + // for example, prevents updates from 2.1.4.0-RC17 to 2.1.4.0-RC17+1-307f2f6c-SNAPSHOT. + (v.minAlphaOrder < minAlphaOrder) || + // Do not select a version with hash if this version contains no hash. + (v.containsHash && !containsHash) + + val commonFilter: Version => Boolean = v => + // Do not select versions that are identical up to the hashes. + v.alnumComponents === alnumComponents || + // Don't select "versions" like %5BWARNING%5D. + !v.startsWithLetterOrDigit + + vs.filterNot { v => + commonFilter(v) || + (!allowPreReleases && releasesFilter(v)) || + (allowPreReleases && preReleasesFilter(v)) + }.sorted + } + .lastOption + } + + private def startsWithLetterOrDigit: Boolean = + components.headOption.forall { + case _: Version.Component.Numeric => true + case a: Version.Component.Alpha => a.value.headOption.forall(_.isLetter) + case _ => false + } + + private def isPreRelease: Boolean = + components.exists { + case a: Version.Component.Alpha => a.isPreReleaseIdent + case _: Version.Component.Hash => true + case _ => false + } + + private def isSnapshot: Boolean = + components.exists { + case a: Version.Component.Alpha => a.isSnapshotIdent + case _: Version.Component.Hash => true + case _ => false + } + + private def containsHash: Boolean = + components.exists { + case _: Version.Component.Hash => true + case _ => false + } || Rfc5234.hexdig.rep(8).string.filterNot(startsWithDate).parse(value).isRight + + private[this] def alnumComponentsWithoutPreRelease: List[Version.Component] = + alnumComponents.takeWhile { + case a: Version.Component.Alpha => !a.isPreReleaseIdent + case _ => true + } + + private val minAlphaOrder: Int = + alnumComponents.collect { case a: Version.Component.Alpha => a.order }.minOption.getOrElse(0) +} + +object Version { + + implicit val versionOrder: Order[Version] = + Order.from[Version] { (v1, v2) => + val (c1, c2) = padToSameLength(v1.alnumComponents, v2.alnumComponents, Component.Empty) + c1.compare(c2) + } + + private def padToSameLength[A](l1: List[A], l2: List[A], elem: A): (List[A], List[A]) = { + val maxLength = math.max(l1.length, l2.length) + (l1.padTo(maxLength, elem), l2.padTo(maxLength, elem)) + } + + private def startsWithDate(s: String): Boolean = + s.length >= 8 && s.substring(0, 8).forall(_.isDigit) && { + val year = s.substring(0, 4).toInt + val month = s.substring(4, 6).toInt + val day = s.substring(6, 8).toInt + (year >= 1900 && year <= 2100) && + (month >= 1 && month <= 12) && + (day >= 1 && day <= 31) + } + + sealed trait Component extends Product with Serializable { + final def isAlphanumeric: Boolean = + this match { + case _: Component.Numeric => true + case _: Component.Alpha => true + case _ => false + } + } + object Component { + final case class Numeric(value: String) extends Component { + def toBigInt: BigInt = BigInt(value) + } + final case class Alpha(value: String) extends Component { + def isPreReleaseIdent: Boolean = order < 0 + def isSnapshotIdent: Boolean = order <= -6 + def order: Int = + value.toUpperCase match { + case "SNAP" | "SNAPSHOT" | "NIGHTLY" => -6 + case "ALPHA" | "PREVIEW" => -5 + case "BETA" | "B" => -4 + case "EA" /* early access */ => -3 + case "M" | "MILESTONE" | "AM" => -2 + case "RC" => -1 + case _ => 0 + } + } + final case class Hash(value: String) extends Component + final case class Separator(c: Char) extends Component + case object Empty extends Component + + private val componentsParser = { + val digits = ('0' to '9').toSet + val separators = Set('.', '-', '_', '+') + + val numeric = Numbers.digits.map(s => List(Numeric(s))) + val alpha = Parser.charsWhile(c => !digits(c) && !separators(c)).map(s => List(Alpha(s))) + val separator = Parser.charIn(separators).map(c => List(Separator(c))) + val hash = (Parser.charIn('-', '+') ~ + Parser.char('g').string.? ~ + Rfc5234.hexdig.rep(6).string.filterNot(startsWithDate)).backtrack + .map { case ((s, g), h) => List(Separator(s), Hash(g.getOrElse("") + h)) } + + (numeric | alpha | hash | separator).rep0.map(_.flatten) + } + + def parse(str: String): List[Component] = + componentsParser.parseAll(str).getOrElse(List.empty) + + def render(components: List[Component]): String = + components.map { + case n: Numeric => n.value + case a: Alpha => a.value + case h: Hash => h.value + case s: Separator => s.c.toString + case Empty => "" + }.mkString + + // This is similar to https://get-coursier.io/docs/other-version-handling.html#ordering + // but not exactly the same ordering as used by Coursier. One difference is that we are + // using different pre-release identifiers. + implicit val componentOrder: Order[Component] = + Order.from[Component] { + case (n1: Numeric, n2: Numeric) => n1.toBigInt.compare(n2.toBigInt) + case (_: Numeric, _) => 1 + case (_, _: Numeric) => -1 + + case (a1: Alpha, a2: Alpha) => + val (o1, o2) = (a1.order, a2.order) + if (o1 < 0 || o2 < 0) o1.compare(o2) else a1.value.compare(a2.value) + + case (_: Alpha, Empty) => -1 + case (Empty, _: Alpha) => 1 + + case _ => 0 + } + } +} diff --git a/bleep-site-in/usage/dependencies.mdx b/bleep-site-in/usage/dependencies.mdx index 706ebcbf0..512ce4668 100644 --- a/bleep-site-in/usage/dependencies.mdx +++ b/bleep-site-in/usage/dependencies.mdx @@ -72,6 +72,21 @@ This lets you build sbt plugins with bleep (note that publishing is not implemen module: org.scala-sbt:sbt:1.5.5 ``` +## Updating dependencies +bleep can update it's own dependencies using cli commands. +```shell +bleep build update-deps +``` +will update all the dependencies of a build. It is also possible to specify a single dependency or dependencies of a single organization to upgrade using the command +```shell +bleep build update-dep com.example::foo +# Alternatively for all dependencies of the organization +bleep build update-dep com.example +``` +The update commands support two flags: + - --prerelease which updates to the latest prerelease version if possible + - --steward which makes the update command update to the version according to the same strategy that Scala Steward uses. Detailed description of the strategy can be found [here](https://github.com/scala-steward-org/scala-steward/blob/main/docs/faq.md#how-does-scala-steward-decide-what-version-it-is-updating-to). + ## Limitations -Note that Bleep has shed support for the most intricate ivyisms. What is left makes the structure much more cacheable, and should be enough for 99% of projects. \ No newline at end of file +Note that Bleep has shed support for the most intricate ivyisms. What is left makes the structure much more cacheable, and should be enough for 99% of projects. diff --git a/bleep-tests/src/scala/bleep/DependencyUpdateTest.scala b/bleep-tests/src/scala/bleep/DependencyUpdateTest.scala new file mode 100644 index 000000000..45d11d1f5 --- /dev/null +++ b/bleep-tests/src/scala/bleep/DependencyUpdateTest.scala @@ -0,0 +1,566 @@ +package bleep + +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers._ +import bleep.commands.DependencyUpgrader +import org.scalactic.TripleEqualsSupport +import coursier.core.Dependency +import coursier.core.Organization +import coursier.core.ModuleName +import bleep.model.Dep +import bleep.model.VersionCombo +import bleep.model.VersionScala +import coursier.core.Versions +import coursier.core.Versions.DateTime +import bleep.rewrites.UpgradeDependencies + +class DependencyUpdateTest extends AnyFunSuite with TripleEqualsSupport { + + test("parses single dependency") { + val dependency = "org.http4s::http4s-core" + DependencyUpgrader.singleDepParser.parseAll(dependency) match { + + case Left(_) => assert(false) + case Right(value) => + val org = value._1 + val module = value._2.getOrElse("") + + (org, module) shouldBe ("org.http4s", "http4s-core") + } + } + + test("parses single java dependency") { + val dependency = "org.springframework:spring-boot-starter-web" + DependencyUpgrader.singleDepParser.parseAll(dependency) match { + + case Left(_) => assert(false) + case Right(value) => + val org = value._1 + val module = value._2.getOrElse("") + + (org, module) shouldBe ("org.springframework", "spring-boot-starter-web") + } + } + test("parses single dependency - only organization") { + val dependency = "org.http4s" + DependencyUpgrader.singleDepParser.parseAll(dependency) match { + case Left(_) => assert(false) + case Right(value) => + val org = value._1 + val module = value._2 + + (org, module) shouldBe ("org.http4s", None) + } + } + + test("find upgrades with no single dep defined") { + + val upgrades = DependencyUpgrader.depsToUpgrade(None, DependencyUpdateTestFixture.foundByDep, false, false) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => assert(value.size === 4) + } + + } + + test("find upgrades with single dep") { + + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.http4s::http4s-dsl"), DependencyUpdateTestFixture.foundByDep, false, false) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => + val moduleName = value.head._2.baseModuleName.value + (value.size, moduleName) shouldBe (1, "http4s-dsl") + } + } + + test("find upgrades with single dep - org only") { + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.http4s"), DependencyUpdateTestFixture.foundByDep, false, false) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => + val modules = List("http4s-dsl", "http4s-core") + assert(value.values.map(_.baseModuleName.value) === modules) + } + } + + test("find upgrades with no matching deps gives error") { + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.nottoday"), DependencyUpdateTestFixture.foundByDep, false, false) + + assert(upgrades.isLeft) + } + + test("correctly update to newest non-prerelease") { + + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.http4s::http4s-dsl"), DependencyUpdateTestFixture.foundByDep, false, false) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => + val version = value.head._2.version + assert(version === "0.23.28") + } + } + + test("correctly updates java-dep") { + + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.springframework:spring-boot-starter-web"), DependencyUpdateTestFixture.foundByDep, false, false) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => + val version = value.head._2.version + assert(version === "3.3.4") + } + } + test("correctly update to newest prerelease") { + + val upgrades = DependencyUpgrader.depsToUpgrade(Some("org.http4s::http4s-dsl"), DependencyUpdateTestFixture.foundByDep, false, true) + + upgrades match { + case Left(_) => assert(false) + case Right(value) => + val version = value.head._2.version + assert(version === "1.0.0-M41") + } + } +} + +object DependencyUpdateTestFixture { + val springVersions = Versions( + "3.3.4", + "3.3.4", + List( + "1.0.0.RELEASE", + "1.0.1.RELEASE", + "1.0.2.RELEASE", + "1.1.0.RELEASE", + "1.1.1.RELEASE", + "1.1.2.RELEASE", + "1.1.3.RELEASE", + "1.1.4.RELEASE", + "1.1.5.RELEASE", + "1.1.6.RELEASE", + "1.1.7.RELEASE", + "1.1.8.RELEASE", + "1.1.9.RELEASE", + "1.1.10.RELEASE", + "1.1.11.RELEASE", + "1.1.12.RELEASE", + "1.2.0.RELEASE", + "1.2.1.RELEASE", + "1.2.2.RELEASE", + "1.2.3.RELEASE", + "1.2.4.RELEASE", + "1.2.5.RELEASE", + "1.2.6.RELEASE", + "1.2.7.RELEASE", + "1.2.8.RELEASE", + "1.3.0.RELEASE", + "1.3.1.RELEASE", + "1.3.2.RELEASE", + "1.3.3.RELEASE", + "1.3.4.RELEASE", + "1.3.5.RELEASE", + "1.3.6.RELEASE", + "1.3.7.RELEASE", + "1.3.8.RELEASE", + "1.4.0.RELEASE", + "1.4.1.RELEASE", + "1.4.2.RELEASE", + "1.4.3.RELEASE", + "1.4.4.RELEASE", + "1.4.5.RELEASE", + "1.4.6.RELEASE", + "1.4.7.RELEASE", + "1.5.0.RELEASE", + "1.5.1.RELEASE", + "1.5.2.RELEASE", + "1.5.3.RELEASE", + "1.5.4.RELEASE", + "1.5.5.RELEASE", + "1.5.6.RELEASE", + "1.5.7.RELEASE", + "1.5.8.RELEASE", + "1.5.9.RELEASE", + "1.5.10.RELEASE", + "1.5.11.RELEASE", + "1.5.12.RELEASE", + "1.5.13.RELEASE", + "1.5.14.RELEASE", + "1.5.15.RELEASE", + "1.5.16.RELEASE", + "1.5.17.RELEASE", + "1.5.18.RELEASE", + "1.5.19.RELEASE", + "1.5.20.RELEASE", + "1.5.21.RELEASE", + "1.5.22.RELEASE", + "2.0.0.RELEASE", + "2.0.1.RELEASE", + "2.0.2.RELEASE", + "2.0.3.RELEASE", + "2.0.4.RELEASE", + "2.0.5.RELEASE", + "2.0.6.RELEASE", + "2.0.7.RELEASE", + "2.0.8.RELEASE", + "2.0.9.RELEASE", + "2.1.0.RELEASE", + "2.1.1.RELEASE", + "2.1.2.RELEASE", + "2.1.3.RELEASE", + "2.1.4.RELEASE", + "2.1.5.RELEASE", + "2.1.6.RELEASE", + "2.1.7.RELEASE", + "2.1.8.RELEASE", + "2.1.9.RELEASE", + "2.1.10.RELEASE", + "2.1.11.RELEASE", + "2.1.12.RELEASE", + "2.1.13.RELEASE", + "2.1.14.RELEASE", + "2.1.15.RELEASE", + "2.1.16.RELEASE", + "2.1.17.RELEASE", + "2.1.18.RELEASE", + "2.2.0.RELEASE", + "2.2.1.RELEASE", + "2.2.2.RELEASE", + "2.2.3.RELEASE", + "2.2.4.RELEASE", + "2.2.5.RELEASE", + "2.2.6.RELEASE", + "2.2.7.RELEASE", + "2.2.8.RELEASE", + "2.2.9.RELEASE", + "2.2.10.RELEASE", + "2.2.11.RELEASE", + "2.2.12.RELEASE", + "2.2.13.RELEASE", + "2.3.0.RELEASE", + "2.3.1.RELEASE", + "2.3.2.RELEASE", + "2.3.3.RELEASE", + "2.3.4.RELEASE", + "2.3.5.RELEASE", + "2.3.6.RELEASE", + "2.3.7.RELEASE", + "2.3.8.RELEASE", + "2.3.9.RELEASE", + "2.3.10.RELEASE", + "2.3.11.RELEASE", + "2.3.12.RELEASE", + "2.4.0", + "2.4.1", + "2.4.2", + "2.4.3", + "2.4.4", + "2.4.5", + "2.4.6", + "2.4.7", + "2.4.8", + "2.4.9", + "2.4.10", + "2.4.11", + "2.4.12", + "2.4.13", + "2.5.0", + "2.5.1", + "2.5.2", + "2.5.3", + "2.5.4", + "2.5.5", + "2.5.6", + "2.5.7", + "2.5.8", + "2.5.9", + "2.5.10", + "2.5.11", + "2.5.12", + "2.5.13", + "2.5.14", + "2.5.15", + "2.6.0", + "2.6.1", + "2.6.2", + "2.6.3", + "2.6.4", + "2.6.5", + "2.6.6", + "2.6.7", + "2.6.8", + "2.6.9", + "2.6.10", + "2.6.11", + "2.6.12", + "2.6.13", + "2.6.14", + "2.6.15", + "2.7.0", + "2.7.1", + "2.7.2", + "2.7.3", + "2.7.4", + "2.7.5", + "2.7.6", + "2.7.7", + "2.7.8", + "2.7.9", + "2.7.10", + "2.7.11", + "2.7.12", + "2.7.13", + "2.7.14", + "2.7.15", + "2.7.16", + "2.7.17", + "2.7.18", + "3.0.0", + "3.0.1", + "3.0.2", + "3.0.3", + "3.0.4", + "3.0.5", + "3.0.6", + "3.0.7", + "3.0.8", + "3.0.9", + "3.0.10", + "3.0.11", + "3.0.12", + "3.0.13", + "3.1.0", + "3.1.1", + "3.1.2", + "3.1.3", + "3.1.4", + "3.1.5", + "3.1.6", + "3.1.7", + "3.1.8", + "3.1.9", + "3.1.10", + "3.1.11", + "3.1.12", + "3.2.0", + "3.2.1", + "3.2.2", + "3.2.3", + "3.2.4", + "3.2.5", + "3.2.6", + "3.2.7", + "3.2.8", + "3.2.9", + "3.2.10", + "3.3.0", + "3.3.1", + "3.3.2", + "3.3.3", + "3.3.4" + ), + Some(DateTime(2024, 9, 19, 10, 54, 26)) + ) + + val http4sVersions = Versions( + "1.0-234-d1a2b53", + "1.0-234-d1a2b53", + List( + "0.10.0-M10", + "0.21.0-M1", + "0.21.0-M2", + "0.21.0-M3", + "0.21.0-M4", + "0.21.0-M5", + "0.21.0-M6", + "0.21.0-RC1", + "0.21.0-RC2", + "0.21.0-RC3", + "0.21.0-RC4", + "0.21.0-RC5", + "0.21.0", + "0.21.1", + "0.21.2", + "0.21.3", + "0.21.4", + "0.21.5", + "0.21.6", + "0.21.7", + "0.21.8", + "0.21.9", + "0.21.11", + "0.21.12", + "0.21.13", + "0.21.14", + "0.21.15", + "0.21.16", + "0.21.17", + "0.21.18", + "0.21.19", + "0.21.20", + "0.21.21", + "0.21.22", + "0.21.23", + "0.21.24", + "0.21.25", + "0.21.26", + "0.21.27", + "0.21.28", + "0.21.29", + "0.21.30", + "0.21.31", + "0.21.33", + "0.21.34", + "0.22.0-M1", + "0.22.0-M2", + "0.22.0-M3", + "0.22.0-M4", + "0.22.0-M5", + "0.22.0-M6", + "0.22.0-M7", + "0.22.0-M8", + "0.22.0-RC1", + "0.22.0", + "0.22.1", + "0.22.2", + "0.22.3", + "0.22.4", + "0.22.5", + "0.22.6", + "0.22.7", + "0.22.8", + "0.22.9", + "0.22.10", + "0.22.11", + "0.22.12", + "0.22.13", + "0.22.14", + "0.22.15", + "0.22-53-01128f5", + "0.22-96-55d3184", + "0.22-129-24d065b", + "0.22-143-49b5a8d", + "0.23.0-M1", + "0.23.0-RC1", + "0.23.0", + "0.23.1", + "0.23.2", + "0.23.3", + "0.23.4", + "0.23.5", + "0.23.6", + "0.23.7", + "0.23.8", + "0.23.9", + "0.23.10", + "0.23.11", + "0.23.12", + "0.23.13", + "0.23.14", + "0.23.15", + "0.23.16", + "0.23.17", + "0.23.18", + "0.23.19-RC1", + "0.23.19-RC2", + "0.23.19-RC3", + "0.23.19", + "0.23.20", + "0.23.21", + "0.23.22", + "0.23.23", + "0.23.24", + "0.23.25", + "0.23.26", + "0.23.27", + "0.23.28", + "1.0.0-M2", + "1.0.0-M3", + "1.0.0-M4", + "1.0.0-M5", + "1.0.0-M6", + "1.0.0-M7", + "1.0.0-M8", + "1.0.0-M9", + "1.0.0-M10", + "1.0.0-M11", + "1.0.0-M13", + "1.0.0-M14", + "1.0.0-M15", + "1.0.0-M16", + "1.0.0-M17", + "1.0.0-M18", + "1.0.0-M19", + "1.0.0-M20", + "1.0.0-M21", + "1.0.0-M22", + "1.0.0-M23", + "1.0.0-M24", + "1.0.0-M25", + "1.0.0-M27", + "1.0.0-M28", + "1.0.0-M29", + "1.0.0-M30", + "1.0.0-M31", + "1.0.0-M32", + "1.0.0-M33", + "1.0.0-M34", + "1.0.0-M35", + "1.0.0-M36", + "1.0.0-M37", + "1.0.0-M38", + "1.0.0-M39", + "1.0.0-M40", + "1.0.0-M41", + "1.0-2-1e49ccf", + "1.0-2-79831c5", + "1.0-4-a7fc4a9", + "1.0-6-e439945", + "1.0-10-df008e1", + "1.0-12-e6a1f1f", + "1.0-14-cdeb1e3", + "1.0-14-7c5ec00", + "1.0-16-b673b49", + "1.0-18-a497d1a", + "1.0-18-659c58b", + "1.0-21-a0087dd", + "1.0-23-f99b0af", + "1.0-25-f1a4ab0", + "1.0-37-9661f07", + "1.0-39-5736fdc", + "1.0-57-0368053", + "1.0-81-ce14ddb", + "1.0-103-5f6e845", + "1.0-107-6676c1e", + "1.0-231-e9b2b41", + "1.0-232-85dadc2", + "1.0-234-d1a2b53" + ), + Some(DateTime(2024, 9, 9, 18, 53, 55)) + ) + val http4sCore: (UpgradeDependencies.ContextualDep, (Dependency, Versions)) = ( + (Dep.Scala("org.http4s", "http4s-core", "0.21.0"), VersionCombo.Jvm(VersionScala("2.13.12"))), + (Dependency(coursier.Module(Organization("org.http4s"), ModuleName("http4s-core"), Map.empty), "0.21.0"), http4sVersions) + ) + val http4sDsl: (UpgradeDependencies.ContextualDep, (Dependency, Versions)) = ( + (Dep.Scala("org.http4s", "http4s-dsl", "0.21.0"), VersionCombo.Jvm(VersionScala("2.13.12"))), + (Dependency(coursier.Module(Organization("org.http4s"), ModuleName("http4s-dsl"), Map.empty), "0.21.0"), http4sVersions) + ) + + val someOtherDep: (UpgradeDependencies.ContextualDep, (Dependency, Versions)) = ( + (Dep.Scala("org.other", "http4s-dsl", "0.21.0"), VersionCombo.Jvm(VersionScala("2.13.12"))), + (Dependency(coursier.Module(Organization("org.other"), ModuleName("http4s-dsl"), Map.empty), "0.21.0"), http4sVersions) + ) + + val springBootWeb: (UpgradeDependencies.ContextualDep, (Dependency, Versions)) = ( + (Dep.Scala("org.springframework", "spring-boot-starter-web", "3.0.0"), VersionCombo.Jvm(VersionScala("2.13.12"))), + (Dependency(coursier.Module(Organization("org.springframework"), ModuleName("spring-boot-starter-web"), Map.empty), "3.0.0"), springVersions) + ) + val foundByDep = List(http4sDsl, http4sCore, someOtherDep, springBootWeb).toMap + +} diff --git a/bleep.yaml b/bleep.yaml index bc2bdb7bd..3f251d7c0 100644 --- a/bleep.yaml +++ b/bleep.yaml @@ -11,6 +11,7 @@ projects: - org.scalameta:svm-subs:101.0.0 # note: weird binary incompatibility when bumping this for scala3 - org.typelevel::cats-core:2.9.0 + - org.typelevel::cats-parse:1.0.0 dependsOn: bleep-core extends: - template-common diff --git a/snapshot-tests/bloop/resolve-cache.json.gz b/snapshot-tests/bloop/resolve-cache.json.gz index 95e1e8fca..7572e3062 100644 Binary files a/snapshot-tests/bloop/resolve-cache.json.gz and b/snapshot-tests/bloop/resolve-cache.json.gz differ diff --git a/snapshot-tests/converter/resolve-cache.json.gz b/snapshot-tests/converter/resolve-cache.json.gz index 38df7aa0b..b72053126 100644 Binary files a/snapshot-tests/converter/resolve-cache.json.gz and b/snapshot-tests/converter/resolve-cache.json.gz differ diff --git a/snapshot-tests/create-new-build/resolve-cache.json.gz b/snapshot-tests/create-new-build/resolve-cache.json.gz index 8647a8a51..13dc00e1e 100644 Binary files a/snapshot-tests/create-new-build/resolve-cache.json.gz and b/snapshot-tests/create-new-build/resolve-cache.json.gz differ diff --git a/snapshot-tests/doobie/resolve-cache.json.gz b/snapshot-tests/doobie/resolve-cache.json.gz index 786e63dba..254c860bb 100644 Binary files a/snapshot-tests/doobie/resolve-cache.json.gz and b/snapshot-tests/doobie/resolve-cache.json.gz differ diff --git a/snapshot-tests/http4s/resolve-cache.json.gz b/snapshot-tests/http4s/resolve-cache.json.gz index 0ffc1d10c..d5754856d 100644 Binary files a/snapshot-tests/http4s/resolve-cache.json.gz and b/snapshot-tests/http4s/resolve-cache.json.gz differ diff --git a/snapshot-tests/sbt/resolve-cache.json.gz b/snapshot-tests/sbt/resolve-cache.json.gz index 67e747b2c..e3f82ee54 100644 Binary files a/snapshot-tests/sbt/resolve-cache.json.gz and b/snapshot-tests/sbt/resolve-cache.json.gz differ diff --git a/snapshot-tests/scalameta/resolve-cache.json.gz b/snapshot-tests/scalameta/resolve-cache.json.gz index 42513aefd..571f8da3a 100644 Binary files a/snapshot-tests/scalameta/resolve-cache.json.gz and b/snapshot-tests/scalameta/resolve-cache.json.gz differ diff --git a/snapshot-tests/tapir/resolve-cache.json.gz b/snapshot-tests/tapir/resolve-cache.json.gz index b145bd0b4..31e38f5d2 100644 Binary files a/snapshot-tests/tapir/resolve-cache.json.gz and b/snapshot-tests/tapir/resolve-cache.json.gz differ