From fc2413f29bc2bb08f78ef368c35483e5f96025b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Tue, 9 Jan 2018 20:11:14 +0100 Subject: [PATCH] Version checking utility #24030 --- .../src/test/scala/akka/AkkaVersionSpec.scala | 81 +++++++++++++++++++ .../src/main/scala/akka/AkkaVersion.scala | 52 ++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 akka-actor-tests/src/test/scala/akka/AkkaVersionSpec.scala create mode 100644 akka-actor/src/main/scala/akka/AkkaVersion.scala diff --git a/akka-actor-tests/src/test/scala/akka/AkkaVersionSpec.scala b/akka-actor-tests/src/test/scala/akka/AkkaVersionSpec.scala new file mode 100644 index 0000000000..efd561f15c --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/AkkaVersionSpec.scala @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2009-2017 Lightbend Inc. + */ +package akka + +import org.scalatest.{ Matchers, WordSpec } + +class AkkaVersionSpec extends WordSpec with Matchers { + + "The Akka version check" must { + + "succeed if version is ok" in { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.6") + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.7") + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.6.0") + } + + "succeed if version is RC and ok" in { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.7-RC10") + } + + "fail if version is RC and not ok" in { + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.6-RC1") + } + } + + "succeed if version is milestone and ok" in { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.7-M10") + } + + "fail if version is milestone and not ok" in { + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.6-M1") + } + } + + "fail if major version is different" in { + // because not bincomp + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "3.0.0") + } + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "1.0.0") + } + } + + "fail if minor version is too low" in { + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.4.19") + } + } + + "fail if patch version is too low" in { + intercept[UnsupportedAkkaVersion] { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5.5") + } + } + + "succeed if Akka version is SNAPSHOT" in { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5-SNAPSHOT") + } + + "succeed if Akka version is timestamped SNAPSHOT" in { + AkkaVersion.require("AkkaVersionSpec", "2.5.6", "2.5-20180109-133700") + } + + "silently comply if current version is incomprehensible" in { + // because we may want to release with weird numbers for some reason + AkkaVersion.require("nonsense", "2.5.6", "nonsense") + } + + "fail if fed incomprehensible requirement" in { + intercept[IllegalArgumentException] { + AkkaVersion.require("AkkaVersionSpec", "nonsense", "2.5.6") + } + } + + } + +} diff --git a/akka-actor/src/main/scala/akka/AkkaVersion.scala b/akka-actor/src/main/scala/akka/AkkaVersion.scala new file mode 100644 index 0000000000..cfeb516760 --- /dev/null +++ b/akka-actor/src/main/scala/akka/AkkaVersion.scala @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009-2017 Lightbend Inc. + */ +package akka + +import akka.annotation.InternalApi + +import scala.annotation.varargs + +final class UnsupportedAkkaVersion private[akka] (msg: String) extends RuntimeException(msg) + +object AkkaVersion { + /** + * Check that the version of Akka is a specific patch version or higher and throw an [[UnsupportedAkkaVersion]] + * exception if the version requirement is not fulfilled. + * + * For example: `require("my-library", "2.5.4")` would fail if used with Akka 2.4.19 and 2.5.3, but succeed with 2.5.4 + * and 2.6.1 + * + * @param libraryName The name of the library or component requiring the Akka version, used in the error message. + * @param requiredVersion Minimal version that this library works with + */ + def require(libraryName: String, requiredVersion: String): Unit = { + require(libraryName, requiredVersion, Version.current) + } + + /** + * Internal API: + */ + @InternalApi + private[akka] def require(libraryName: String, requiredVersion: String, currentVersion: String): Unit = { + val VersionPattern = """(\d+)\.(\d+)\.(\d+)(-(?:M|RC)\d+)?""".r + currentVersion match { + case VersionPattern(currentMajorStr, currentMinorStr, currentPatchStr, mOrRc) ⇒ + requiredVersion match { + case requiredVersion @ VersionPattern(requiredMajorStr, requiredMinorStr, requiredPatchStr, _) ⇒ + // a M or RC is basically inbetween versions, so offset + val currentPatch = + if (mOrRc ne null) currentPatchStr.toInt - 1 + else currentPatchStr.toInt + if (requiredMajorStr.toInt != currentMajorStr.toInt || + requiredMinorStr.toInt > currentMinorStr.toInt || + (requiredMinorStr == currentMinorStr && requiredPatchStr.toInt > currentPatch)) + throw new UnsupportedAkkaVersion(s"Current version of Akka is [$currentVersion], but $libraryName requires version [$requiredVersion]") + case _ ⇒ throw new IllegalArgumentException(s"Required version string is invalid: [$requiredVersion]") + } + + case _ ⇒ // SNAPSHOT or unknown - you're on your own + } + } + +}