Skip to content

Commit

Permalink
Support establishing the actual level of compatability
Browse files Browse the repository at this point in the history
  • Loading branch information
rtyley committed Oct 24, 2023
1 parent 322da0c commit a53f088
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
package sbtversionpolicy

import coursier.version.{ Version, VersionCompatibility }
import sbt.VersionNumber
import com.typesafe.tools.mima.plugin.MimaPlugin
import coursier.version.{Version, VersionCompatibility}
import sbt.{TaskKey, VersionNumber}
import sbtversionpolicy.SbtVersionPolicyPlugin.autoImport

/** Compatibility level between two version values.
*/
sealed trait Compatibility
sealed trait Compatibility {
val checkThatMustPassForCompatabilityLevel: Option[TaskKey[_]]
val shortDescription: String
}

object Compatibility {

/** There is NO source compatibility or binary compatibility.
*/
case object None extends Compatibility
case object None extends Compatibility {
override val checkThatMustPassForCompatabilityLevel: Option[TaskKey[_]] = scala.None
override val shortDescription: String = "not"
}

/** Binary compatibility only.
*/
case object BinaryCompatible extends Compatibility
case object BinaryCompatible extends Compatibility {
override val checkThatMustPassForCompatabilityLevel: Option[TaskKey[_]] =
Some(MimaPlugin.autoImport.mimaReportBinaryIssues)
override val shortDescription: String = "binary"
}

/** Binary and source compatibility.
*/
case object BinaryAndSourceCompatible extends Compatibility
case object BinaryAndSourceCompatible extends Compatibility {
override val checkThatMustPassForCompatabilityLevel: Option[TaskKey[_]] =
Some(autoImport.versionPolicyForwardCompatibilityCheck)
override val shortDescription: String = "binary and source"
}

// Ordered from least to MOST exacting
val Levels: Seq[Compatibility] = Seq(None, BinaryCompatible, BinaryAndSourceCompatible)

implicit val compatibilityOrdering: Ordering[Compatibility] = Ordering.by[Compatibility, Int](Levels.indexOf(_))

def apply(value1: String, value2: String, scheme: VersionCompatibility): Compatibility = {
def get(idx: Int, items: Vector[Version.Item]) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import sbt.librarymanagement.DependencyBuilders.OrganizationArtifactName
import scala.util.matching.Regex

trait SbtVersionPolicyKeys {
final val versionPolicyIntention = settingKey[Compatibility]("Compatibility intentions for the next release.")
final val versionPolicyIntention = settingKey[Compatibility]("Compatibility intentions for the next release.")
final val versionAssessMimaCompatibility = taskKey[Compatibility]("The compatability level of the code, based on the current state of the project.")
final val versionAssessDependencyCompatibility = taskKey[Compatibility]("The compatability level of the dependencies.")
final val versionAssessOverallCompatibility = taskKey[Compatibility]("The overall compatability level of the code & dependencies.")
final val versionPolicyPreviousArtifacts = taskKey[Seq[ModuleID]]("")
final val versionPolicyReportDependencyIssues = taskKey[Unit]("Check for removed or updated dependencies in an incompatible way.")
final val versionPolicyCheck = taskKey[Unit]("Runs both versionPolicyReportDependencyIssues and versionPolicyMimaCheck")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package sbtversionpolicy

import com.typesafe.tools.mima.plugin.{MimaPlugin, SbtMima}
import coursier.version.{ModuleMatchers, Version, VersionCompatibility}
import sbt._
import sbt.Keys._
import sbt.{Def, *}
import sbt.Keys.*
import sbt.librarymanagement.CrossVersion
import lmcoursier.CoursierDependencyResolution
import sbtversionpolicy.internal.{DependencyCheck, DependencySchemes, MimaIssues}
import sbtversionpolicy.SbtVersionPolicyMima.autoImport._
import sbtversionpolicy.SbtVersionPolicyMima.autoImport.*

import scala.math.Ordering.Implicits._
import scala.util.Try

object SbtVersionPolicySettings {
Expand Down Expand Up @@ -272,47 +273,44 @@ object SbtVersionPolicySettings {
}
else Compatibility.None
},
versionAssessMimaCompatibility := {
lastPopulatedValueOf(Compatibility.Levels.toList.flatMap { level =>
level.checkThatMustPassForCompatabilityLevel.map(_.result.map(_.toEither.toOption.map(_ => level)))
}).map(_.getOrElse(Compatibility.None)).value
},
versionPolicyMimaCheck := Def.taskDyn {
import Compatibility._
val compatibility =
val intendedCompatibility =
versionPolicyIntention.?.value
.getOrElse(throw new MessageOnlyException("Please set the key versionPolicyIntention to declare the compatibility you want to check"))
val log = streams.value.log
val currentModule = projectID.value
val currentModule = nameAndRevision(projectID.value)
val formattedPreviousVersions = formatVersions(versionPolicyPreviousVersions.value)
val actualCompat = versionAssessMimaCompatibility.value

val reportBackwardBinaryCompatibilityIssues: Def.Initialize[Task[Unit]] =
MimaPlugin.autoImport.mimaReportBinaryIssues.result.map(_.toEither.left.foreach { error =>
log.error(s"Module ${nameAndRevision(currentModule)} is not binary compatible with ${formattedPreviousVersions}. You have to relax your compatibility intention by changing the value of versionPolicyIntention.")
throw new MessageOnlyException(error.directCause.map(_.toString).getOrElse("mimaReportBinaryIssues failed"))
})
println(s"actualCompat=$actualCompat")

val reportForwardBinaryCompatibilityIssues: Def.Initialize[Task[Unit]] =
versionPolicyForwardCompatibilityCheck.result.map(_.toEither.left.foreach { error =>
log.error(s"Module ${nameAndRevision(currentModule)} is not source compatible with ${formattedPreviousVersions}. You have to relax your compatibility intention by changing the value of versionPolicyIntention.")
throw new MessageOnlyException(error.directCause.map(_.toString).getOrElse("versionPolicyForwardCompatibilityCheck failed"))
Def.task {
log.info(if (intendedCompatibility == Compatibility.None) {
s"Not checking compatibility of module $currentModule because versionPolicyIntention is set to 'Compatibility.None'"
} else {
s"Module $currentModule is ${actualCompat.shortDescription} compatible with $formattedPreviousVersions"
})

compatibility match {
case BinaryCompatible =>
reportBackwardBinaryCompatibilityIssues.map { _ =>
log.info(s"Module ${nameAndRevision(currentModule)} is binary compatible with ${formattedPreviousVersions}")
}
case BinaryAndSourceCompatible =>
Def.task {
val ignored1 = reportForwardBinaryCompatibilityIssues.value
val ignored2 = reportBackwardBinaryCompatibilityIssues.value
}.map { _ =>
log.info(s"Module ${nameAndRevision(currentModule)} is binary and source compatible with ${formattedPreviousVersions}")
}
case None => Def.task {
// skip mima if no compatibility is intented
log.info(s"Not checking compatibility of module ${nameAndRevision(currentModule)} because versionPolicyIntention is set to 'Compatibility.None'")
}
}
}.value
)

private def lastPopulatedValueOf[B](tasks: List[Def.Initialize[Task[Option[B]]]]): Def.Initialize[Task[Option[B]]] = {
tasks match {
case Nil => Def.task(None)
case x :: xs =>
Def.task {
val tv = x.value
if (tv.isDefined) lastPopulatedValueOf(xs).value.orElse(tv)
else None
}
}
}

def skipSettings = Seq(
versionCheck / skip := (publish / skip).value,
versionPolicyCheck / skip := (publish / skip).value
Expand Down

0 comments on commit a53f088

Please sign in to comment.