Split Installer Versions in Manifest (#1355)

This commit is contained in:
Radosław Waśko 2020-12-16 12:34:33 +01:00 committed by GitHub
parent bf37754428
commit 2e6a5af4fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 226 additions and 47 deletions

View File

@ -15,7 +15,7 @@ val scalacVersion = "2.13.3"
val rustVersion = "1.40.0-nightly (b520af6fd 2019-11-03)"
val graalVersion = "20.2.0"
val javaVersion = "11"
val ensoVersion = "0.1.0" // Note [Engine And Launcher Version]
val ensoVersion = "0.1.0-SNAPSHOT" // Note [Engine And Launcher Version]
/* Note [Engine And Launcher Version]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,4 +1,5 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 0.0.1
jvm-options:
- value: "-Dpolyglot.engine.IterativePartialEscape=true"
- value: "-Dtruffle.class.path.append=$enginePackagePath\\component\\runtime.jar"

View File

@ -107,11 +107,18 @@ root of an Enso version package. It has at least the following fields:
- `minimum-launcher-version` - specifies the minimum version of the launcher
that should be used with this release of Enso,
- `minimum-project-manager-version` - specifies the minimum version of the
project manager that should be used with this release of Enso; currently it is
the same as the launcher version but this may change in the future,
- `graal-vm-version` - specifies the exact version of GraalVM that should be
used with this release of Enso,
- `graal-java-version` - as GraalVM versions may have different variants for
different Java versions, this specifies which variant to use.
The minimum launcher and project manager versions are kept as separate fields,
because at some point the same runtime version management logic may be
associated with different versions of these components.
It can also contain the following additional fields:
- `jvm-options` - specifies a list of options that should be passed to the JVM
@ -131,8 +138,7 @@ For example:
```yaml
minimum-launcher-version: 0.0.1
graal-vm-version: 20.2.0
graal-java-version: 11
minimum-project-manager-version: 0.0.1
jvm-options:
- value: "-Dpolyglot.engine.IterativePartialEscape=true"
- value: "-Dtruffle.class.path.append=$enginePackagePath\\component\\runtime.jar"
@ -141,6 +147,8 @@ jvm-options:
os: "linux"
- value: "-Dtruffle.class.path.append=$enginePackagePath/component/runtime.jar"
os: "macos"
graal-vm-version: 20.2.0
graal-java-version: 11
```
The `minimum-launcher-version` should be updated whenever a new version of Enso

View File

@ -4,7 +4,10 @@ import org.enso.launcher.cli.{
CLIRuntimeVersionManagementUserInterface,
GlobalCLIOptions
}
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
import org.enso.runtimeversionmanager.components.{
InstallerKind,
RuntimeVersionManager
}
import org.enso.runtimeversionmanager.distribution.{
PortableDistributionManager,
TemporaryDirectoryManager
@ -54,6 +57,7 @@ object DefaultManagers {
temporaryDirectoryManager,
defaultResourceManager,
EngineRepository.defaultEngineReleaseProvider,
GraalCEReleaseProvider
GraalCEReleaseProvider,
InstallerKind.Launcher
)
}

View File

@ -21,7 +21,7 @@ class DefaultVersionSpec extends RuntimeVersionManagerTest {
def makeConfigurationManager(): GlobalConfigurationManager =
makeConfigAndComponentsManagers()._2
private val latestAvailable = SemVer(0, 0, 1)
private val latestAvailable = SemVer(0, 1, 0)
"enso default" should {
"fallback to latest available version if none is installed" in {

View File

@ -1,6 +1,6 @@
name: Cycle_Test
version: 0.0.1
enso-version: 0.1.1-rc5
license: ""
author: ""
maintainer: ""
license: APLv2
enso-version: default
version: "0.0.1"
author: "Enso Team <contact@enso.org>"
maintainer: "Enso Team <contact@enso.org>"

View File

@ -2,6 +2,7 @@ package org.enso.projectmanager.versionmanagement
import org.enso.runtimeversionmanager.Environment
import org.enso.runtimeversionmanager.components.{
InstallerKind,
RuntimeVersionManagementUserInterface,
RuntimeVersionManager
}
@ -62,7 +63,8 @@ object DefaultDistributionConfiguration extends DistributionConfiguration {
temporaryDirectoryManager = temporaryDirectoryManager,
resourceManager = resourceManager,
engineReleaseProvider = engineReleaseProvider,
runtimeReleaseProvider = runtimeReleaseProvider
runtimeReleaseProvider = runtimeReleaseProvider,
installerKind = InstallerKind.ProjectManager
)
/** @inheritdoc */

View File

@ -4,6 +4,7 @@ import java.nio.file.Path
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
import org.enso.runtimeversionmanager.components.{
InstallerKind,
RuntimeVersionManagementUserInterface,
RuntimeVersionManager
}
@ -74,7 +75,8 @@ class TestDistributionConfiguration(
temporaryDirectoryManager = temporaryDirectoryManager,
resourceManager = resourceManager,
engineReleaseProvider = engineReleaseProvider,
runtimeReleaseProvider = runtimeReleaseProvider
runtimeReleaseProvider = runtimeReleaseProvider,
installerKind = InstallerKind.ProjectManager
)
/** JVM settings that will force to use the same JVM that we are running.

View File

@ -44,6 +44,7 @@ class EngineManagementApiSpec extends BaseServerSpec with FlakySpec {
"result" : {
"versions" : [
{"version": "0.999.0-broken", "markedAsBroken": true},
{"version": "0.1.0", "markedAsBroken": false},
{"version": "0.0.1", "markedAsBroken": false},
{"version": "0.0.1-pre", "markedAsBroken": false},
{"version": "0.0.0", "markedAsBroken": false}

View File

@ -0,0 +1,37 @@
package org.enso.projectmanager.protocol
import io.circe.literal.JsonStringContext
import org.enso.projectmanager.BaseServerSpec
class ProjectCreateDefaultToLatestSpec extends BaseServerSpec {
"project/open" should {
"default to latest available engine version if none are installed" in {
implicit val client = new WsTestClient(address)
client.send(json"""
{ "jsonrpc": "2.0",
"method": "project/create",
"id": 1,
"params": {
"name": "testproj",
"missingComponentAction": "Install"
}
}
""")
/** The error here is expected (only the latest version will give this
* error), we just wanted to check the logic for selecting the latest
* version, not installing.
*/
val message =
"Project manager 999.0.0 is required to install the requested " +
"engine. Please upgrade."
client.expectJson(json"""
{
"jsonrpc":"2.0",
"id":1,
"error": { "code": 4022, "message": $message }
}
""")
}
}
}

