Skip to content

Commit

Permalink
Automatically handle MiMa for all versions
Browse files Browse the repository at this point in the history
  • Loading branch information
mdedetrich committed Aug 3, 2023
1 parent ebb98f1 commit 7194968
Showing 1 changed file with 75 additions and 18 deletions.
93 changes: 75 additions & 18 deletions project/MiMa.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
*/

import scala.collection.immutable
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.matching.Regex.Groups

import sbt._
import sbt.Keys._
import sbt.librarymanagement.Http.http
import gigahorse.GigahorseSupport.url
import com.typesafe.tools.mima.plugin.MimaPlugin
import com.typesafe.tools.mima.plugin.MimaPlugin.autoImport._

object MiMa extends AutoPlugin {

private val latestPatchOf10 = 0

override def requires = MimaPlugin
override def trigger = allRequirements

Expand All @@ -29,35 +33,88 @@ object MiMa extends AutoPlugin {

override val projectSettings = Seq(
mimaReportSignatureProblems := true,
mimaPreviousArtifacts := pekkoPreviousArtifacts(name.value, organization.value),
checkMimaFilterDirectories := checkFilterDirectories(baseDirectory.value))
mimaPreviousArtifacts := pekkoPreviousArtifacts(name.value, organization.value, version.value,
scalaBinaryVersion.value),
checkMimaFilterDirectories := checkFilterDirectories(baseDirectory.value, version.value))

def checkFilterDirectories(moduleRoot: File): Unit = {
def checkFilterDirectories(moduleRoot: File, version: String): Unit = {
val patchVersion = patchFromVersion(version).toInt
val nextVersionFilterDir =
moduleRoot / "src" / "main" / "mima-filters" / s"1.0.${latestPatchOf10 + 1}.backwards.excludes"
moduleRoot / "src" / "main" / "mima-filters" / s"1.0.${patchVersion + 1}.backwards.excludes"
if (nextVersionFilterDir.exists()) {
throw new IllegalArgumentException(s"Incorrect mima filter directory exists: '$nextVersionFilterDir' " +
s"should be with number from current release '${moduleRoot / "src" / "main" / "mima-filters" / s"1.0.$latestPatchOf10.backwards.excludes"}")
s"should be with number from current release '${moduleRoot / "src" / "main" / "mima-filters" / s"1.0.$patchVersion.backwards.excludes"}")
}
}

private def patchFromVersion(version: String): String =
version.split("\\.").last

private def latestPatchOfReleasedVersion(projectName: String, organization: String, scalaBinaryVersion: String,
major: Long,
minor: Long): Option[Int] = {
val orgWithSlashes = organization.replaceAll("\\.", "/")

val baseUrl = s"${Resolver.DefaultMavenRepositoryRoot}$orgWithSlashes/${projectName}_$scalaBinaryVersion/"

val fullResponse = Await.result(http.processFull(url(baseUrl)), 10.seconds)

fullResponse.status match {
case s if s / 100 == 2 =>
val versionR = s"""href="$major.$minor.(\\d+)""".r

versionR.findAllMatchIn(fullResponse.bodyAsString).map {
case Groups(patchVersion) =>
patchVersion
}.toArray.lastOption.map(_.toInt)
case s if s == 404 =>
// Its possible for us to get non deployed artifacts as an input
// to this function which results in 404
None
case s =>
throw new IllegalArgumentException(s"Unexpected status code: $s for url: $baseUrl")
}
}

def allArtifactsInPreviousMinorRelease(minor: Long, projectName: String, organization: String,
scalaBinaryVersion: String, major: Long): Set[ModuleID] = {
val previousMinor = minor - 1
latestPatchOfReleasedVersion(projectName, organization, scalaBinaryVersion, major, previousMinor).map(
previousLatestPatchVersion =>
expandVersions(major, previousMinor, 0 to previousLatestPatchVersion).map(v =>
organization %% projectName % v).toSet).getOrElse(Set.empty)
}

def pekkoPreviousArtifacts(
projectName: String,
organization: String): Set[sbt.ModuleID] = {
val versions: Seq[String] = {
val firstPatchOf10 = 0
organization: String,
version: String,
scalaBinaryVersion: String): Set[sbt.ModuleID] = {
val Some((major, minor)) = CrossVersion.partialVersion(version)
val patchVersion = patchFromVersion(version)

val pekko10Previous = expandVersions(1, 0, 0 to latestPatchOf10)
val milestoneStartingPatchRegex = """(\d+)-M0(.*)""".r
val firstPatchThatIsRc = """0-RC(\d+)$""".r
val strippedPatchRegex = """(\d+)(.*)""".r

pekko10Previous
patchVersion match {
case milestoneStartingPatchRegex(_, _) =>
// We use M0 to signal that a bump in minor has happened however that minor
// has not been released yet, so lets compare to the entire previous minor series
allArtifactsInPreviousMinorRelease(minor, projectName, organization, scalaBinaryVersion, major)
case "0" | firstPatchThatIsRc(_) =>
// This case occurs when we are right at the point of a new release for a new version,
// including release candidates
allArtifactsInPreviousMinorRelease(minor, projectName, organization, scalaBinaryVersion, major)
case strippedPatchRegex(v, _) if v.toInt == 0 =>
// This case is when we currently have a snapshot with first version of minor being released
Set(organization %% projectName % s"$major.$minor.0")
case strippedPatchRegex(v, _) =>
// Standard case, lets check against all versions up until this one
expandVersions(major, minor, 0 until v.toInt).map(v => organization %% projectName % v).toSet
}

// check against all binary compatible artifacts
versions.map { v =>
organization %% projectName % v
}.toSet
}

private def expandVersions(major: Int, minor: Int, patches: immutable.Seq[Int]): immutable.Seq[String] =
private def expandVersions(major: Long, minor: Long, patches: immutable.Seq[Int]): immutable.Seq[String] =
patches.map(patch => s"$major.$minor.$patch")
}

0 comments on commit 7194968

Please sign in to comment.