View File

@ -18,7 +18,7 @@ class ProjectOpenMissingComponentsSpec
with RetrySpec
with ProjectManagementOps
with MissingComponentBehavior {
val ordinaryVersion = SemVer(0, 0, 1)
val ordinaryVersion: SemVer = SemVer(0, 0, 1)
override val engineToInstall: Option[SemVer] = Some(ordinaryVersion)
var ordinaryProject: UUID = _
var brokenProject: UUID = _

View File

@ -1,4 +1,5 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 0.0.1
graal-vm-version: 1.0.0
graal-java-version: 11
jvm-options:

View File

@ -1,3 +1,4 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 0.0.1
graal-vm-version: 2.0.0
graal-java-version: 11

View File

@ -1,4 +1,5 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 0.0.1
graal-vm-version: 2.0.0
graal-java-version: 11
jvm-options:

View File

@ -0,0 +1,13 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 999.0.0
graal-vm-version: 2.0.0
graal-java-version: 11
jvm-options:
- value: "-Dpolyglot.engine.IterativePartialEscape=true"
- value: "-Dtruffle.class.path.append=$enginePackagePath\\component\\runtime.jar"
os: "windows"
- value: "-Dtruffle.class.path.append=$enginePackagePath/component/runtime.jar"
os: "linux"
- value: "-Dtruffle.class.path.append=$enginePackagePath/component/runtime.jar"
os: "macos"
- value: "-Denso.version.override=0.1.0"

View File

@ -1,3 +1,4 @@
minimum-launcher-version: 0.0.1
minimum-project-manager-version: 0.0.1
graal-vm-version: 2.0.0
graal-java-version: 11

View File

@ -6,6 +6,7 @@ import nl.gn0s1s.bump.SemVer
import org.enso.pkg.{PackageManager, SemVerEnsoVersion}
import org.enso.runtimeversionmanager._
import org.enso.runtimeversionmanager.components.{
InstallerKind,
RuntimeVersionManagementUserInterface,
RuntimeVersionManager
}
@ -38,7 +39,8 @@ class RuntimeVersionManagerTest
def makeManagers(
environmentOverrides: Map[String, String] = Map.empty,
userInterface: RuntimeVersionManagementUserInterface =
TestRuntimeVersionManagementUserInterface.default
TestRuntimeVersionManagementUserInterface.default,
installerKind: InstallerKind = InstallerKind.Launcher
): (DistributionManager, RuntimeVersionManager, Environment) = {
val env = fakeInstalledEnvironment(environmentOverrides)
val distributionManager = new PortableDistributionManager(env)
@ -53,7 +55,8 @@ class RuntimeVersionManagerTest
temporaryDirectoryManager,
resourceManager,
FakeReleases.engineReleaseProvider,
FakeReleases.runtimeReleaseProvider
FakeReleases.runtimeReleaseProvider,
installerKind
)
(distributionManager, runtimeVersionManager, env)

View File

@ -14,7 +14,7 @@ class RuntimeVersionManagerSpec extends RuntimeVersionManagerTest {
"find the latest engine version in semver ordering " +
"(skipping broken releases)" in {
val componentsManager = makeRuntimeVersionManager()
componentsManager.fetchLatestEngineVersion() shouldEqual SemVer(0, 0, 1)
componentsManager.fetchLatestEngineVersion() shouldEqual SemVer(0, 1, 0)
}
"install the engine and a matching runtime for it" in {
@ -143,5 +143,38 @@ class RuntimeVersionManagerSpec extends RuntimeVersionManagerTest {
componentsManager.listInstalledEngines() should have length 0
componentsManager.listInstalledGraalRuntimes() should have length 0
}
"correctly handle version depending on installer type" in {
val projectManager =
makeManagers(installerKind = InstallerKind.ProjectManager)._2
val launcher =
makeManagers(installerKind = InstallerKind.Launcher)._2
val engineWithDifferentVersionRequirements = SemVer(0, 1, 0)
val manifest =
launcher
.findOrInstallEngine(engineWithDifferentVersionRequirements)
.manifest
val usualVersion = SemVer(0, 0, 1)
val bigVersion = SemVer(999, 0, 0)
manifest.requiredInstallerVersions.launcher shouldEqual usualVersion
manifest.requiredInstallerVersions.projectManager shouldEqual bigVersion
manifest.minimumRequiredVersion(installerKind =
InstallerKind.Launcher
) shouldEqual usualVersion
manifest.minimumRequiredVersion(installerKind =
InstallerKind.ProjectManager
) shouldEqual bigVersion
val upgradeException = intercept[UpgradeRequiredError] {
projectManager.findOrInstallEngine(
engineWithDifferentVersionRequirements
)
}
upgradeException.expectedVersion shouldEqual bigVersion
}
}
}

View File

@ -8,6 +8,7 @@ import org.enso.runtimeversionmanager.FileSystem.PathSyntax
import org.enso.runtimeversionmanager._
import org.enso.runtimeversionmanager.components.{
GraalVMVersion,
InstallerKind,
Manifest,
RuntimeVersionManager
}
@ -151,7 +152,8 @@ class ConcurrencyTest
temporaryDirectoryManager,
resourceManager,
engineProvider,
runtimeProvider
runtimeProvider,
InstallerKind.Launcher
)
(distributionManager, componentsManager, temporaryDirectoryManager)

View File

@ -15,8 +15,7 @@ object CurrentVersion {
throw new IllegalStateException("Cannot parse the built-in version.")
}
/** Version of the launcher.
*/
/** Version of the component. */
def version: SemVer = currentVersion
/** Override launcher version with the provided one.

View File

@ -0,0 +1,18 @@
package org.enso.runtimeversionmanager.components
/** Describes the kind of installer that can use the `runtime-version-manager`.
*
* Currently there are two installers: the launcher and the project manager.
* This [[InstallerKind]] is used to distinguish them as a given version of the
* `runtime-version-manager` may be related to different versions of these
* installers.
*/
sealed trait InstallerKind
object InstallerKind {
/** The [[InstallerKind]] representing the launcher. */
case object Launcher extends InstallerKind
/** The [[InstallerKind]] representing the project manager. */
case object ProjectManager extends InstallerKind
}

View File

@ -6,20 +6,28 @@ import java.nio.file.Path
import cats.Show
import io.circe.{yaml, Decoder}
import nl.gn0s1s.bump.SemVer
import org.enso.runtimeversionmanager.components.Manifest.JVMOption
import org.enso.runtimeversionmanager.{components, CurrentVersion, OS}
import org.enso.pkg.SemVerJson._
import org.enso.runtimeversionmanager.components.Manifest.{
JVMOption,
RequiredInstallerVersions
}
import org.enso.runtimeversionmanager.{components, OS}
import scala.util.{Failure, Try, Using}
/** Contains release metadata read from the manifest file that is attached to
* each release.
*
* @param minimumLauncherVersion The minimum required version of the launcher
* that can be used to run this engine release.
* Earlier launcher versions should not be able
* to download this release, but print a message
* that the launcher needs an upgrade.
* @param requiredInstallerVersions The minimum required version of the
* installer that ensures its runtime version
* management logic is compatible with this
* particular engine release. Earlier
* installer versions should not be able to
* download this release (as the logic for
* installing it has probably changed).
* Instead they should print a message that
* the installer should be upgraded to use
* that version.
* @param graalVMVersion the version of the GraalVM runtime that has to be
* used with this engine
* @param graalJavaVersion the java version of that GraalVM runtime
@ -27,7 +35,7 @@ import scala.util.{Failure, Try, Using}
* this engine
*/
case class Manifest(
minimumLauncherVersion: SemVer,
requiredInstallerVersions: RequiredInstallerVersions,
graalVMVersion: SemVer,
graalJavaVersion: String,
jvmOptions: Seq[JVMOption],
@ -40,12 +48,25 @@ case class Manifest(
def runtimeVersion: GraalVMVersion =
components.GraalVMVersion(graalVMVersion, graalJavaVersion)
def isUsableWithCurrentVersion: Boolean =
CurrentVersion.version >= minimumLauncherVersion
/** Returns the minimum required version of the installer that is required for
* using this particular engine release.
*/
def minimumRequiredVersion(implicit installerKind: InstallerKind): SemVer =
installerKind match {
case InstallerKind.Launcher =>
requiredInstallerVersions.launcher
case InstallerKind.ProjectManager =>
requiredInstallerVersions.projectManager
}
}
object Manifest {
/** Stores minimum required versions of each installer kind described in the
* [[Manifest]].
*/
case class RequiredInstallerVersions(launcher: SemVer, projectManager: SemVer)
/** Defines the name under which the manifest is included in the releases.
*/
val DEFAULT_MANIFEST_NAME = "manifest.yaml"
@ -165,17 +186,21 @@ object Manifest {
}
object Fields {
val minimumLauncherVersion = "minimum-launcher-version"
val jvmOptions = "jvm-options"
val graalVMVersion = "graal-vm-version"
val graalJavaVersion = "graal-java-version"
val brokenMark = "broken"
val minimumLauncherVersion = "minimum-launcher-version"
val minimumProjectManagerVersion = "minimum-project-manager-version"
val jvmOptions = "jvm-options"
val graalVMVersion = "graal-vm-version"
val graalJavaVersion = "graal-java-version"
val brokenMark = "broken"
}
implicit private val decoder: Decoder[Manifest] = { json =>
for {
minimumLauncherVersion <- json.get[SemVer](Fields.minimumLauncherVersion)
graalVMVersion <- json.get[SemVer](Fields.graalVMVersion)
minimumProjectManagerVersion <- json.get[SemVer](
Fields.minimumProjectManagerVersion
)
graalVMVersion <- json.get[SemVer](Fields.graalVMVersion)
graalJavaVersion <-
json
.get[String](Fields.graalJavaVersion)
@ -183,11 +208,14 @@ object Manifest {
jvmOptions <- json.getOrElse[Seq[JVMOption]](Fields.jvmOptions)(Seq())
broken <- json.getOrElse[Boolean](Fields.brokenMark)(false)
} yield Manifest(
minimumLauncherVersion = minimumLauncherVersion,
graalVMVersion = graalVMVersion,
graalJavaVersion = graalJavaVersion,
jvmOptions = jvmOptions,
brokenMark = broken
requiredInstallerVersions = RequiredInstallerVersions(
launcher = minimumLauncherVersion,
projectManager = minimumProjectManagerVersion
),
graalVMVersion = graalVMVersion,
graalJavaVersion = graalJavaVersion,
jvmOptions = jvmOptions,
brokenMark = broken
)
}
}

View File

@ -4,7 +4,7 @@ import java.nio.file.{Files, Path, StandardOpenOption}
import com.typesafe.scalalogging.Logger
import nl.gn0s1s.bump.SemVer
import org.enso.runtimeversionmanager.FileSystem
import org.enso.runtimeversionmanager.{CurrentVersion, FileSystem}
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
import org.enso.runtimeversionmanager.archive.Archive
import org.enso.runtimeversionmanager.distribution.{
@ -42,7 +42,8 @@ class RuntimeVersionManager(
temporaryDirectoryManager: TemporaryDirectoryManager,
resourceManager: ResourceManager,
engineReleaseProvider: ReleaseProvider[EngineRelease],
runtimeReleaseProvider: GraalVMRuntimeReleaseProvider
runtimeReleaseProvider: GraalVMRuntimeReleaseProvider,
implicit private val installerKind: InstallerKind
) {
private val logger = Logger[RuntimeVersionManager]
@ -333,6 +334,14 @@ class RuntimeVersionManager(
cleanupGraalRuntimes()
}
/** Checks if the component version specified in the release's manifest is
* compatible with the current installer version.
*/
private def isEngineVersionCompatibleWithThisInstaller(
manifest: Manifest
): Boolean =
CurrentVersion.version >= manifest.minimumRequiredVersion
/** Installs the engine with the provided version.
*
* Used internally by [[findOrInstallEngine]]. Does not check if the engine
@ -349,8 +358,11 @@ class RuntimeVersionManager(
*/
private def installEngine(version: SemVer): Engine = {
val engineRelease = engineReleaseProvider.fetchRelease(version).get
if (!engineRelease.manifest.isUsableWithCurrentVersion) {
throw UpgradeRequiredError(engineRelease.manifest.minimumLauncherVersion)
val isCompatible = isEngineVersionCompatibleWithThisInstaller(
engineRelease.manifest
)
if (!isCompatible) {
throw UpgradeRequiredError(engineRelease.manifest.minimumRequiredVersion)
}
if (engineRelease.isBroken) {
val continue = userInterface.shouldInstallBrokenEngine(version)
@ -588,8 +600,8 @@ class RuntimeVersionManager(
path: Path
): Try[Manifest] = {
Manifest.load(path / Manifest.DEFAULT_MANIFEST_NAME).flatMap { manifest =>
if (!manifest.isUsableWithCurrentVersion) {
Failure(UpgradeRequiredError(manifest.minimumLauncherVersion))
if (!isEngineVersionCompatibleWithThisInstaller(manifest)) {
Failure(UpgradeRequiredError(manifest.minimumRequiredVersion))
} else Success(manifest)
}
}