mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
Define Editions (#1797)
This commit is contained in:
parent
c819f42130
commit
241a1e7d74
7
.github/workflows/nightly.yml
vendored
7
.github/workflows/nightly.yml
vendored
@ -65,6 +65,13 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: repo
|
||||
- name: Configure Pagefile (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: al-cheb/configure-pagefile-action@v1.2
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
- name: Enable Developer Command Prompt (Windows)
|
||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||
- name: Disable TCP/UDP Offloading (macOS)
|
||||
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -32,6 +32,13 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: repo
|
||||
- name: Configure Pagefile (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: al-cheb/configure-pagefile-action@v1.2
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
- name: Enable Developer Command Prompt (Windows)
|
||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||
- name: Disable TCP/UDP Offloading (macOS)
|
||||
|
7
.github/workflows/scala.yml
vendored
7
.github/workflows/scala.yml
vendored
@ -27,6 +27,13 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Configure Pagefile (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: al-cheb/configure-pagefile-action@v1.2
|
||||
with:
|
||||
minimum-size: 16GB
|
||||
maximum-size: 16GB
|
||||
disk-root: "C:"
|
||||
- name: Enable Developer Command Prompt (Windows)
|
||||
uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||
- name: Setup Go
|
||||
|
12
RELEASES.md
12
RELEASES.md
@ -39,6 +39,18 @@
|
||||
- Added support for multiple content roots in the language server
|
||||
([#1800](https://github.com/enso-org/enso/pull/1800/)). It is not yet exposed
|
||||
to the IDE, as this will be done as part of future work.
|
||||
- Modified the `package.yaml` format in preparation for the library ecosystem
|
||||
([#1797](https://github.com/enso-org/enso/pull/1797)). The `engine-version`
|
||||
field has been deprecated in favour of an `edition` field that allows to set
|
||||
up the engine version and dependency resolution using the upcoming Edition
|
||||
system. New tools will still be able to read the old format, but upon
|
||||
modification, they will save changes in the new format. As the `edition` file
|
||||
did not exist in the older version, old tools will actually correctly load the
|
||||
migrated package file (as we allow for unknown fields), but they will not know
|
||||
how to interpret the new `edition` field and so will fall back to using the
|
||||
`default` engine version, which may be unexpected. Ideally, after migration,
|
||||
the project should be used only with the new tools. The affected tools are the
|
||||
Launcher and the Project Manager.
|
||||
|
||||
## Libraries
|
||||
|
||||
|
56
build.sbt
56
build.sbt
@ -583,11 +583,11 @@ lazy val pkg = (project in file("lib/scala/pkg"))
|
||||
version := "0.1",
|
||||
libraryDependencies ++= circe ++ Seq(
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||
"nl.gn0s1s" %% "bump" % bumpVersion,
|
||||
"io.circe" %% "circe-yaml" % circeYamlVersion, // separate from other circe deps because its independent project with its own versioning
|
||||
"commons-io" % "commons-io" % commonsIoVersion
|
||||
)
|
||||
)
|
||||
.dependsOn(editions)
|
||||
|
||||
lazy val `akka-native` = project
|
||||
.in(file("lib/scala/akka-native"))
|
||||
@ -728,9 +728,12 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
||||
)
|
||||
.dependsOn(`akka-native`)
|
||||
.dependsOn(`version-output`)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(editions)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`polyglot-api`)
|
||||
.dependsOn(`runtime-version-manager`)
|
||||
.dependsOn(`library-manager`)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(`json-rpc-server`)
|
||||
.dependsOn(`json-rpc-server-test` % Test)
|
||||
.dependsOn(testkit % Test)
|
||||
@ -883,6 +886,7 @@ lazy val `polyglot-api` = project
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(`text-buffer`)
|
||||
.dependsOn(`logging-utils`)
|
||||
.dependsOn(testkit % Test)
|
||||
|
||||
lazy val `language-server` = (project in file("engine/language-server"))
|
||||
.settings(
|
||||
@ -1184,8 +1188,53 @@ lazy val launcher = project
|
||||
.dependsOn(`version-output`)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(`logging-service`)
|
||||
.dependsOn(`distribution-manager` % Test)
|
||||
.dependsOn(`runtime-version-manager-test` % Test)
|
||||
|
||||
lazy val `distribution-manager` = project
|
||||
.in(file("lib/scala/distribution-manager"))
|
||||
.configs(Test)
|
||||
.settings(
|
||||
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||
libraryDependencies ++= Seq(
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||
"io.circe" %% "circe-yaml" % circeYamlVersion,
|
||||
"commons-io" % "commons-io" % commonsIoVersion,
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
)
|
||||
)
|
||||
.dependsOn(editions)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(`logging-utils`)
|
||||
|
||||
lazy val editions = project
|
||||
.in(file("lib/scala/editions"))
|
||||
.configs(Test)
|
||||
.settings(
|
||||
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||
libraryDependencies ++= Seq(
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||
"nl.gn0s1s" %% "bump" % bumpVersion,
|
||||
"io.circe" %% "circe-yaml" % circeYamlVersion,
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
)
|
||||
)
|
||||
|
||||
lazy val `library-manager` = project
|
||||
.in(file("lib/scala/library-manager"))
|
||||
.configs(Test)
|
||||
.settings(
|
||||
resolvers += Resolver.bintrayRepo("gn0s1s", "releases"),
|
||||
libraryDependencies ++= Seq(
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
)
|
||||
)
|
||||
.dependsOn(editions)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`distribution-manager`)
|
||||
.dependsOn(testkit % Test)
|
||||
|
||||
lazy val `runtime-version-manager` = project
|
||||
.in(file("lib/scala/runtime-version-manager"))
|
||||
.configs(Test)
|
||||
@ -1204,6 +1253,7 @@ lazy val `runtime-version-manager` = project
|
||||
.dependsOn(`logging-service`)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`version-output`)
|
||||
.dependsOn(`distribution-manager`)
|
||||
|
||||
lazy val `runtime-version-manager-test` = project
|
||||
.in(file("lib/scala/runtime-version-manager-test"))
|
||||
@ -1224,6 +1274,8 @@ lazy val `runtime-version-manager-test` = project
|
||||
.dependsOn(`runtime-version-manager`)
|
||||
.dependsOn(`logging-service`)
|
||||
.dependsOn(testkit)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`distribution-manager`)
|
||||
|
||||
lazy val `locking-test-helper` = project
|
||||
.in(file("lib/scala/locking-test-helper"))
|
||||
|
@ -26,6 +26,11 @@ The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `com.thoughtworks.paranamer.paranamer-2.8`.
|
||||
|
||||
|
||||
'slf4j-api', licensed under the MIT License, is distributed with the engine.
|
||||
The license file can be found at `licenses/MIT`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.slf4j.slf4j-api-1.7.26`.
|
||||
|
||||
|
||||
'izumi-reflect_2.13', licensed under the BSD-style, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `dev.zio.izumi-reflect_2.13-1.0.0-M5`.
|
||||
@ -246,11 +251,6 @@ The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.scala-logging.scala-logging_2.13-3.9.2`.
|
||||
|
||||
|
||||
'slf4j-api', licensed under the MIT License, is distributed with the engine.
|
||||
The license file can be found at `licenses/MIT`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.slf4j.slf4j-api-1.7.25`.
|
||||
|
||||
|
||||
'commons-cli', licensed under the Apache License, Version 2.0, is distributed with the engine.
|
||||
The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `commons-cli.commons-cli-1.4`.
|
||||
|
@ -1,19 +1,3 @@
|
||||
/*
|
||||
* Copyright 2008 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2013 Google LLC
|
||||
*
|
||||
@ -43,3 +27,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
* Copyright 2016 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2016 The Error Prone Authors.
|
||||
* Copyright 2015 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -47,7 +47,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2015 The Error Prone Authors.
|
||||
* Copyright 2014 The Error Prone Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
@ -15,8 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
||||
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
||||
@ -36,3 +34,5 @@ Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||
* Copyright (C) 2011 Mathias Doenitz
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Mathias Doenitz
|
||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||
*/
|
||||
|
@ -15,8 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
|
||||
* Copyright 2017-2018 Łukasz Biały, Paul Chiusano, Michael Pilquist,
|
||||
@ -36,3 +34,5 @@ Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Copyright 2017-2019 John A. De Goes and the ZIO Contributors
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||
* Copyright (C) 2011 Mathias Doenitz
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Mathias Doenitz
|
||||
* Copyright (C) 2011,2012 Mathias Doenitz, Johannes Rudolph
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -47,3 +47,4 @@ It is broken up into categories as follows:
|
||||
- [**Parser:**](./parser) Design and specification of the Enso parser.
|
||||
- [**Infrastructure:**](./infrastructure) Description of the infrastructure for
|
||||
building Enso.
|
||||
- [**Libraries:**](./libraries) Description of Enso's Library Ecosystem.
|
||||
|
@ -20,7 +20,7 @@ structured and how it should behave.
|
||||
- [Installing from a Portable Distribution](#installing-from-a-portable-distribution)
|
||||
- [Layout of an Enso Version Package](#layout-of-an-enso-version-package)
|
||||
- [Standard Library](#standard-library)
|
||||
- [Resolvers](#resolvers)
|
||||
- [Enso Home Layout](#enso-home-layout)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
@ -64,19 +64,19 @@ extraction-location
|
||||
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
||||
│ └── <truncated>
|
||||
├── runtime # A directory storing distributions of the JVM used by the Enso distributions.
|
||||
│ └── graalvm-ce-27.1.1
|
||||
├── lib
|
||||
│ └── src # Contains sources of downloaded libraries.
|
||||
│ └── Dataframe # Each library may be stored in multiple version.
|
||||
│ └── graalvm-ce-java11-27.1.1
|
||||
├── lib # Contains sources of downloaded libraries.
|
||||
│ └── Standard # Each prefix (usually corresponding to the author) is placed in a separate directory.
|
||||
│ └── Dataframe # Each library may be stored in multiple versions.
|
||||
│ └── 1.7.0 # Each version contains a standard Enso package.
|
||||
│ ├── package.yaml
|
||||
│ └── src
|
||||
│ ├── List.enso
|
||||
│ ├── Number.enso
|
||||
│ └── Text.enso
|
||||
├── resolvers # Contains resolver specifications, described below.
|
||||
│ ├── lts-1.56.7.yaml
|
||||
│ └── lts-2.0.8.yaml
|
||||
├── editions # Contains Edition specifications.
|
||||
│ ├── 2021.4.yaml
|
||||
│ └── nightly-2021-06-31.yaml
|
||||
├── README.md # Information on layout and usage of the Enso distribution.
|
||||
├── .enso.portable # A file that allows the universal launcher to detect that if it is run from this directory, it should run in portable distribution mode.
|
||||
└── THIRD-PARTY # Contains licences of distributed components, including the NOTICE file.
|
||||
@ -94,19 +94,19 @@ ENSO_DATA_DIRECTORY
|
||||
│ └── 1.2.0 # A full distribution of given Enso version, described below.
|
||||
│ └── <truncated>
|
||||
├── runtime # A directory storing (optional) distributions of the JVM used by the Enso distributions.
|
||||
│ └── graalvm-ce-27.1.1
|
||||
├── lib
|
||||
│ └── src # Contains sources of downloaded libraries.
|
||||
│ └── Dataframe # Each library may be stored in multiple version.
|
||||
│ └── graalvm-ce-java11-27.1.1
|
||||
├── lib # Contains sources of downloaded libraries.
|
||||
│ └── Standard # Each prefix (usually corresponding to the author) is placed in a separate directory.
|
||||
│ └── Dataframe # Each library may be stored in multiple versions.
|
||||
│ └── 1.7.0 # Each version contains a standard Enso package.
|
||||
│ ├── package.yaml
|
||||
│ └── src
|
||||
│ ├── List.enso
|
||||
│ ├── Number.enso
|
||||
│ └── Text.enso
|
||||
└── resolvers # Contains resolver specifications, described below.
|
||||
├── lts-1.56.7.yaml
|
||||
└── lts-2.0.8.yaml
|
||||
└── editions # Contains Edition specifications.
|
||||
├── 2021.4.yaml
|
||||
└── nightly-2021-06-31.yaml
|
||||
|
||||
ENSO_CONFIG_DIRECTORY
|
||||
└── global-config.yaml # Global user configuration.
|
||||
@ -202,20 +202,19 @@ but the following are some guidelines:
|
||||
3. Packages that the compiler relies on, e.g. compile error definitions, stack
|
||||
traces etc.
|
||||
|
||||
### Resolvers
|
||||
## Enso Home Layout
|
||||
|
||||
**Note** This system is not implemented yet.
|
||||
The location called in some places `<ENSO_HOME>` is the place where user's
|
||||
projects and similar files are stored. Currently it is specified to always be
|
||||
`$HOME/enso`.
|
||||
|
||||
A resolver is a manifest containing library versions that are automatically
|
||||
available for import in any project using the resolver.
|
||||
It has the following structure:
|
||||
|
||||
Example contents of a resolver file are as follows:
|
||||
|
||||
```yaml
|
||||
enso-version: 1.0.7
|
||||
libraries:
|
||||
- name: Base
|
||||
version: 1.0.0
|
||||
- name: Http
|
||||
version: 5.3.5
|
||||
```
|
||||
<ENSO_HOME>
|
||||
├── projects # Contains all user projects.
|
||||
├── libraries # Contains all local libraries that can be edited by the user.
|
||||
│ └── Prefix1 # Contains libraries with the given prefix.
|
||||
│ └── Library_Name # Contains a package of a local library.
|
||||
└── editions # Contains custom, user-defined editions that can be used as a base for project configurations.
|
||||
```
|
||||
|
@ -102,19 +102,20 @@ The following is an example of this manifest file.
|
||||
license: MIT
|
||||
name: My_Package
|
||||
version: 1.0.1
|
||||
enso-version: 0.2.0
|
||||
edition:
|
||||
extends: 2021.3
|
||||
enso-version: 0.2.12
|
||||
libraries:
|
||||
- name: Foo.Bar
|
||||
version: 1.2.3
|
||||
repository: main
|
||||
prefer-local-libraries: false
|
||||
authors:
|
||||
- name: John Doe
|
||||
email: john.doe@example.com
|
||||
maintainers:
|
||||
- name: Jane Doe
|
||||
email: jane.doe@example.com
|
||||
resolver: lts-1.2.0
|
||||
extra-dependencies:
|
||||
- name: Base
|
||||
version: "1.2.0"
|
||||
- name: Http
|
||||
version: "4.5.3"
|
||||
```
|
||||
|
||||
The following is the specification of the manifest fields. Fields marked as
|
||||
@ -129,12 +130,37 @@ fields cannot be published.
|
||||
package. Defaults to `None`, meaning the package is not safe for use by third
|
||||
parties.
|
||||
|
||||
#### edition
|
||||
|
||||
**Optional (required for publishing)** _Edition_: Defines the Edition settings
|
||||
of the package that determine the engine version and library resolution
|
||||
settings. It is a sub-setting that can consist of multiple fields, see
|
||||
[the Edition documentation](../libraries/editions.md#the-edition-file) for the
|
||||
format description.
|
||||
|
||||
The field was added in version 0.2.12 as a replacement for `enso-version` as it
|
||||
supersedes its functionality.
|
||||
|
||||
If the `edition` field is not specified, a default edition is used.
|
||||
|
||||
#### enso-version
|
||||
|
||||
**Optional (required for publishing)** _String_: Specifies the Enso version that
|
||||
should be used for this project. If not set or set to `default`, the default
|
||||
locally installed Enso version will be used. The version should not be `default`
|
||||
if the package is to be published.
|
||||
**Deprecated** _String_: Specifies the Enso version that should be used for this
|
||||
project. If not set or set to `default`, the default locally installed Enso
|
||||
version will be used.
|
||||
|
||||
The field was deprecated in version 0.2.12. Currently it is still supported, but
|
||||
the newer tools will migrate it to the `edition` format when the config is
|
||||
modified.
|
||||
|
||||
If old tools see a config file that includes an `edition` setting but does not
|
||||
include the `engine-version` (for example after the migration), they will fall
|
||||
back to using the default engine version - that is because old tools were not
|
||||
aware of the `edition` field, so they will simply ignore it.
|
||||
|
||||
If a config defines the `edition` field it should not define the
|
||||
`engine-version` field anymore, as that could lead to inconsistent engine
|
||||
version settings.
|
||||
|
||||
#### version
|
||||
|
||||
@ -163,32 +189,18 @@ present.
|
||||
**Optional** _List of contacts_: The name(s) and contact info(s) of the current
|
||||
maintainer(s) of this library, in the same format as `authors` above.
|
||||
|
||||
#### resolver
|
||||
#### prefer-local-libraries
|
||||
|
||||
**Note** This field is not currently implemented. **Optional (required for
|
||||
publishing)** _String_: The resolver name, used to choose compiler version and
|
||||
basic libraries set. If not set, the system-default resolver will be used.
|
||||
**Optional** _Boolean_: A flag that tells the library resolver to prefer local
|
||||
library versions over the ones specified by the edition configuration. This is
|
||||
useful to make all local libraries easily accessible, but in more sophisticated
|
||||
scenarios individual `local` repository overrides should be used instead of
|
||||
that. See [Library Resolution](../libraries/editions.md#library-resolution) for
|
||||
more details.
|
||||
|
||||
> The actionables for this section are:
|
||||
>
|
||||
> - Extend the compiler version to handle version bounds.
|
||||
|
||||
#### extra-dependencies
|
||||
|
||||
**Note** This field is not currently implemented. **Optional** _List of Library
|
||||
objects_: The list of libraries this package requires to function properly and
|
||||
that are not included in the resolver. Defaults to an empty list.
|
||||
|
||||
A library object is of the form:
|
||||
|
||||
```yaml
|
||||
name: <name of the library>
|
||||
version: <semver string of the required library version>
|
||||
```
|
||||
|
||||
> The actionables for this section are:
|
||||
>
|
||||
> - Extend the library version field to handle version bounds.
|
||||
If the flag is not specified, it defaults to `false`, delegating all library
|
||||
resolution to the edition configuration. However, newly created projects will
|
||||
have it set to `true`.
|
||||
|
||||
### The `visualization` Directory
|
||||
|
||||
|
14
docs/libraries/README.md
Normal file
14
docs/libraries/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: section-summary
|
||||
title: Enso Library Ecosystem
|
||||
category: libraries
|
||||
tags: [libraries, readme]
|
||||
order: 0
|
||||
---
|
||||
|
||||
# Enso Library Ecosystem
|
||||
|
||||
Documents in this section describe Enso's library ecosystem.
|
||||
|
||||
- [**Editions:**](./editions.md) Information on Editions, the concept that
|
||||
organizes library versioning.
|
217
docs/libraries/editions.md
Normal file
217
docs/libraries/editions.md
Normal file
@ -0,0 +1,217 @@
|
||||
---
|
||||
layout: developer-doc
|
||||
title: Editions
|
||||
category: libraries
|
||||
tags: [libraries, editions]
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Editions
|
||||
|
||||
This document describes the concept of Editions.
|
||||
|
||||
<!-- MarkdownTOC levels="2,3" autolink="true" -->
|
||||
|
||||
- [What Is An Edition](#what-is-an-edition)
|
||||
- [The Edition File](#the-edition-file)
|
||||
- [Repositories](#repositories)
|
||||
- [Libraries](#libraries)
|
||||
- [Extending the Editions](#extending-the-editions)
|
||||
- [An Example Configuration](#an-example-configuration)
|
||||
- [Edition Resolution](#edition-resolution)
|
||||
- [Updating the Editions](#updating-the-editions)
|
||||
- [Library Resolution](#library-resolution)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
## What Is An Edition
|
||||
|
||||
An Edition, is in principle, a list of library versions that should be
|
||||
compatible with each other. An Edition specifies the engine version and a set of
|
||||
library versions that can be used together.
|
||||
|
||||
If a library included in an Edition depends on another library, that other
|
||||
library must also be included in that Edition and the version at which it is
|
||||
included must be compatible with the library that depends on it. Each Edition
|
||||
may only include a single version of each library.
|
||||
|
||||
Thus, when a library is to be installed, its version is uniquely determined by
|
||||
the selected Edition. A curated Edition file will guarantee that all libraries
|
||||
are compatible which simplifies version resolution - the exact version that is
|
||||
specified in the Edition is always used.
|
||||
|
||||
## The Edition File
|
||||
|
||||
The Edition file is a YAML file that can contain the following fields:
|
||||
|
||||
- `engine-version` which should be a semantic versioning string specifying the
|
||||
engine version that should be associated with that edition,
|
||||
- `repositories` which defines the repositories which are sources of library
|
||||
packages, its format is [described below](#repositories),
|
||||
- `extends` which can contain a name of another Edition that this Edition
|
||||
extends,
|
||||
- `libraries` which defines the libraries that this Edition should include, its
|
||||
format is [described below](#libraries).
|
||||
|
||||
Every field is optional, but for an Edition file to be valid it must specify at
|
||||
least the engine version to be used (either by specifying it directly or
|
||||
extending another edition that specifies it).
|
||||
|
||||
### Repositories
|
||||
|
||||
The `repositories` field is a list of repository objects.
|
||||
|
||||
Each object must have:
|
||||
|
||||
- a `name` field which specifies the name under which this repository will be
|
||||
referred to in the rest of the file,
|
||||
- a `url` field which specifies the URL of the root of that repository.
|
||||
|
||||
The `name` can be any string which only needs to be consistent with the names
|
||||
used in the package definitions. The only reserved name is `local`, which is a
|
||||
special repository name, as [explained below](#library-resolution).
|
||||
|
||||
### Libraries
|
||||
|
||||
The `libraries` field defines the set of libraries included in the edition.
|
||||
|
||||
Each library is represented by an object that must have:
|
||||
|
||||
- a `name` field which is the fully qualified name of the library (consisting of
|
||||
its prefix and the name itself),
|
||||
- a `repository` field which specifies which repository this package should be
|
||||
downloaded from. The `repository` field should refer to the `name` of one of
|
||||
the repositories defined in the edition or to `local`,
|
||||
- a `version` field which specifies which exact package version should be used
|
||||
when the library is imported; it is normally required, but if the `repository`
|
||||
is set to `local`, the version must not be specified as the version will only
|
||||
depend on what is available in the local repository,
|
||||
- an optional `hash` that can be included to verify the integrity of the
|
||||
package.
|
||||
|
||||
The `hash` field is currently not implemented.
|
||||
|
||||
### Extending the Editions
|
||||
|
||||
An edition may extend another one by using the `extends` property specifying the
|
||||
name of the edition that is to be extended. Henceforth we will call the edition
|
||||
that is being extended 'the parent edition' and the other one 'the local
|
||||
edition'.
|
||||
|
||||
The current edition inherits all configuration of the parent edition, but it can
|
||||
also override specific settings.
|
||||
|
||||
If the `engine-version` is specified in the current edition, it overrides the
|
||||
engine version that was implied from the parent edition.
|
||||
|
||||
If the current edition specifies its libraries, they are added to the set of
|
||||
available libraries defined by the parent edition. If the current edition
|
||||
defines a library that has the same fully qualified name as a library that was
|
||||
already defined in the parent edition, the definition from the current edition
|
||||
takes precedence. This is the most important mechanism of extending editions
|
||||
that allows to override library settings.
|
||||
|
||||
The libraries defined in the current edition can refer to the repositories
|
||||
defined both in the current edition and in the parent. However, if the current
|
||||
edition defines a repository with the same name as some repository defined in
|
||||
the parent edition, the definition from the current edition takes precedence for
|
||||
the package definitions of the current definition, **but** the package
|
||||
definitions in the parent edition are not affected (they still refer to the
|
||||
definition from the their own edition). So you can shadow a repository name, but
|
||||
you cannot override it for libraries from the parent edition - instead libraries
|
||||
whose repository should be changed must all be overridden in the current
|
||||
edition.
|
||||
|
||||
Extending editions can be arbitrarily nested. That is, an edition can extend
|
||||
another edition that extends another one etc. The only limitation is that
|
||||
obviously there can be no cycles in the chain of extensions. Multiple extensions
|
||||
are resolved as follows: first the parent edition is completely resolved (which
|
||||
may recursively need to first resolve its parents etc.) and only then the
|
||||
current edition applies its overrides.
|
||||
|
||||
### An Example Configuration
|
||||
|
||||
```yaml
|
||||
extends: 2021.4
|
||||
engine-version: 1.2.3
|
||||
repositories:
|
||||
- name: secondary
|
||||
url: https://example.com/
|
||||
libraries:
|
||||
- name: Foo.Bar
|
||||
version: 1.0.0
|
||||
repository: secondary
|
||||
```
|
||||
|
||||
The edition file shown above extends a base edition file called `2021.4`. It
|
||||
overrides the engine version set in the parent edition to `1.2.3`. Moreover it
|
||||
adds a library `Foo.Bar` from the `secondary` repository, or if `2021.4`
|
||||
included the library `Foo.Bar`, its definition is overridden with the one
|
||||
provided here.
|
||||
|
||||
## Edition Resolution
|
||||
|
||||
The edition configuration for a project is loaded from the `edition` section in
|
||||
its `package.yaml` configuration. This 'per-project' edition has no assigned
|
||||
names, but it can refer to other editions by their names (when extending them).
|
||||
These editions are resolved using the logic below:
|
||||
|
||||
1. Each `<edition-name>` corresponds to a file `<edition-name>.yaml`.
|
||||
2. First, the directory `<ENSO_HOME>/editions` is scanned for a matching edition
|
||||
file.
|
||||
3. If none is found above, the directory `$ENSO_DATA_DIRECTORY/editions` is
|
||||
checked.
|
||||
|
||||
See [The Enso Distribution](../distribution/distribution.md) for definitions of
|
||||
the directories.
|
||||
|
||||
### Updating the Editions
|
||||
|
||||
The global user configuration file should contain a list of URLs specifying
|
||||
edition providers that should be used. By default (if the field is missing), it
|
||||
will default to our official edition provider, but users may add other providers
|
||||
or remove the official one.
|
||||
|
||||
When `enso update-editions` is called or when requested by the IDE, these
|
||||
providers are queried and any new edition files are downloaded to the
|
||||
`$ENSO_DATA_DIRECTORY/editions` directory. Editions are assumed to be immutable,
|
||||
so edition files that already exist on disk are not redownloaded.
|
||||
|
||||
## Library Resolution
|
||||
|
||||
Below are listed the steps that are taken when resolving an import of library
|
||||
`Foo.Bar`:
|
||||
|
||||
1. If and only if the project has `prefer-local-libraries` set to `true` and if
|
||||
any directory on the library path contains `Foo/Bar`, that local instance is
|
||||
chosen as the library that should be used, regardless of the version that is
|
||||
there;
|
||||
2. Otherwise, the list of libraries defined directly in the `edition` section of
|
||||
`package.yaml` of the current project is checked, and if the library is
|
||||
defined there, it is selected.
|
||||
3. Otherwise, any parent editions are consulted; if they too do not contain the
|
||||
library that we are searching for, an error is reported.
|
||||
4. Once we know the library version to be used:
|
||||
1. If the repository associated with the library is `local`, the library path
|
||||
is searched for the first directory to contain `Foo/Bar` and this path is
|
||||
loaded. If the library is not present on the library path, an error is
|
||||
reported.
|
||||
2. Otherwise, the edition must have defined an exact `<version>` of the
|
||||
library that is supposed to be used.
|
||||
3. If the library is already downloaded in the local repository cache, that
|
||||
is the directory `$ENSO_DATA_DIRECTORY/lib/Foo/Bar/<version>` exists, that
|
||||
package is loaded.
|
||||
4. Otherwise, the library is missing and must be downloaded from its
|
||||
associated repository (and placed in the cache as above).
|
||||
|
||||
By default, the library path is `<ENSO_HOME>/libraries/` but it can be
|
||||
overridden by setting the `ENSO_LIBRARY_PATH` environment variable. It may
|
||||
include a list of directories (separated by the system specific path separator);
|
||||
the first directory on the list has the highest precedence.
|
||||
|
||||
In particular, if `prefer-local-libraries` is `false`, and the edition does not
|
||||
define a library at all, when trying to resolve such a library, it is reported
|
||||
as not found even if a local version of it exists. That is because
|
||||
auto-discovery of local libraries is only done with `prefer-local-libraries` set
|
||||
to `true`. In all other cases, the `local` repository overrides should be set
|
||||
explicitly.
|
@ -1,10 +1,10 @@
|
||||
package org.enso.launcher
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import io.circe.Json
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.EditionManager
|
||||
import org.enso.runtimeversionmanager.CurrentVersion
|
||||
import org.enso.runtimeversionmanager.config.{
|
||||
DefaultVersion,
|
||||
@ -42,12 +42,14 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
|
||||
runtimeVersionManager(cliOptions, alwaysInstallMissing = false)
|
||||
private lazy val configurationManager =
|
||||
new GlobalConfigurationManager(componentsManager, distributionManager)
|
||||
private lazy val projectManager = new ProjectManager(configurationManager)
|
||||
private lazy val editionManager = EditionManager(distributionManager)
|
||||
private lazy val projectManager = new ProjectManager
|
||||
private lazy val runner =
|
||||
new LauncherRunner(
|
||||
projectManager,
|
||||
configurationManager,
|
||||
componentsManager,
|
||||
editionManager,
|
||||
LauncherEnvironment,
|
||||
LauncherLogging.loggingServiceEndpoint()
|
||||
)
|
||||
|
@ -3,12 +3,12 @@ package org.enso.launcher.cli
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.cli.CLIOutput
|
||||
import org.enso.distribution.locking.Resource
|
||||
import org.enso.launcher.InfoLogger
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
GraalVMVersion,
|
||||
RuntimeVersionManagementUserInterface
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.Resource
|
||||
|
||||
/** [[RuntimeVersionManagementUserInterface]] that reports information and progress
|
||||
* to the command line.
|
||||
|
@ -2,13 +2,13 @@ package org.enso.launcher.cli
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.{Files, NoSuchFileException, Path}
|
||||
|
||||
import cats.implicits._
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.cli.arguments.Opts
|
||||
import org.enso.cli.arguments.Opts.implicits._
|
||||
import org.enso.runtimeversionmanager.{CurrentVersion, FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.{FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.CurrentVersion
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.cli.Arguments._
|
||||
import org.enso.launcher.distribution.LauncherEnvironment
|
||||
import org.enso.launcher.upgrade.LauncherUpgrader
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.enso.launcher.cli
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import org.enso.cli.arguments
|
||||
import org.enso.cli.arguments.CommandHelp
|
||||
import org.enso.runtimeversionmanager.{Environment, FileSystem}
|
||||
import org.enso.distribution.{Environment, FileSystem}
|
||||
import org.enso.launcher.distribution.LauncherEnvironment
|
||||
|
||||
import scala.sys.process._
|
||||
|
@ -1,22 +1,15 @@
|
||||
package org.enso.launcher.components
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.runtimeversionmanager.Environment
|
||||
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner.{
|
||||
LanguageServerOptions,
|
||||
RunSettings,
|
||||
Runner,
|
||||
RunnerError,
|
||||
WhichEngine
|
||||
}
|
||||
import org.enso.distribution.{EditionManager, Environment}
|
||||
import org.enso.launcher.project.ProjectManager
|
||||
import org.enso.loggingservice.LogLevel
|
||||
import org.enso.runtimeversionmanager.components.RuntimeVersionManager
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner._
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Try
|
||||
|
||||
@ -26,10 +19,13 @@ class LauncherRunner(
|
||||
projectManager: ProjectManager,
|
||||
configurationManager: GlobalConfigurationManager,
|
||||
componentsManager: RuntimeVersionManager,
|
||||
editionManager: EditionManager,
|
||||
environment: Environment,
|
||||
loggerConnection: Future[Option[Uri]]
|
||||
) extends Runner(
|
||||
componentsManager,
|
||||
configurationManager,
|
||||
editionManager,
|
||||
environment,
|
||||
loggerConnection
|
||||
) {
|
||||
@ -53,12 +49,7 @@ class LauncherRunner(
|
||||
projectManager.findProject(currentWorkingDirectory).get
|
||||
}
|
||||
|
||||
val version =
|
||||
versionOverride.getOrElse {
|
||||
inProject
|
||||
.map(_.version)
|
||||
.getOrElse(configurationManager.defaultVersion)
|
||||
}
|
||||
val version = resolveVersion(versionOverride, inProject)
|
||||
val arguments = inProject match {
|
||||
case Some(project) =>
|
||||
val projectPackagePath =
|
||||
@ -110,9 +101,7 @@ class LauncherRunner(
|
||||
val project =
|
||||
if (projectMode) Some(projectManager.loadProject(actualPath).get)
|
||||
else projectManager.findProject(actualPath).get
|
||||
val version = versionOverride
|
||||
.orElse(project.map(_.version))
|
||||
.getOrElse(configurationManager.defaultVersion)
|
||||
val version = resolveVersion(versionOverride, project)
|
||||
|
||||
val arguments =
|
||||
if (projectMode) Seq("--run", actualPath.toString)
|
||||
@ -184,8 +173,7 @@ class LauncherRunner(
|
||||
for {
|
||||
project <- projectManager.findProject(currentWorkingDirectory)
|
||||
} yield {
|
||||
val version =
|
||||
project.map(_.version).getOrElse(configurationManager.defaultVersion)
|
||||
val version = resolveVersion(None, project)
|
||||
val arguments =
|
||||
Seq("--version") ++ (if (useJSON) Seq("--json") else Seq())
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
package org.enso.launcher.distribution
|
||||
|
||||
import org.enso.distribution.PortableDistributionManager
|
||||
import org.enso.distribution.locking.{
|
||||
ResourceManager,
|
||||
ThreadSafeFileLockManager
|
||||
}
|
||||
import org.enso.launcher.cli.{
|
||||
CLIRuntimeVersionManagementUserInterface,
|
||||
GlobalCLIOptions
|
||||
@ -11,14 +16,7 @@ import org.enso.runtimeversionmanager.components.{
|
||||
RuntimeComponentUpdaterFactory,
|
||||
RuntimeVersionManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.{
|
||||
PortableDistributionManager,
|
||||
TemporaryDirectoryManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.{
|
||||
ResourceManager,
|
||||
ThreadSafeFileLockManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||
import org.enso.runtimeversionmanager.releases.engine.EngineRepository
|
||||
import org.enso.runtimeversionmanager.releases.graalvm.GraalCEReleaseProvider
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
package org.enso.launcher.distribution
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.runtimeversionmanager.Environment
|
||||
import org.enso.distribution.Environment
|
||||
|
||||
/** Default [[Environment]] to use in the launcher.
|
||||
*
|
||||
|
@ -1,14 +1,17 @@
|
||||
package org.enso.launcher.installation
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.cli.CLIOutput
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.{
|
||||
DistributionManager,
|
||||
FileSystem,
|
||||
OS,
|
||||
PortableDistributionManager
|
||||
}
|
||||
import org.enso.distribution.locking.ResourceManager
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.distribution.PortableDistributionManager
|
||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
||||
import org.enso.launcher.InfoLogger
|
||||
import org.enso.launcher.cli.{GlobalCLIOptions, InternalOpts, Main}
|
||||
import org.enso.launcher.distribution.DefaultManagers
|
||||
@ -54,9 +57,9 @@ class DistributionInstaller(
|
||||
private val nonEssentialDirectories = Seq("THIRD-PARTY")
|
||||
|
||||
private val enginesDirectory =
|
||||
installed.dataDirectory / manager.ENGINES_DIRECTORY
|
||||
installed.dataDirectory / DistributionManager.ENGINES_DIRECTORY
|
||||
private val runtimesDirectory =
|
||||
installed.dataDirectory / manager.RUNTIMES_DIRECTORY
|
||||
installed.dataDirectory / DistributionManager.RUNTIMES_DIRECTORY
|
||||
|
||||
/** Installs the distribution under configured location.
|
||||
*
|
||||
|
@ -1,15 +1,18 @@
|
||||
package org.enso.launcher.installation
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.enso.cli.CLIOutput
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.{
|
||||
DistributionManager,
|
||||
FileSystem,
|
||||
OS,
|
||||
PortableDistributionManager
|
||||
}
|
||||
import org.enso.distribution.locking.ResourceManager
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.distribution.PortableDistributionManager
|
||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
||||
import org.enso.launcher.InfoLogger
|
||||
import org.enso.launcher.cli.{
|
||||
GlobalCLIOptions,
|
||||
@ -178,7 +181,7 @@ class DistributionUninstaller(
|
||||
private val knownDataDirectories =
|
||||
Set.from(
|
||||
manager.LocallyInstalledDirectories.possibleDirectoriesInsideDataDirectory
|
||||
) - manager.LOCK_DIRECTORY
|
||||
) - DistributionManager.LOCK_DIRECTORY
|
||||
|
||||
/** Removes all files contained in the ENSO_DATA_DIRECTORY and possibly the
|
||||
* directory itself.
|
||||
@ -211,7 +214,7 @@ class DistributionUninstaller(
|
||||
resourceManager.unlockTemporaryDirectory()
|
||||
resourceManager.releaseMainLock()
|
||||
|
||||
val lockDirectory = dataRoot / manager.LOCK_DIRECTORY
|
||||
val lockDirectory = dataRoot / DistributionManager.LOCK_DIRECTORY
|
||||
if (Files.isDirectory(lockDirectory)) {
|
||||
for (lock <- FileSystem.listDirectory(lockDirectory)) {
|
||||
try {
|
||||
|
@ -1,11 +1,9 @@
|
||||
package org.enso.launcher.project
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner.Project
|
||||
import org.enso.pkg.PackageManager
|
||||
import org.enso.runtimeversionmanager.runner.Project
|
||||
|
||||
import java.nio.file.Path
|
||||
import scala.util.{Failure, Try}
|
||||
|
||||
/** A helper class for project management.
|
||||
@ -13,7 +11,7 @@ import scala.util.{Failure, Try}
|
||||
* It allows to create new project, open existing ones or traverse the
|
||||
* directory tree to find a project based on a path inside it.
|
||||
*/
|
||||
class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
||||
class ProjectManager {
|
||||
|
||||
private val packageManager = PackageManager.Default
|
||||
|
||||
@ -22,7 +20,7 @@ class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
||||
def loadProject(path: Path): Try[Project] =
|
||||
packageManager
|
||||
.loadPackage(path.toFile)
|
||||
.map(new Project(_, globalConfigurationManager))
|
||||
.map(new Project(_))
|
||||
.recoverWith(error => Failure(ProjectLoadingError(path, error)))
|
||||
|
||||
/** Traverses the directory tree looking for a project in one of the ancestors
|
||||
@ -40,7 +38,7 @@ class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
|
||||
private def tryFindingProject(root: Path): Try[Project] =
|
||||
packageManager
|
||||
.loadPackage(root.toFile)
|
||||
.map(new Project(_, globalConfigurationManager))
|
||||
.map(new Project(_))
|
||||
.recoverWith {
|
||||
case PackageManager.PackageNotFound() if root.getParent != null =>
|
||||
tryFindingProject(root.getParent)
|
||||
|
@ -3,7 +3,7 @@ package org.enso.launcher.releases.launcher
|
||||
import io.circe.{yaml, Decoder}
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.runtimeversionmanager.releases.ReleaseProviderException
|
||||
import org.enso.pkg.SemVerJson._
|
||||
import org.enso.editions.SemVerJson._
|
||||
|
||||
import scala.util.{Failure, Try}
|
||||
|
||||
|
@ -1,32 +1,32 @@
|
||||
package org.enso.launcher.upgrade
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.cli.CLIOutput
|
||||
import org.enso.runtimeversionmanager.{CurrentVersion, FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.archive.Archive
|
||||
import org.enso.runtimeversionmanager.components.UpgradeRequiredError
|
||||
import org.enso.runtimeversionmanager.distribution.DistributionManager
|
||||
import org.enso.launcher.cli.{
|
||||
CLIProgressReporter,
|
||||
GlobalCLIOptions,
|
||||
InternalOpts
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.{
|
||||
import org.enso.distribution.{DistributionManager, FileSystem, OS}
|
||||
import org.enso.distribution.locking.{
|
||||
LockType,
|
||||
LockUserInterface,
|
||||
Resource,
|
||||
ResourceManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.CurrentVersion
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.archive.Archive
|
||||
import org.enso.runtimeversionmanager.components.UpgradeRequiredError
|
||||
import org.enso.launcher.cli.{
|
||||
CLIProgressReporter,
|
||||
GlobalCLIOptions,
|
||||
InternalOpts
|
||||
}
|
||||
import org.enso.launcher.releases.launcher.LauncherRelease
|
||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||
import org.enso.launcher.releases.LauncherRepository
|
||||
import org.enso.launcher.InfoLogger
|
||||
import org.enso.launcher.distribution.DefaultManagers
|
||||
import org.enso.logger.LoggerSyntax
|
||||
import org.enso.runtimeversionmanager.locking.Resources
|
||||
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
@ -66,7 +66,7 @@ class LauncherUpgrader(
|
||||
}
|
||||
resourceManager.withResource(
|
||||
failIfAnotherUpgradeIsRunning,
|
||||
Resource.LauncherExecutable,
|
||||
Resources.LauncherExecutable,
|
||||
LockType.Exclusive
|
||||
) {
|
||||
runCleanup(isStartup = true)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.enso.launcher
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import org.enso.distribution.OS
|
||||
|
||||
import org.enso.runtimeversionmanager.OS
|
||||
import java.nio.file.{Files, Path}
|
||||
import org.enso.runtimeversionmanager.test.NativeTestHelper
|
||||
import org.scalatest.concurrent.{Signaler, TimeLimitedTests}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
@ -4,7 +4,8 @@ import java.nio.file.{Files, Path}
|
||||
import java.util.UUID
|
||||
import akka.http.scaladsl.model.Uri
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.EditionManager
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner._
|
||||
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
||||
@ -30,13 +31,15 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
|
||||
new GlobalConfigurationManager(componentsManager, distributionManager) {
|
||||
override def defaultVersion: SemVer = defaultEngineVersion
|
||||
}
|
||||
val projectManager = new ProjectManager(configurationManager)
|
||||
val editionManager = EditionManager(distributionManager)
|
||||
val projectManager = new ProjectManager()
|
||||
val cwd = cwdOverride.getOrElse(getTestDirectory)
|
||||
val runner =
|
||||
new LauncherRunner(
|
||||
projectManager,
|
||||
configurationManager,
|
||||
componentsManager,
|
||||
editionManager,
|
||||
env,
|
||||
Future.successful(Some(fakeUri))
|
||||
) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.enso.launcher.installation
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import org.enso.distribution.{FileSystem, OS}
|
||||
|
||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import java.nio.file.{Files, Path}
|
||||
import FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||
import org.enso.launcher._
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.enso.launcher.installation
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import org.enso.distribution.{FileSystem, OS}
|
||||
|
||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import java.nio.file.{Files, Path}
|
||||
import FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||
import org.enso.launcher.NativeTest
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.enso.launcher.project
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.distribution.DistributionManager
|
||||
import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest
|
||||
import org.enso.pkg.Contact
|
||||
import org.scalatest.{Inside, OptionValues}
|
||||
@ -19,7 +19,7 @@ class ProjectManagerSpec
|
||||
new GlobalConfigurationManager(null, distributionManager) {
|
||||
override def defaultVersion: SemVer = defaultEnsoVersion
|
||||
}
|
||||
(fakeConfigurationManager, new ProjectManager(fakeConfigurationManager))
|
||||
(fakeConfigurationManager, new ProjectManager())
|
||||
}
|
||||
|
||||
"ProjectManager" should {
|
||||
@ -43,7 +43,8 @@ class ProjectManagerSpec
|
||||
projectDir.resolve("src").resolve("Main.enso").toFile should exist
|
||||
|
||||
val project = projectManager.loadProject(projectDir).get
|
||||
project.version shouldEqual defaultEnsoVersion
|
||||
// TODO [RW] this test may change once we switch to deriving a particular edition by default
|
||||
project.edition.engineVersion should contain(defaultEnsoVersion)
|
||||
project.config.authors.headOption.value shouldEqual author
|
||||
project.config.maintainers.headOption.value shouldEqual author
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.enso.launcher.releases.fallback
|
||||
|
||||
import java.nio.file.Path
|
||||
import org.enso.distribution.FileSystem
|
||||
|
||||
import org.enso.runtimeversionmanager.FileSystem
|
||||
import java.nio.file.Path
|
||||
import org.enso.launcher.TestHelpers
|
||||
import org.enso.launcher.releases.fallback.staticwebsite.FileStorageFallbackReleaseProvider
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
@ -1,9 +1,8 @@
|
||||
package org.enso.launcher.releases.fallback
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import org.enso.cli.task.{TaskProgress, TaskProgressImplementation}
|
||||
import org.enso.runtimeversionmanager.FileSystem
|
||||
import org.enso.distribution.FileSystem
|
||||
import org.enso.launcher.TestHelpers
|
||||
import org.enso.launcher.releases.fallback.staticwebsite.FileStorage
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
package org.enso.launcher.upgrade
|
||||
|
||||
import java.nio.file.{Files, Path, StandardCopyOption}
|
||||
|
||||
import io.circe.parser
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.runtimeversionmanager.{FileSystem, OS}
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.{FileSystem, OS}
|
||||
import org.enso.distribution.locking.{FileLockManager, LockType}
|
||||
import FileSystem.PathSyntax
|
||||
import org.enso.launcher._
|
||||
import org.enso.runtimeversionmanager.locking.{FileLockManager, LockType}
|
||||
import org.enso.runtimeversionmanager.test.WithTemporaryDirectory
|
||||
import org.enso.testkit.RetrySpec
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
|
@ -4,7 +4,7 @@ import org.enso.polyglot.debugger.protocol.{
|
||||
ExceptionRepresentation,
|
||||
ObjectRepresentation
|
||||
}
|
||||
|
||||
import org.enso.testkit.EitherValue
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
|
@ -3,10 +3,11 @@ package org.enso.runner
|
||||
import akka.http.scaladsl.model.{IllegalUriException, Uri}
|
||||
import cats.implicits._
|
||||
import org.apache.commons.cli.{Option => CliOption, _}
|
||||
import org.enso.editions.SemVerEnsoVersion
|
||||
import org.enso.languageserver.boot
|
||||
import org.enso.languageserver.boot.LanguageServerConfig
|
||||
import org.enso.loggingservice.LogLevel
|
||||
import org.enso.pkg.{Contact, PackageManager, SemVerEnsoVersion}
|
||||
import org.enso.pkg.{Contact, PackageManager}
|
||||
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
|
||||
import org.enso.version.VersionDescription
|
||||
import org.graalvm.polyglot.PolyglotException
|
||||
|
@ -1,11 +1,9 @@
|
||||
package org.enso.runtimeversionmanager.distribution
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.runtimeversionmanager.{Environment, FileSystem, OS}
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
@ -27,6 +25,10 @@ import scala.util.control.NonFatal
|
||||
* @param unsafeTemporaryDirectory path to the temporary directory, should not
|
||||
* be used directly, see
|
||||
* [[TemporaryDirectoryManager]]
|
||||
* @param customEditions the search paths for editions
|
||||
* @param localLibrariesSearchPaths a sequence of paths to search for local
|
||||
* libraries, in order of precedence
|
||||
* @param ensoHome the home directory for user's projects etc.
|
||||
*/
|
||||
case class DistributionPaths(
|
||||
dataRoot: Path,
|
||||
@ -36,7 +38,10 @@ case class DistributionPaths(
|
||||
config: Path,
|
||||
locks: Path,
|
||||
logs: Path,
|
||||
unsafeTemporaryDirectory: Path
|
||||
unsafeTemporaryDirectory: Path,
|
||||
customEditions: Seq[Path],
|
||||
localLibrariesSearchPaths: Seq[Path],
|
||||
ensoHome: Path
|
||||
) {
|
||||
|
||||
/** @inheritdoc */
|
||||
@ -48,7 +53,8 @@ case class DistributionPaths(
|
||||
| bundle = $bundle,
|
||||
| config = $config,
|
||||
| locks = $locks,
|
||||
| tmp = $unsafeTemporaryDirectory
|
||||
| tmp = $unsafeTemporaryDirectory,
|
||||
| ensoHome = $ensoHome
|
||||
|)""".stripMargin
|
||||
|
||||
/** Sequence of paths to search for engine installations, in order of
|
||||
@ -61,6 +67,18 @@ case class DistributionPaths(
|
||||
*/
|
||||
def runtimeSearchPaths: Seq[Path] =
|
||||
Seq(runtimes) ++ bundle.map(_.runtimes).toSeq
|
||||
|
||||
/** The directory for cached editions managed by us. */
|
||||
def cachedEditions: Path = dataRoot / DistributionManager.EDITIONS_DIRECTORY
|
||||
|
||||
/** The directory for cached libraries managed by us. */
|
||||
def cachedLibraries: Path = dataRoot / DistributionManager.LIBRARIES_DIRECTORY
|
||||
|
||||
/** Sequence of paths to search for edition configurations, in order of
|
||||
* precedence.
|
||||
*/
|
||||
def editionSearchPaths: Seq[Path] =
|
||||
customEditions ++ Seq(cachedEditions)
|
||||
}
|
||||
|
||||
/** Paths to secondary directories for additionally bundled engine
|
||||
@ -84,6 +102,7 @@ case class Bundle(engines: Path, runtimes: Path)
|
||||
*/
|
||||
class DistributionManager(val env: Environment) {
|
||||
private val logger = Logger[DistributionManager]
|
||||
import DistributionManager._
|
||||
|
||||
/** Determines paths that should be used by the launcher.
|
||||
*/
|
||||
@ -93,30 +112,56 @@ class DistributionManager(val env: Environment) {
|
||||
paths
|
||||
}
|
||||
|
||||
val ENGINES_DIRECTORY = "dist"
|
||||
val RUNTIMES_DIRECTORY = "runtime"
|
||||
val CONFIG_DIRECTORY = "config"
|
||||
val BIN_DIRECTORY = "bin"
|
||||
val LOCK_DIRECTORY = "lock"
|
||||
val LOG_DIRECTORY = "log"
|
||||
val TMP_DIRECTORY = "tmp"
|
||||
|
||||
protected def detectPaths(): DistributionPaths = {
|
||||
val dataRoot = LocallyInstalledDirectories.dataDirectory
|
||||
val configRoot = LocallyInstalledDirectories.configDirectory
|
||||
val runRoot = LocallyInstalledDirectories.runtimeDirectory
|
||||
val home = detectEnsoHome()
|
||||
DistributionPaths(
|
||||
dataRoot = dataRoot,
|
||||
runtimes = dataRoot / RUNTIMES_DIRECTORY,
|
||||
engines = dataRoot / ENGINES_DIRECTORY,
|
||||
bundle = detectBundle(),
|
||||
config = configRoot,
|
||||
locks = runRoot / LOCK_DIRECTORY,
|
||||
logs = LocallyInstalledDirectories.logDirectory,
|
||||
unsafeTemporaryDirectory = dataRoot / TMP_DIRECTORY
|
||||
dataRoot = dataRoot,
|
||||
runtimes = dataRoot / RUNTIMES_DIRECTORY,
|
||||
engines = dataRoot / ENGINES_DIRECTORY,
|
||||
bundle = detectBundle(),
|
||||
config = configRoot,
|
||||
locks = runRoot / LOCK_DIRECTORY,
|
||||
logs = LocallyInstalledDirectories.logDirectory,
|
||||
unsafeTemporaryDirectory = dataRoot / TMP_DIRECTORY,
|
||||
customEditions = detectCustomEditionPaths(home),
|
||||
localLibrariesSearchPaths = detectLocalLibraryPaths(home),
|
||||
ensoHome = home
|
||||
)
|
||||
}
|
||||
|
||||
private val ENSO_HOME = "ENSO_HOME"
|
||||
private val ENSO_EDITION_PATH = "ENSO_EDITION_PATH"
|
||||
private val ENSO_LIBRARY_PATH = "ENSO_LIBRARY_PATH"
|
||||
|
||||
/** Finds the path to the ENSO_HOME directory that is used for keeping user's
|
||||
* projects, libraries and other custom artifacts.
|
||||
*/
|
||||
protected def detectEnsoHome(): Path =
|
||||
env.getEnvPath(ENSO_HOME).getOrElse(env.getUserProfile / "enso")
|
||||
|
||||
/** Finds the paths to look for custom editions, which may be overridden by
|
||||
* setting the ENSO_EDITION_PATH environment variable.
|
||||
*/
|
||||
protected def detectCustomEditionPaths(ensoHome: Path): Seq[Path] =
|
||||
env
|
||||
.getEnvPaths(ENSO_EDITION_PATH)
|
||||
.getOrElse {
|
||||
Seq(ensoHome / DistributionManager.Home.EDITIONS_DIRECTORY)
|
||||
}
|
||||
|
||||
/** Finds the paths to look for local libraries, which may be overridden by
|
||||
* setting the ENSO_LIBRARY_PATH environment variable.
|
||||
*/
|
||||
protected def detectLocalLibraryPaths(ensoHome: Path): Seq[Path] =
|
||||
env
|
||||
.getEnvPaths(ENSO_LIBRARY_PATH)
|
||||
.getOrElse {
|
||||
Seq(ensoHome / DistributionManager.Home.LIBRARIES_DIRECTORY)
|
||||
}
|
||||
|
||||
/** Name of the file that should be placed in the distribution root to mark it
|
||||
* as running in portable mode.
|
||||
*/
|
||||
@ -315,3 +360,24 @@ class DistributionManager(val env: Environment) {
|
||||
safeDataDirectory.exists(Files.isDirectory(_))
|
||||
}
|
||||
}
|
||||
|
||||
/** A helper object that contains constants defining names of various
|
||||
* directories used by Enso components.
|
||||
*/
|
||||
object DistributionManager {
|
||||
val ENGINES_DIRECTORY = "dist"
|
||||
val RUNTIMES_DIRECTORY = "runtime"
|
||||
val CONFIG_DIRECTORY = "config"
|
||||
val BIN_DIRECTORY = "bin"
|
||||
val LOCK_DIRECTORY = "lock"
|
||||
val LOG_DIRECTORY = "log"
|
||||
val TMP_DIRECTORY = "tmp"
|
||||
val EDITIONS_DIRECTORY = "editions"
|
||||
val LIBRARIES_DIRECTORY = "lib"
|
||||
|
||||
/** Defines paths inside of the ENSO_HOME directory. */
|
||||
object Home {
|
||||
val EDITIONS_DIRECTORY = "editions"
|
||||
val LIBRARIES_DIRECTORY = "libraries"
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.enso.distribution
|
||||
|
||||
import org.enso.editions
|
||||
import org.enso.editions.provider.FileSystemEditionProvider
|
||||
import org.enso.editions.{EditionResolver, Editions, EnsoVersion}
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
/** A helper class for resolving editions backed by the Edition storage managed
|
||||
* by the DistributionManager.
|
||||
*/
|
||||
case class EditionManager(distributionManager: DistributionManager) {
|
||||
private val editionProvider = FileSystemEditionProvider(
|
||||
distributionManager.paths.editionSearchPaths.toList
|
||||
)
|
||||
|
||||
private val editionResolver = EditionResolver(editionProvider)
|
||||
private val engineVersionResolver =
|
||||
editions.EngineVersionResolver(editionProvider)
|
||||
|
||||
/** Resolves a raw edition, loading its parents from the edition search path.
|
||||
* @param edition the edition to resolve
|
||||
* @return the resolved edition
|
||||
*/
|
||||
def resolveEdition(
|
||||
edition: Editions.RawEdition
|
||||
): Try[Editions.ResolvedEdition] =
|
||||
editionResolver.resolve(edition).toTry
|
||||
|
||||
/** Resolves the engine version that should be used based on the provided raw
|
||||
* edition configuration.
|
||||
* @param edition the edition configuration to base the selected version on
|
||||
* @return the resolved engine version
|
||||
*/
|
||||
def resolveEngineVersion(edition: Editions.RawEdition): Try[EnsoVersion] =
|
||||
engineVersionResolver.resolveEnsoVersion(edition).toTry
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
package org.enso.runtimeversionmanager
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.logger.masking.MaskedString
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import scala.util.Try
|
||||
|
||||
/** Gathers some helper methods querying the system environment.
|
||||
@ -39,26 +38,38 @@ trait Environment {
|
||||
*
|
||||
* If it is not defined or is not a valid path, returns None.
|
||||
*/
|
||||
def getEnvPath(key: String): Option[Path] = {
|
||||
def parsePathWithWarning(str: String): Option[Path] = {
|
||||
val result = safeParsePath(str)
|
||||
if (result.isEmpty) {
|
||||
Logger[Environment].warn(
|
||||
"System variable [{}] was set to [{}], but it did not " +
|
||||
"represent a valid path, so it has been ignored.",
|
||||
key,
|
||||
MaskedString(str)
|
||||
)
|
||||
def getEnvPath(key: String): Option[Path] =
|
||||
getEnvVar(key).flatMap(parsePathWithWarning(key))
|
||||
|
||||
/** Queries the system environment for the given variable that should
|
||||
* represent a list of valid filesystem paths.
|
||||
*
|
||||
* If a path is on the list but is invalid, it is skipped.
|
||||
*/
|
||||
def getEnvPaths(key: String): Option[Seq[Path]] =
|
||||
getEnvVar(key)
|
||||
.map { value =>
|
||||
value
|
||||
.split(File.pathSeparator)
|
||||
.toSeq
|
||||
.flatMap(str => parsePathWithWarning(key)(str).toSeq)
|
||||
}
|
||||
|
||||
result
|
||||
private def parsePathWithWarning(key: String)(str: String): Option[Path] = {
|
||||
val result = safeParsePath(str)
|
||||
if (result.isEmpty) {
|
||||
Logger[Environment].warn(
|
||||
"System variable [{}] was set to [{}], but it did not " +
|
||||
"represent a valid path, so it has been ignored.",
|
||||
key,
|
||||
MaskedString(str)
|
||||
)
|
||||
}
|
||||
|
||||
getEnvVar(key).flatMap(parsePathWithWarning)
|
||||
result
|
||||
}
|
||||
|
||||
/** Returns the system PATH, if available.
|
||||
*/
|
||||
/** Returns the system PATH, if available. */
|
||||
def getSystemPath: Seq[Path] =
|
||||
getEnvVar("PATH")
|
||||
.map(_.split(File.pathSeparatorChar).toSeq.flatMap(safeParsePath))
|
||||
@ -85,6 +96,33 @@ trait Environment {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the location of the user profile directory (`%UserProfile%`) on
|
||||
* Windows.
|
||||
*/
|
||||
def getWindowsUserProfile: Path = {
|
||||
if (!OS.isWindows)
|
||||
throw new IllegalStateException(
|
||||
"fatal error: USERPROFILE should be queried only on Windows."
|
||||
)
|
||||
else {
|
||||
getEnvVar("USERPROFILE").flatMap(safeParsePath) match {
|
||||
case Some(path) => path
|
||||
case None =>
|
||||
throw new RuntimeException(
|
||||
"fatal error: %USERPROFILE% environment variable is not defined."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a path to the user's home directory, as defined on their
|
||||
* particular Operating System.
|
||||
*
|
||||
* On UNIX, this is $HOME and on Windows it is %UserProfile%.
|
||||
*/
|
||||
def getUserProfile: Path =
|
||||
if (OS.isWindows) getWindowsUserProfile else getHome
|
||||
|
||||
/** Returns the location of the local application data directory
|
||||
* (`%LocalAppData%`) on Windows.
|
||||
*
|
@ -1,4 +1,7 @@
|
||||
package org.enso.runtimeversionmanager
|
||||
package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
import java.io.PrintWriter
|
||||
import java.nio.file.attribute.PosixFilePermissions
|
||||
@ -8,10 +11,6 @@ import java.nio.file.{
|
||||
Path,
|
||||
StandardCopyOption
|
||||
}
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
import scala.collection.Factory
|
||||
import scala.jdk.StreamConverters._
|
||||
import scala.util.Using
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager
|
||||
package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import io.circe.{Decoder, DecodingFailure}
|
@ -1,10 +1,9 @@
|
||||
package org.enso.runtimeversionmanager.distribution
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
package org.enso.distribution
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.enso.runtimeversionmanager.Environment
|
||||
import org.enso.runtimeversionmanager.FileSystem.PathSyntax
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
/** A specialized variant of [[DistributionManager]] that is able to detect if
|
||||
* the currently running distribution is running in portable or locally
|
||||
@ -69,15 +68,20 @@ class PortableDistributionManager(env: Environment)
|
||||
override protected def detectPaths(): DistributionPaths =
|
||||
if (isRunningPortable) {
|
||||
val root = env.getPathToRunningExecutable.getParent.getParent
|
||||
val home = detectEnsoHome()
|
||||
import DistributionManager._
|
||||
DistributionPaths(
|
||||
dataRoot = root,
|
||||
runtimes = root / RUNTIMES_DIRECTORY,
|
||||
engines = root / ENGINES_DIRECTORY,
|
||||
bundle = None,
|
||||
config = root / CONFIG_DIRECTORY,
|
||||
locks = root / LOCK_DIRECTORY,
|
||||
logs = root / LOG_DIRECTORY,
|
||||
unsafeTemporaryDirectory = root / TMP_DIRECTORY
|
||||
dataRoot = root,
|
||||
runtimes = root / RUNTIMES_DIRECTORY,
|
||||
engines = root / ENGINES_DIRECTORY,
|
||||
bundle = None,
|
||||
config = root / CONFIG_DIRECTORY,
|
||||
locks = root / LOCK_DIRECTORY,
|
||||
logs = root / LOG_DIRECTORY,
|
||||
unsafeTemporaryDirectory = root / TMP_DIRECTORY,
|
||||
customEditions = detectCustomEditionPaths(home),
|
||||
localLibrariesSearchPaths = detectLocalLibraryPaths(home),
|
||||
ensoHome = home
|
||||
)
|
||||
} else super.detectPaths()
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
import java.nio.channels.{FileChannel, FileLock}
|
||||
import java.nio.file.{Files, Path, StandardOpenOption}
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
import scala.util.Using.Releasable
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
/** Manages locks that can be used to synchronize different launcher processes
|
||||
* running in parallel to avoid components corruption caused by simultaneous
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
/** Defines the lock type.
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
/** Defines callbacks that can be called when a requested resource is locked and
|
||||
* the application has to wait for other processes.
|
@ -0,0 +1,16 @@
|
||||
package org.enso.distribution.locking
|
||||
|
||||
/** Represents a resource that can be locked. */
|
||||
trait Resource {
|
||||
|
||||
/** Name of the resource.
|
||||
*
|
||||
* Must be a valid filename part.
|
||||
*/
|
||||
def name: String
|
||||
|
||||
/** A message that is displayed by default if the lock on that resource cannot
|
||||
* be acquired immediately.
|
||||
*/
|
||||
def waitMessage: String
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
import com.typesafe.scalalogging.Logger
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.enso.runtimeversionmanager.locking
|
||||
package org.enso.distribution.locking
|
||||
|
||||
import java.nio.file.Path
|
||||
|
@ -0,0 +1,65 @@
|
||||
package org.enso.editions
|
||||
|
||||
import cats.Show
|
||||
|
||||
/** Indicates an error during resolution of a raw edition. */
|
||||
sealed class EditionResolutionError(message: String, cause: Throwable = null)
|
||||
extends RuntimeException(message, cause)
|
||||
|
||||
object EditionResolutionError {
|
||||
|
||||
/** Indicates that a parent edition referenced in one of the editions that are
|
||||
* being loaded cannot be loaded.
|
||||
*/
|
||||
case class CannotLoadEdition(name: String, cause: Throwable)
|
||||
extends EditionResolutionError(
|
||||
s"Cannot load the edition: ${cause.getMessage}",
|
||||
cause
|
||||
)
|
||||
|
||||
/** Indicates that the edition cannot be parsed. */
|
||||
case class EditionParseError(message: String, cause: Throwable)
|
||||
extends EditionResolutionError(message, cause)
|
||||
|
||||
/** Indicates that a library defined in an edition references a repository
|
||||
* that is not defined in that edition or any of its parents, and so such a
|
||||
* reference is invalid.
|
||||
*/
|
||||
case class LibraryReferencesUndefinedRepository(
|
||||
libraryName: String,
|
||||
repositoryName: String
|
||||
) extends EditionResolutionError(
|
||||
s"A library `$libraryName` references a repository `$repositoryName` " +
|
||||
s"that is not defined in the edition or its parents."
|
||||
)
|
||||
|
||||
/** Indicates that the chain of parent editions forms a cycle which is not
|
||||
* allowed.
|
||||
*/
|
||||
case class EditionResolutionCycle(editions: List[String])
|
||||
extends EditionResolutionError(
|
||||
s"Edition resolution encountered a cycle: ${editions.mkString(" -> ")}"
|
||||
)
|
||||
|
||||
/** Wraps a Circe's decoding error into a more user-friendly error. */
|
||||
def wrapDecodingError(decodingError: io.circe.Error): EditionParseError = {
|
||||
val errorMessage =
|
||||
implicitly[Show[io.circe.Error]].show(decodingError)
|
||||
EditionParseError(
|
||||
s"Could not parse the edition: $errorMessage",
|
||||
decodingError
|
||||
)
|
||||
}
|
||||
|
||||
/** Wraps a general error thrown when loading a parsing an edition into a more
|
||||
* specific error type.
|
||||
*/
|
||||
def wrapLoadingError(
|
||||
editionName: String,
|
||||
throwable: Throwable
|
||||
): EditionResolutionError =
|
||||
throwable match {
|
||||
case decodingError: io.circe.Error => wrapDecodingError(decodingError)
|
||||
case other => CannotLoadEdition(editionName, other)
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package org.enso.editions
|
||||
|
||||
import cats.implicits._
|
||||
import org.enso.editions.EditionResolutionError.{
|
||||
EditionResolutionCycle,
|
||||
LibraryReferencesUndefinedRepository
|
||||
}
|
||||
import org.enso.editions.Editions.{RawEdition, ResolvedEdition}
|
||||
import org.enso.editions.provider.EditionProvider
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/** A helper class that allows to resolve a [[RawEdition]] into a
|
||||
* [[ResolvedEdition]] by loading its parents and resolving the repository
|
||||
* references.
|
||||
*
|
||||
* @param provider an [[EditionProvider]] that is used for loading the
|
||||
* referenced parent editions
|
||||
*/
|
||||
case class EditionResolver(provider: EditionProvider) {
|
||||
|
||||
/** Runs the edition resolution.
|
||||
*
|
||||
* @param edition the raw edition to resolve
|
||||
* @return either a resolution error or the resolved edition
|
||||
*/
|
||||
def resolve(
|
||||
edition: RawEdition
|
||||
): Either[EditionResolutionError, ResolvedEdition] =
|
||||
resolveEdition(edition, Nil)
|
||||
|
||||
/** A helper method that resolves an edition and keeps a list of already
|
||||
* visited edition names to avoid cycles.
|
||||
*/
|
||||
private def resolveEdition(
|
||||
edition: RawEdition,
|
||||
visitedEditions: List[String]
|
||||
): Either[EditionResolutionError, ResolvedEdition] =
|
||||
for {
|
||||
parent <- resolveParent(edition.parent, visitedEditions)
|
||||
libraries <- resolveLibraries(
|
||||
edition.libraries,
|
||||
edition.repositories,
|
||||
parent
|
||||
)
|
||||
} yield Editions.Resolved.Edition(
|
||||
parent = parent,
|
||||
engineVersion = edition.engineVersion,
|
||||
repositories = edition.repositories,
|
||||
libraries = libraries
|
||||
)
|
||||
|
||||
/** A helper method for resolving libraries.
|
||||
*
|
||||
* @param libraries the raw mapping of libraries
|
||||
* @param currentRepositories the mapping of repositories defined in the
|
||||
* current edition
|
||||
* @param parent an optional reference to an (already resolved) parent
|
||||
* edition, which is used if the library references a
|
||||
* repository that was not defined in `currentRepositories`
|
||||
* @return either an error indicating a reference to an unknown repository or
|
||||
* a mapping of resolved libraries
|
||||
*/
|
||||
private def resolveLibraries(
|
||||
libraries: Map[String, Editions.Raw.Library],
|
||||
currentRepositories: Map[String, Editions.Repository],
|
||||
parent: Option[ResolvedEdition]
|
||||
): Either[
|
||||
LibraryReferencesUndefinedRepository,
|
||||
Map[String, Editions.Resolved.Library]
|
||||
] = {
|
||||
val resolvedPairs: Either[
|
||||
LibraryReferencesUndefinedRepository,
|
||||
List[(String, Editions.Resolved.Library)]
|
||||
] =
|
||||
libraries.toList.traverse { case (name, library) =>
|
||||
val resolved = resolveLibrary(library, currentRepositories, parent)
|
||||
resolved.map((name, _))
|
||||
}
|
||||
|
||||
resolvedPairs.map(Map.from)
|
||||
}
|
||||
|
||||
/** A helper method to resolve a single library.
|
||||
*
|
||||
* @param library the library to resolve
|
||||
* @param currentRepositories the mapping of repositories defined in the
|
||||
* current edition
|
||||
* @param parent an optional reference to an (already resolved) parent
|
||||
* edition, which is used if the library references a
|
||||
* repository that was not defined in `currentRepositories`
|
||||
* @return either an error or the resolved library
|
||||
*/
|
||||
@tailrec
|
||||
private def resolveLibrary(
|
||||
library: Editions.Raw.Library,
|
||||
currentRepositories: Map[String, Editions.Repository],
|
||||
parent: Option[ResolvedEdition]
|
||||
): Either[
|
||||
LibraryReferencesUndefinedRepository,
|
||||
Editions.Resolved.Library
|
||||
] = library match {
|
||||
case Editions.Raw.LocalLibrary(qualifiedName) =>
|
||||
Right(Editions.Resolved.LocalLibrary(qualifiedName))
|
||||
case Editions.Raw.PublishedLibrary(
|
||||
qualifiedName,
|
||||
version,
|
||||
repositoryName
|
||||
) =>
|
||||
(currentRepositories.get(repositoryName), parent) match {
|
||||
case (Some(repository), _) =>
|
||||
Right(
|
||||
Editions.Resolved
|
||||
.PublishedLibrary(qualifiedName, version, repository)
|
||||
)
|
||||
case (None, Some(parentEdition)) =>
|
||||
resolveLibrary(
|
||||
library,
|
||||
parentEdition.repositories,
|
||||
parentEdition.parent
|
||||
)
|
||||
case (None, None) =>
|
||||
Left(
|
||||
LibraryReferencesUndefinedRepository(
|
||||
libraryName = library.qualifiedName,
|
||||
repositoryName = repositoryName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A helper function that takes an optional parent name and list of already
|
||||
* seen editions and tries to load the parent (if present), but avoiding
|
||||
* cycles (returning an error if a cycle was encountered).
|
||||
*/
|
||||
private def resolveParent(
|
||||
parent: Option[String],
|
||||
visitedEditions: List[String]
|
||||
): Either[EditionResolutionError, Option[ResolvedEdition]] = parent match {
|
||||
case Some(parentName) =>
|
||||
if (visitedEditions.contains(parentName)) {
|
||||
val cycle = parentName :: visitedEditions
|
||||
Left(EditionResolutionCycle(cycle.reverse))
|
||||
} else
|
||||
for {
|
||||
rawParent <- provider
|
||||
.findEditionForName(parentName)
|
||||
.toEither
|
||||
.left
|
||||
.map(EditionResolutionError.CannotLoadEdition(parentName, _))
|
||||
res <- resolveEdition(rawParent, parentName :: visitedEditions)
|
||||
} yield Some(res)
|
||||
case None => Right(None)
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
package org.enso.editions
|
||||
|
||||
import cats.Show
|
||||
import io.circe.syntax.EncoderOps
|
||||
import io.circe._
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.{Raw, Repository}
|
||||
import org.enso.editions.SemVerJson._
|
||||
|
||||
import java.io.FileReader
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import scala.util.{Failure, Try, Using}
|
||||
|
||||
/** Gathers methods for decoding and encoding of Raw editions. */
|
||||
object EditionSerialization {
|
||||
|
||||
/** Tries to parse an edition definition from a string in the YAML format. */
|
||||
def parseYamlString(yamlString: String): Try[Raw.Edition] =
|
||||
yaml.parser
|
||||
.parse(yamlString)
|
||||
.flatMap(_.as[Raw.Edition])
|
||||
.left
|
||||
.map(EditionResolutionError.wrapDecodingError)
|
||||
.toTry
|
||||
|
||||
/** Tries to load an edition definition from a YAML file. */
|
||||
def loadEdition(path: Path): Try[Raw.Edition] =
|
||||
Using(new FileReader(path.toFile)) { reader =>
|
||||
yaml.parser
|
||||
.parse(reader)
|
||||
.flatMap(_.as[Raw.Edition])
|
||||
.toTry
|
||||
}.flatten
|
||||
.recoverWith { error =>
|
||||
Failure(
|
||||
EditionResolutionError.wrapLoadingError(
|
||||
path.getFileName.toString,
|
||||
error
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** A [[Decoder]] instance for [[Raw.Edition]].
|
||||
*
|
||||
* It can be used to decode nested editions in other kinds of configurations
|
||||
* files, for example in `package.yaml`.
|
||||
*/
|
||||
implicit val editionDecoder: Decoder[Raw.Edition] = { json =>
|
||||
for {
|
||||
parent <- json.get[Option[EditionName]](Fields.parent)
|
||||
engineVersion <- json.get[Option[EnsoVersion]](Fields.engineVersion)
|
||||
_ <-
|
||||
if (parent.isEmpty && engineVersion.isEmpty)
|
||||
Left(
|
||||
DecodingFailure(
|
||||
s"The edition must specify at least one of " +
|
||||
s"${Fields.engineVersion} or ${Fields.parent}.",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
else Right(())
|
||||
repositories <-
|
||||
json.getOrElse[Seq[Repository]](Fields.repositories)(Seq())
|
||||
libraries <- json.getOrElse[Seq[Raw.Library]](Fields.libraries)(Seq())
|
||||
res <- {
|
||||
val repositoryMap = Map.from(repositories.map(r => (r.name, r)))
|
||||
val libraryMap = Map.from(libraries.map(l => (l.qualifiedName, l)))
|
||||
if (libraryMap.size != libraries.size)
|
||||
Left(
|
||||
DecodingFailure(
|
||||
"Names of libraries defined within a single edition file must be unique.",
|
||||
json.downField(Fields.libraries).history
|
||||
)
|
||||
)
|
||||
else if (repositoryMap.size != repositories.size)
|
||||
Left(
|
||||
DecodingFailure(
|
||||
"Names of repositories defined within a single edition file must be unique.",
|
||||
json.downField(Fields.libraries).history
|
||||
)
|
||||
)
|
||||
else
|
||||
Right(
|
||||
Raw.Edition(
|
||||
parent = parent.map(_.name),
|
||||
engineVersion = engineVersion,
|
||||
repositories = repositoryMap,
|
||||
libraries = libraryMap
|
||||
)
|
||||
)
|
||||
}
|
||||
} yield res
|
||||
}
|
||||
|
||||
/** An [[Encoder]] instance for [[Raw.Edition]]. */
|
||||
implicit val editionEncoder: Encoder[Raw.Edition] = { edition =>
|
||||
val parent = edition.parent.map { parent => Fields.parent -> parent.asJson }
|
||||
val engineVersion = edition.engineVersion.map { version =>
|
||||
Fields.engineVersion -> version.asJson
|
||||
}
|
||||
|
||||
if (parent.isEmpty && engineVersion.isEmpty) {
|
||||
throw new IllegalArgumentException(
|
||||
"Internal error: An edition must specify at least the engine version or extends clause"
|
||||
)
|
||||
}
|
||||
|
||||
val repositories =
|
||||
if (edition.repositories.isEmpty) None
|
||||
else Some(Fields.repositories -> edition.repositories.values.asJson)
|
||||
val libraries =
|
||||
if (edition.libraries.isEmpty) None
|
||||
else Some(Fields.libraries -> edition.libraries.values.asJson)
|
||||
Json.obj(
|
||||
parent.toSeq ++ engineVersion.toSeq ++ repositories.toSeq ++ libraries.toSeq: _*
|
||||
)
|
||||
}
|
||||
|
||||
object Fields {
|
||||
val name = "name"
|
||||
val version = "version"
|
||||
val repository = "repository"
|
||||
val url = "url"
|
||||
val parent = "extends"
|
||||
val engineVersion = "engine-version"
|
||||
val repositories = "repositories"
|
||||
val libraries = "libraries"
|
||||
val localRepositoryName = "local"
|
||||
}
|
||||
|
||||
case class EditionLoadingError(message: String, cause: Throwable)
|
||||
extends RuntimeException(message, cause) {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toString: String = message
|
||||
}
|
||||
|
||||
object EditionLoadingError {
|
||||
|
||||
/** Creates a [[EditionLoadingError]] by wrapping another [[Throwable]].
|
||||
*
|
||||
* Special logic is used for [[io.circe.Error]] to display the error
|
||||
* summary in a human-readable way.
|
||||
*/
|
||||
def fromThrowable(throwable: Throwable): EditionLoadingError =
|
||||
throwable match {
|
||||
case decodingError: io.circe.Error =>
|
||||
val errorMessage =
|
||||
implicitly[Show[io.circe.Error]].show(decodingError)
|
||||
EditionLoadingError(
|
||||
s"Could not parse the edition file: $errorMessage",
|
||||
decodingError
|
||||
)
|
||||
case other =>
|
||||
EditionLoadingError(s"Could not load the edition file: $other", other)
|
||||
}
|
||||
}
|
||||
|
||||
/** A helper opaque type to handle special parsing logic of edition names.
|
||||
*
|
||||
* The issue is that if an edition is called `2021.4` and it is written
|
||||
* unquoted inside of a YAML file, that is treated as a floating point
|
||||
* number, so special care must be taken to correctly parse it.
|
||||
*/
|
||||
private case class EditionName(name: String)
|
||||
|
||||
implicit private val editionNameDecoder: Decoder[EditionName] = { json =>
|
||||
json
|
||||
.as[String]
|
||||
.orElse(json.as[Int].map(_.toString))
|
||||
.orElse(json.as[Float].map(_.toString))
|
||||
.map(EditionName)
|
||||
}
|
||||
|
||||
implicit private val libraryDecoder: Decoder[Raw.Library] = { json =>
|
||||
def makeLibrary(name: String, repository: String, version: Option[SemVer]) =
|
||||
if (repository == Fields.localRepositoryName)
|
||||
if (version.isDefined)
|
||||
Left(
|
||||
DecodingFailure(
|
||||
"Version field must not be set for libraries associated with the local repository.",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
else Right(Raw.LocalLibrary(name))
|
||||
else {
|
||||
version match {
|
||||
case Some(versionValue) =>
|
||||
Right(Raw.PublishedLibrary(name, versionValue, repository))
|
||||
case None =>
|
||||
Left(
|
||||
DecodingFailure(
|
||||
"Version field is mandatory for non-local libraries.",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
for {
|
||||
name <- json.get[String](Fields.name)
|
||||
repository <- json.get[String](Fields.repository)
|
||||
version <- json.get[Option[SemVer]](Fields.version)
|
||||
res <- makeLibrary(name, repository, version)
|
||||
} yield res
|
||||
}
|
||||
|
||||
implicit private val libraryEncoder: Encoder[Raw.Library] = {
|
||||
case Raw.LocalLibrary(name) =>
|
||||
Json.obj(
|
||||
Fields.name -> name.asJson,
|
||||
Fields.repository -> Fields.localRepositoryName.asJson
|
||||
)
|
||||
case Raw.PublishedLibrary(name, version, repository) =>
|
||||
Json.obj(
|
||||
Fields.name -> name.asJson,
|
||||
Fields.version -> version.asJson,
|
||||
Fields.repository -> repository.asJson
|
||||
)
|
||||
}
|
||||
|
||||
implicit private val urlEncoder: Encoder[URL] = { url => url.toString.asJson }
|
||||
|
||||
implicit private val urlDecoder: Decoder[URL] = { json =>
|
||||
json.as[String].flatMap { str =>
|
||||
Try(new URL(str)).toEither.left.map(throwable =>
|
||||
DecodingFailure(
|
||||
s"Cannot parse an URL: ${throwable.getMessage}",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
implicit private val repositoryEncoder: Encoder[Repository] = { repo =>
|
||||
Json.obj(
|
||||
Fields.name -> repo.name.asJson,
|
||||
Fields.url -> repo.url.asJson
|
||||
)
|
||||
}
|
||||
|
||||
implicit private val repositoryDecoder: Decoder[Repository] = { json =>
|
||||
val nameField = json.downField(Fields.name)
|
||||
for {
|
||||
name <- nameField.as[String]
|
||||
url <- json.get[URL](Fields.url)
|
||||
res <-
|
||||
if (name == Fields.localRepositoryName)
|
||||
Left(
|
||||
DecodingFailure(
|
||||
s"A defined repository cannot be called " +
|
||||
s"`${Fields.localRepositoryName}` which is a reserved keyword.",
|
||||
nameField.history
|
||||
)
|
||||
)
|
||||
else Right(Repository(name, url))
|
||||
} yield res
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package org.enso.editions
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
|
||||
import java.net.URL
|
||||
import scala.util.Try
|
||||
|
||||
/** Defines the general edition structure.
|
||||
*
|
||||
* We split the data type into two categories: Raw and Resolved editions.
|
||||
*
|
||||
* Raw editions are directly parsed from a YAML structure and can be serialized
|
||||
* back to it. They are just an object model of the underlying YAML format.
|
||||
*
|
||||
* The raw edition may reference a parent edition or repositories by name.
|
||||
* The [[EditionResolver]] takes care of resolving a Raw edition into a
|
||||
* Resolved instance by loading and parsing any of its parents and replacing
|
||||
* the repository by-name references by actual references to the repository
|
||||
* instances.
|
||||
*/
|
||||
trait Editions {
|
||||
|
||||
/** The type of nested editions.
|
||||
*
|
||||
* Raw editions will refer to parents by their name and the Resolved edition
|
||||
* will contain a reference to an actual object.
|
||||
*/
|
||||
type NestedEditionType
|
||||
|
||||
/** The type of repository reference in listed libraries.
|
||||
*
|
||||
* Libraries in Raw editions will refer to repositories by their name and the
|
||||
* Resolved variant will contain a reference to the actual repository object
|
||||
* loaded from the edition or one of its parents.
|
||||
*/
|
||||
type LibraryRepositoryType
|
||||
|
||||
/** The library description included in the edition. */
|
||||
sealed trait Library {
|
||||
|
||||
/** The qualified name of the library.
|
||||
*
|
||||
* It should consist of a prefix followed by a dot an the library name, for
|
||||
* example `Prefix.Library_Name`.
|
||||
*/
|
||||
def qualifiedName: String
|
||||
}
|
||||
|
||||
/** Represents a local library. */
|
||||
case class LocalLibrary(override val qualifiedName: String) extends Library
|
||||
|
||||
/** Represents a specific version of the library that is published in a
|
||||
* repository.
|
||||
*
|
||||
* @param qualifiedName the qualified name of the library
|
||||
* @param version the exact version of the library that should be used
|
||||
* @param repository the recommended repository to download the library from,
|
||||
* if it is not yet cached
|
||||
*/
|
||||
case class PublishedLibrary(
|
||||
override val qualifiedName: String,
|
||||
version: SemVer,
|
||||
repository: LibraryRepositoryType
|
||||
) extends Library
|
||||
|
||||
/** An Edition describing the library resolution configuration.
|
||||
*
|
||||
* @param parent a parent edition (if applicable)
|
||||
* @param engineVersion an engine version; it should be defined if the
|
||||
* edition wants to override the setting from the parent
|
||||
* or if it has no parents
|
||||
* @param repositories a mapping of repositories directly defined in the
|
||||
* edition (does not include ones defined in the parents)
|
||||
* @param libraries a mapping of libraries directly defined in the edition
|
||||
* (does not include ones defined in the parents)
|
||||
*/
|
||||
case class Edition(
|
||||
parent: Option[NestedEditionType],
|
||||
engineVersion: Option[EnsoVersion],
|
||||
repositories: Map[String, Editions.Repository],
|
||||
libraries: Map[String, Library]
|
||||
) {
|
||||
if (parent.isEmpty && engineVersion.isEmpty)
|
||||
throw new IllegalArgumentException(
|
||||
"The edition must specify the engine version or a parent edition " +
|
||||
"that will imply it."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Editions {
|
||||
|
||||
/** Represents a repository that provides libraries. */
|
||||
case class Repository(name: String, url: URL)
|
||||
|
||||
object Repository {
|
||||
|
||||
/** A helper function that creates a Repository instance from a raw string
|
||||
* URL.
|
||||
*/
|
||||
def make(name: String, url: String): Try[Repository] = Try {
|
||||
Repository(name, new URL(url))
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements the Raw editions that can be directly parsed from a YAML
|
||||
* configuration.
|
||||
*
|
||||
* All references are typed as String, because this is what is directly read
|
||||
* from the configuration, without any postprocessing.
|
||||
*/
|
||||
object Raw extends Editions {
|
||||
override type NestedEditionType = String
|
||||
override type LibraryRepositoryType = String
|
||||
}
|
||||
|
||||
/** Implements the Resolved editions which are obtained by analyzing the Raw
|
||||
* edition and loading any of its parents.
|
||||
*/
|
||||
object Resolved extends Editions {
|
||||
override type NestedEditionType = this.Edition
|
||||
override type LibraryRepositoryType = Repository
|
||||
}
|
||||
|
||||
/** An alias for Raw editions. */
|
||||
type RawEdition = Raw.Edition
|
||||
|
||||
/** An alias for Resolved editions. */
|
||||
type ResolvedEdition = Resolved.Edition
|
||||
|
||||
/** Syntax helpers for resolved edition. */
|
||||
implicit class ResolvedEditionOps(edition: ResolvedEdition) {
|
||||
|
||||
/** Resolves the engine version that is implied by this resolved edition. It
|
||||
* is either the version override directly specified in the edition or the
|
||||
* version implied by its parent.
|
||||
*/
|
||||
def getEngineVersion: EnsoVersion = edition.engineVersion.getOrElse {
|
||||
val parent = edition.parent.getOrElse {
|
||||
throw new IllegalStateException(
|
||||
"Internal error: Resolved edition does not imply an engine version."
|
||||
)
|
||||
}
|
||||
parent.getEngineVersion
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.enso.editions
|
||||
|
||||
import org.enso.editions.Editions.RawEdition
|
||||
import org.enso.editions.provider.EditionProvider
|
||||
|
||||
/** A helper class which resolves the engine version that is entailed by the
|
||||
* edition configuration.
|
||||
*
|
||||
* It requires an [[EditionProvider]] instance, because the edition may not
|
||||
* specify the engine version directly, but it can just rely on what is
|
||||
* specified in a parent edition, so it needs a way to load the parent
|
||||
* editions.
|
||||
*/
|
||||
case class EngineVersionResolver(editionProvider: EditionProvider) {
|
||||
private val editionResolver = EditionResolver(editionProvider)
|
||||
|
||||
/** Returns the [[EnsoVersion]] that is entailed by the provided edition (or a
|
||||
* resolution error if the edition cannot be resolved).
|
||||
*/
|
||||
def resolveEnsoVersion(
|
||||
edition: RawEdition
|
||||
): Either[EditionResolutionError, EnsoVersion] = {
|
||||
for {
|
||||
edition <- editionResolver.resolve(edition)
|
||||
} yield edition.getEngineVersion
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package org.enso.pkg
|
||||
package org.enso.editions
|
||||
|
||||
import io.circe.syntax._
|
||||
import io.circe.{Decoder, DecodingFailure, Encoder}
|
@ -0,0 +1,16 @@
|
||||
package org.enso.editions
|
||||
|
||||
/** Represents a library name that should uniquely identify the library.
|
||||
*
|
||||
* The prefix is either a special prefix or a username.
|
||||
*/
|
||||
case class LibraryName(prefix: String, name: String) {
|
||||
|
||||
/** The qualified name of the library consists of its prefix and name
|
||||
* separated with a dot.
|
||||
*/
|
||||
def qualifiedName: String = s"$prefix.$name"
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toString: String = qualifiedName
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package org.enso.pkg
|
||||
package org.enso.editions
|
||||
|
||||
import io.circe.{Decoder, DecodingFailure, Encoder}
|
||||
import io.circe.syntax._
|
||||
import io.circe.{Decoder, DecodingFailure, Encoder}
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
|
||||
object SemVerJson {
|
@ -0,0 +1,16 @@
|
||||
package org.enso.editions.provider
|
||||
|
||||
import org.enso.editions.Editions
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
/** Interface for a provider of editions which is able to load a raw edition
|
||||
* based on its name.
|
||||
*
|
||||
* It is used when resolving parent edition references.
|
||||
*/
|
||||
trait EditionProvider {
|
||||
|
||||
/** Tries to load an edition with the given name. */
|
||||
def findEditionForName(name: String): Try[Editions.Raw.Edition]
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.enso.editions.provider
|
||||
|
||||
import org.enso.editions.{EditionSerialization, Editions}
|
||||
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
/** An implementation of [[EditionProvider]] that looks for the edition files in
|
||||
* a list of filesystem paths.
|
||||
*/
|
||||
case class FileSystemEditionProvider(searchPaths: List[Path])
|
||||
extends EditionProvider {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findEditionForName(name: String): Try[Editions.Raw.Edition] = {
|
||||
val result = findEdition(name, searchPaths)
|
||||
result match {
|
||||
case Left(EditionNotFound) =>
|
||||
Failure(new FileNotFoundException(s"Could not find edition `$name`."))
|
||||
case Left(EditionReadError(error)) => Failure(error)
|
||||
case Right(value) => Success(value)
|
||||
}
|
||||
}
|
||||
|
||||
private val editionSuffix = ".yaml"
|
||||
|
||||
@tailrec
|
||||
private def findEdition(
|
||||
name: String,
|
||||
paths: List[Path]
|
||||
): Either[EditionLoadingError, Editions.Raw.Edition] = paths match {
|
||||
case head :: tail =>
|
||||
val headResult = loadEdition(name, head)
|
||||
headResult match {
|
||||
case Left(EditionNotFound) =>
|
||||
findEdition(name, tail)
|
||||
case _ => headResult
|
||||
}
|
||||
case Nil => Left(EditionNotFound)
|
||||
}
|
||||
|
||||
sealed private trait EditionLoadingError
|
||||
private case object EditionNotFound extends EditionLoadingError
|
||||
private case class EditionReadError(error: Throwable)
|
||||
extends EditionLoadingError
|
||||
|
||||
private def loadEdition(
|
||||
name: String,
|
||||
path: Path
|
||||
): Either[EditionLoadingError, Editions.Raw.Edition] = {
|
||||
val fileName = name + editionSuffix
|
||||
val editionPath = path.resolve(fileName)
|
||||
if (Files.exists(editionPath)) {
|
||||
EditionSerialization
|
||||
.loadEdition(editionPath)
|
||||
.toEither
|
||||
.left
|
||||
.map(EditionReadError)
|
||||
} else Left(EditionNotFound)
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package org.enso.editions
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.EditionResolutionError.EditionResolutionCycle
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.provider.EditionProvider
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
import org.scalatest.{Inside, OptionValues}
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class EditionResolverSpec
|
||||
extends AnyWordSpec
|
||||
with Matchers
|
||||
with Inside
|
||||
with OptionValues {
|
||||
object FakeEditionProvider extends EditionProvider {
|
||||
val mainRepo = Repository.make("main", "https://example.com/main").get
|
||||
val editions: Map[String, Editions.RawEdition] = Map(
|
||||
"2021.0" -> Editions.Raw.Edition(
|
||||
parent = None,
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map(
|
||||
"main" -> mainRepo
|
||||
),
|
||||
libraries = Map(
|
||||
"Standard.Base" -> Editions.Raw
|
||||
.PublishedLibrary("Standard.Base", SemVer(1, 2, 3), "main")
|
||||
)
|
||||
),
|
||||
"cycleA" -> Editions.Raw.Edition(
|
||||
parent = Some("cycleB"),
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map(),
|
||||
libraries = Map()
|
||||
),
|
||||
"cycleB" -> Editions.Raw.Edition(
|
||||
parent = Some("cycleA"),
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map(),
|
||||
libraries = Map()
|
||||
)
|
||||
)
|
||||
|
||||
override def findEditionForName(name: String): Try[Editions.Raw.Edition] =
|
||||
editions
|
||||
.get(name)
|
||||
.map(Success(_))
|
||||
.getOrElse(
|
||||
Failure(new RuntimeException(s"Could not load edition `$name`."))
|
||||
)
|
||||
}
|
||||
|
||||
val resolver = EditionResolver(FakeEditionProvider)
|
||||
|
||||
"EditionResolver" should {
|
||||
"resolve a simple edition" in {
|
||||
val repo = Repository.make("foo", "http://example.com").get
|
||||
val edition = Editions.Raw.Edition(
|
||||
parent = None,
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map("foo" -> repo),
|
||||
libraries = Map(
|
||||
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
||||
"foo.bar" -> Editions.Raw
|
||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "foo")
|
||||
)
|
||||
)
|
||||
|
||||
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||
resolved.engineVersion shouldEqual edition.engineVersion
|
||||
resolved.parent should be(empty)
|
||||
resolved.repositories shouldEqual edition.repositories
|
||||
resolved.libraries should have size 2
|
||||
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
||||
.LocalLibrary("bar.baz")
|
||||
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), repo)
|
||||
}
|
||||
}
|
||||
|
||||
"resolve a nested edition" in {
|
||||
val edition = Editions.Raw.Edition(
|
||||
parent = Some("2021.0"),
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map(),
|
||||
libraries = Map(
|
||||
"bar.baz" -> Editions.Raw.LocalLibrary("bar.baz"),
|
||||
"foo.bar" -> Editions.Raw
|
||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
||||
)
|
||||
)
|
||||
|
||||
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||
resolved.parent should be(defined)
|
||||
resolved.libraries should have size 2
|
||||
resolved.libraries("bar.baz") shouldEqual Editions.Resolved
|
||||
.LocalLibrary("bar.baz")
|
||||
resolved.libraries("foo.bar") shouldEqual Editions.Resolved
|
||||
.PublishedLibrary(
|
||||
"foo.bar",
|
||||
SemVer(1, 2, 3),
|
||||
FakeEditionProvider.mainRepo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"correctly handle repository shadowing when resolving libraries" in {
|
||||
val localRepo = Repository.make("main", "http://example.com/local").get
|
||||
|
||||
localRepo should not equal FakeEditionProvider.mainRepo
|
||||
|
||||
val edition = Editions.Raw.Edition(
|
||||
parent = Some("2021.0"),
|
||||
engineVersion = None,
|
||||
repositories = Map("main" -> localRepo),
|
||||
libraries = Map(
|
||||
"foo.bar" -> Editions.Raw
|
||||
.PublishedLibrary("foo.bar", SemVer(1, 2, 3), "main")
|
||||
)
|
||||
)
|
||||
|
||||
inside(resolver.resolve(edition)) { case Right(resolved) =>
|
||||
resolved.parent should be(defined)
|
||||
resolved.libraries should have size 1
|
||||
resolved.libraries("foo.bar") shouldEqual
|
||||
Editions.Resolved.PublishedLibrary(
|
||||
"foo.bar",
|
||||
SemVer(1, 2, 3),
|
||||
localRepo
|
||||
)
|
||||
|
||||
resolved.parent.value.libraries("Standard.Base") shouldEqual
|
||||
Editions.Resolved.PublishedLibrary(
|
||||
"Standard.Base",
|
||||
SemVer(1, 2, 3),
|
||||
FakeEditionProvider.mainRepo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"avoid cycles in the resolution" in {
|
||||
val edition = Editions.Raw.Edition(
|
||||
parent = Some("cycleA"),
|
||||
engineVersion = Some(SemVerEnsoVersion(SemVer(1, 2, 3))),
|
||||
repositories = Map(),
|
||||
libraries = Map()
|
||||
)
|
||||
|
||||
val failure = resolver.resolve(edition)
|
||||
inside(failure) { case Left(EditionResolutionCycle(editions)) =>
|
||||
editions shouldEqual Seq("cycleA", "cycleB", "cycleA")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package org.enso.editions
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.scalatest.Inside
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
class EditionSerializationSpec extends AnyWordSpec with Matchers with Inside {
|
||||
"EditionSerialization" should {
|
||||
"parse an edition that just derives another one" in {
|
||||
val parsed = EditionSerialization.parseYamlString(
|
||||
"""extends: 2021.4
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed) { case Success(edition) =>
|
||||
edition.parent should contain("2021.4")
|
||||
edition.repositories should be(empty)
|
||||
edition.libraries should be(empty)
|
||||
edition.engineVersion should be(empty)
|
||||
}
|
||||
}
|
||||
|
||||
"parse a standalone edition" in {
|
||||
val parsed = EditionSerialization.parseYamlString(
|
||||
"""engine-version: 1.2.3-SNAPSHOT
|
||||
|repositories:
|
||||
| - name: example
|
||||
| url: https://example.com/
|
||||
| - name: foo
|
||||
| url: http://127.0.0.1:8080/root
|
||||
|libraries:
|
||||
| - name: Foo.Local
|
||||
| repository: local
|
||||
| - name: Bar.Baz
|
||||
| repository: example
|
||||
| version: 0.0.0
|
||||
| - name: A.B
|
||||
| repository: bar
|
||||
| version: 1.0.1
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed) { case Success(edition) =>
|
||||
edition.parent should be(empty)
|
||||
edition.repositories.size shouldEqual 2
|
||||
edition.repositories("example").name shouldEqual "example"
|
||||
edition
|
||||
.repositories("example")
|
||||
.url
|
||||
.toString shouldEqual "https://example.com/"
|
||||
edition.repositories("foo").name shouldEqual "foo"
|
||||
edition
|
||||
.repositories("foo")
|
||||
.url
|
||||
.toString shouldEqual "http://127.0.0.1:8080/root"
|
||||
|
||||
edition.libraries.values should contain theSameElementsAs Seq(
|
||||
Editions.Raw.LocalLibrary("Foo.Local"),
|
||||
Editions.Raw.PublishedLibrary("Bar.Baz", SemVer(0, 0, 0), "example"),
|
||||
Editions.Raw.PublishedLibrary("A.B", SemVer(1, 0, 1), "bar")
|
||||
)
|
||||
edition.engineVersion should contain(
|
||||
SemVerEnsoVersion(SemVer(1, 2, 3, Some("SNAPSHOT")))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"not parse an edition that does not define the engine version nor attempt to derive it" in {
|
||||
val parsed = EditionSerialization.parseYamlString(
|
||||
"""libraries:
|
||||
|- name: foo
|
||||
| repository: local
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed) { case Failure(exception) =>
|
||||
exception.getMessage should include(
|
||||
"The edition must specify at least one of"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"not allow invalid urls for repositories" in {
|
||||
val parsed = EditionSerialization.parseYamlString(
|
||||
"""extends: foo
|
||||
|repositories:
|
||||
|- name: bar
|
||||
| url: baz
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed) { case Failure(exception) =>
|
||||
exception.getMessage should include("Cannot parse an URL")
|
||||
}
|
||||
}
|
||||
|
||||
"not allow invalid version combinations for libraries" in {
|
||||
val parsed = EditionSerialization.parseYamlString(
|
||||
"""extends: foo
|
||||
|libraries:
|
||||
|- name: bar
|
||||
| repository: local
|
||||
| version: 1.2.3-SHOULD-NOT-BE-HERE
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed) { case Failure(exception) =>
|
||||
exception.getMessage should include("Version field must not be set")
|
||||
}
|
||||
|
||||
val parsed2 = EditionSerialization.parseYamlString(
|
||||
"""extends: foo
|
||||
|libraries:
|
||||
|- name: bar
|
||||
| repository: something
|
||||
|""".stripMargin
|
||||
)
|
||||
inside(parsed2) { case Failure(exception) =>
|
||||
exception.getMessage should include("Version field is mandatory")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{Editions, LibraryName}
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
import org.enso.librarymanager.published.{
|
||||
DefaultPublishedLibraryProvider,
|
||||
PublishedLibraryProvider
|
||||
}
|
||||
|
||||
/** A helper class for loading libraries. */
|
||||
case class LibraryLoader(distributionManager: DistributionManager) {
|
||||
private val localLibraryProvider =
|
||||
LocalLibraryProvider.make(distributionManager)
|
||||
private val resolver = LibraryResolver(localLibraryProvider)
|
||||
private val publishedLibraryProvider: PublishedLibraryProvider =
|
||||
new DefaultPublishedLibraryProvider(distributionManager)
|
||||
|
||||
/** Resolves the library version that should be used based on the
|
||||
* configuration and returns its location on the filesystem.
|
||||
*
|
||||
* If the library is not available, this operation may download it.
|
||||
*/
|
||||
def findLibrary(
|
||||
libraryName: LibraryName,
|
||||
edition: Editions.ResolvedEdition,
|
||||
preferLocalLibraries: Boolean
|
||||
): LibraryResolutionResult = {
|
||||
val resolvedVersion = resolver
|
||||
.resolveLibraryVersion(libraryName, edition, preferLocalLibraries)
|
||||
resolvedVersion match {
|
||||
case Left(error) =>
|
||||
LibraryResolutionResult.ResolutionFailure(error)
|
||||
case Right(LibraryVersion.Local) =>
|
||||
localLibraryProvider
|
||||
.findLibrary(libraryName)
|
||||
.map(LibraryResolutionResult.ResolvedImmediately)
|
||||
.getOrElse {
|
||||
LibraryResolutionResult.ResolutionFailure(
|
||||
LibraryResolutionError(
|
||||
s"Edition configuration forces to use the local version, but " +
|
||||
s"the `$libraryName` library is not present among local " +
|
||||
s"libraries."
|
||||
)
|
||||
)
|
||||
}
|
||||
case Right(LibraryVersion.Published(version, repository)) =>
|
||||
val dependencyResolver = { name: LibraryName =>
|
||||
resolver
|
||||
.resolveLibraryVersion(name, edition, preferLocalLibraries)
|
||||
.toOption
|
||||
}
|
||||
publishedLibraryProvider.findLibrary(
|
||||
libraryName,
|
||||
version,
|
||||
repository,
|
||||
dependencyResolver
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
/** Indicates an error of library resolution. */
|
||||
case class LibraryResolutionError(message: String)
|
||||
extends RuntimeException(message)
|
@ -0,0 +1,32 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.cli.task.TaskProgress
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** Encapsulates possible results of library resolution. */
|
||||
sealed trait LibraryResolutionResult
|
||||
object LibraryResolutionResult {
|
||||
|
||||
/** Indicates that the resolution has failed immediately with an error. */
|
||||
case class ResolutionFailure(error: Throwable) extends LibraryResolutionResult
|
||||
|
||||
/** Indicates that the resolution has succeeded immediately.
|
||||
*
|
||||
* The library was already available on disk and the path to it is returned.
|
||||
*/
|
||||
case class ResolvedImmediately(path: Path) extends LibraryResolutionResult
|
||||
|
||||
/** Indicates that the initial library resolution has succeeded (that the
|
||||
* corrrect version could be inferred from the edition) but the library was
|
||||
* not installed, so it must be downloaded.
|
||||
*
|
||||
* It contains a [[TaskProgress]] instance that can track the progress of the
|
||||
* download and will be completed once the library has been installed.
|
||||
*
|
||||
* The download and installation may of course fail as well, which is
|
||||
* indicated by failure of the [[TaskProgress]].
|
||||
*/
|
||||
case class ResolutionPending(result: TaskProgress[Path])
|
||||
extends LibraryResolutionResult
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import org.enso.editions.{Editions, LibraryName}
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
|
||||
/** A helper class that figures out which library version should be used in a
|
||||
* given configuration.
|
||||
*
|
||||
* It needs a [[LocalLibraryProvider]] instance, because if
|
||||
* `preferLocalLibraries` is set to true, the resulting library version depends
|
||||
* on whether a local library with a given name exists.
|
||||
*/
|
||||
case class LibraryResolver(
|
||||
localLibraryProvider: LocalLibraryProvider
|
||||
) {
|
||||
|
||||
/** Resolves the library version that entails from the provided configuration.
|
||||
*
|
||||
* The configuration consists of the edition and the flag specifying if local
|
||||
* libraries should override the edition settings.
|
||||
*
|
||||
* @param libraryName the name of the library to resolve
|
||||
* @param edition the edition configuration
|
||||
* @param preferLocalLibraries the flag indicating whether to prefer local
|
||||
* libraries that are present over what the
|
||||
* edition defines
|
||||
* @return
|
||||
*/
|
||||
def resolveLibraryVersion(
|
||||
libraryName: LibraryName,
|
||||
edition: Editions.ResolvedEdition,
|
||||
preferLocalLibraries: Boolean
|
||||
): Either[LibraryResolutionError, LibraryVersion] = {
|
||||
if (preferLocalLibraries) {
|
||||
localLibraryProvider
|
||||
.findLibrary(libraryName)
|
||||
.map(_ => Right(LibraryVersion.Local))
|
||||
.getOrElse {
|
||||
resolveLibraryFromEdition(libraryName, edition)
|
||||
}
|
||||
} else resolveLibraryFromEdition(libraryName, edition)
|
||||
}
|
||||
|
||||
/** Resolves the library version that entails from the edition.
|
||||
*
|
||||
* @param libraryName the name of the library to resolve
|
||||
* @param edition the edition configuration
|
||||
* @return either an error (if the library was not found in the edition) or
|
||||
* the entailed version
|
||||
*/
|
||||
def resolveLibraryFromEdition(
|
||||
libraryName: LibraryName,
|
||||
edition: Editions.ResolvedEdition
|
||||
): Either[LibraryResolutionError, LibraryVersion] = {
|
||||
import Editions.Resolved._
|
||||
val immediateResult =
|
||||
edition.libraries.get(libraryName.qualifiedName).map {
|
||||
case LocalLibrary(_) =>
|
||||
Right(LibraryVersion.Local)
|
||||
case PublishedLibrary(_, version, repository) =>
|
||||
Right(LibraryVersion.Published(version, repository))
|
||||
}
|
||||
|
||||
immediateResult.getOrElse {
|
||||
edition.parent match {
|
||||
case Some(parentEdition) =>
|
||||
resolveLibraryFromEdition(libraryName, parentEdition)
|
||||
case None =>
|
||||
Left(
|
||||
LibraryResolutionError(
|
||||
s"The library `$libraryName` is not defined within " +
|
||||
s"the edition."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
|
||||
/** A resolved version of the library. */
|
||||
sealed trait LibraryVersion
|
||||
object LibraryVersion {
|
||||
|
||||
/** Indicates that the version from the local library path should be used. */
|
||||
case object Local extends LibraryVersion
|
||||
|
||||
/** Indicates that a particular published version should be used.
|
||||
*
|
||||
* It also indicates the repository to download the library from if it is not
|
||||
* already cached.
|
||||
*/
|
||||
case class Published(version: SemVer, repository: Repository)
|
||||
extends LibraryVersion
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.enso.librarymanager.local
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.distribution.FileSystem.PathSyntax
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/** A default implementation of [[LocalLibraryProvider]]. */
|
||||
class DefaultLocalLibraryProvider(distributionManager: DistributionManager)
|
||||
extends LocalLibraryProvider {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||
findLibraryHelper(
|
||||
libraryName,
|
||||
distributionManager.paths.localLibrariesSearchPaths.toList
|
||||
)
|
||||
|
||||
/** Searches through the available library paths, checking if any one of them contains the requested library.
|
||||
*
|
||||
* The first path on the list takes precedence.
|
||||
*/
|
||||
@tailrec
|
||||
private def findLibraryHelper(
|
||||
libraryName: LibraryName,
|
||||
searchPaths: List[Path]
|
||||
): Option[Path] = searchPaths match {
|
||||
case head :: tail =>
|
||||
val potentialPath = head / libraryName.prefix / libraryName.name
|
||||
if (Files.exists(potentialPath) && Files.isDirectory(potentialPath))
|
||||
Some(potentialPath)
|
||||
else findLibraryHelper(libraryName, tail)
|
||||
case Nil => None
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.enso.librarymanager.local
|
||||
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.LibraryName
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** A provider for local libraries. */
|
||||
trait LocalLibraryProvider {
|
||||
|
||||
/** Returns the path to a local instance of the requested library, if it is
|
||||
* available.
|
||||
*/
|
||||
def findLibrary(libraryName: LibraryName): Option[Path]
|
||||
}
|
||||
|
||||
object LocalLibraryProvider {
|
||||
|
||||
/** Creates a default [[LocalLibraryProvider]] from a [[DistributionManager]].
|
||||
*/
|
||||
def make(distributionManager: DistributionManager): LocalLibraryProvider =
|
||||
new DefaultLocalLibraryProvider(distributionManager)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.enso.librarymanager.published
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.DistributionManager
|
||||
import org.enso.editions.{Editions, LibraryName}
|
||||
import org.enso.librarymanager.{LibraryResolutionResult, LibraryVersion}
|
||||
|
||||
import scala.annotation.nowarn
|
||||
|
||||
/** A default implementation of [[PublishedLibraryProvider]]. */
|
||||
class DefaultPublishedLibraryProvider(
|
||||
@nowarn("msg=never used") distributionManager: DistributionManager
|
||||
) extends PublishedLibraryProvider {
|
||||
|
||||
// TODO [RW] This is just a stub and will be properly implemented in #1772.
|
||||
|
||||
/** @inheritdoc */
|
||||
override def findLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer,
|
||||
recommendedRepository: Editions.Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): LibraryResolutionResult = LibraryResolutionResult.ResolutionFailure(
|
||||
new NotImplementedError(
|
||||
"TODO library management is not yet implemented"
|
||||
)
|
||||
)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.enso.librarymanager.published
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.librarymanager.{LibraryResolutionResult, LibraryVersion}
|
||||
|
||||
/** A provider of published libraries.
|
||||
*
|
||||
* It usually should use some kind of a cache to keep already downloaded
|
||||
* libraries and download new libraries on demand.
|
||||
*/
|
||||
trait PublishedLibraryProvider {
|
||||
|
||||
/** Tries to locate the requested library at a specific version.
|
||||
*
|
||||
* If the library is not present, a download may be attempted - this is where
|
||||
* the `recommendedRepository` is used to guide which repository should be
|
||||
* used for the download.
|
||||
*/
|
||||
def findLibrary(
|
||||
libraryName: LibraryName,
|
||||
version: SemVer,
|
||||
recommendedRepository: Repository,
|
||||
dependencyResolver: LibraryName => Option[LibraryVersion]
|
||||
): LibraryResolutionResult
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package org.enso.librarymanager
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.{DefaultEnsoVersion, Editions, LibraryName}
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
import org.enso.testkit.EitherValue
|
||||
import org.scalatest.Inside
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
class LibraryResolverSpec
|
||||
extends AnyWordSpec
|
||||
with Matchers
|
||||
with EitherValue
|
||||
with Inside {
|
||||
"LibraryResolver" should {
|
||||
val mainRepo = Repository.make("main", "https://example.com/main").get
|
||||
val parentEdition = Editions.Resolved.Edition(
|
||||
parent = None,
|
||||
engineVersion = Some(DefaultEnsoVersion),
|
||||
repositories = Map("main" -> mainRepo),
|
||||
libraries = Map(
|
||||
"Standard.Base" -> Editions.Resolved
|
||||
.PublishedLibrary("Standard.Base", SemVer(4, 5, 6), mainRepo)
|
||||
)
|
||||
)
|
||||
val customRepo = Repository.make("custom", "https://example.com/custom").get
|
||||
val currentEdition = Editions.Resolved.Edition(
|
||||
parent = Some(parentEdition),
|
||||
engineVersion = None,
|
||||
repositories = Map("custom" -> customRepo),
|
||||
libraries = Map(
|
||||
"Foo.Main" -> Editions.Resolved
|
||||
.PublishedLibrary("Foo.Main", SemVer(1, 0, 0), mainRepo),
|
||||
"Foo.My" -> Editions.Resolved
|
||||
.PublishedLibrary("Foo.My", SemVer(2, 0, 0), customRepo),
|
||||
"Foo.Local" -> Editions.Resolved.LocalLibrary("Foo.Local")
|
||||
)
|
||||
)
|
||||
|
||||
case class FakeLocalLibraryProvider(fixtures: Map[String, Path])
|
||||
extends LocalLibraryProvider {
|
||||
override def findLibrary(libraryName: LibraryName): Option[Path] =
|
||||
fixtures.get(libraryName.qualifiedName)
|
||||
}
|
||||
|
||||
val localLibraries = Map(
|
||||
"Foo.My" -> Path.of("./Foo/My"),
|
||||
"Foo.Local" -> Path.of("./Foo/Local"),
|
||||
"Standard.Base" -> Path.of("./Standard/Base")
|
||||
)
|
||||
|
||||
val resolver = LibraryResolver(FakeLocalLibraryProvider(localLibraries))
|
||||
|
||||
"correctly resolve libraries based on the edition" in {
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Standard", "Base"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = false
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Published(SemVer(4, 5, 6), mainRepo)
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "Main"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = false
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Published(SemVer(1, 0, 0), mainRepo)
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "My"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = false
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Published(SemVer(2, 0, 0), customRepo)
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "Local"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = false
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Local
|
||||
}
|
||||
|
||||
"correctly handle preference of local libraries" in {
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Standard", "Base"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = true
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Local
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "Main"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = true
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Published(SemVer(1, 0, 0), mainRepo)
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "My"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = true
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Local
|
||||
|
||||
resolver
|
||||
.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "Local"),
|
||||
edition = currentEdition,
|
||||
preferLocalLibraries = true
|
||||
)
|
||||
.rightValue shouldEqual
|
||||
LibraryVersion.Local
|
||||
}
|
||||
|
||||
"not fall back to a local library if it was not defined as such " +
|
||||
"explicitly nor `prefer-local-libraries` is set" in {
|
||||
val result = resolver.resolveLibraryVersion(
|
||||
libraryName = LibraryName("Foo", "Local"),
|
||||
edition = parentEdition,
|
||||
preferLocalLibraries = false
|
||||
)
|
||||
inside(result) { case Left(error) =>
|
||||
error.getMessage should include("not defined")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,13 @@
|
||||
package org.enso.pkg
|
||||
|
||||
import io.circe.syntax._
|
||||
import io.circe.generic.auto._
|
||||
import io.circe.{yaml, Decoder, DecodingFailure, Encoder, Json}
|
||||
import io.circe.{yaml, Decoder, DecodingFailure, Encoder, Json, JsonObject}
|
||||
import io.circe.yaml.Printer
|
||||
import org.enso.editions.{DefaultEnsoVersion, Editions, EnsoVersion}
|
||||
import org.enso.editions.EditionSerialization._
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
/** An extra project dependency.
|
||||
* @param name name of the package
|
||||
* @param version package version
|
||||
*/
|
||||
case class Dependency(name: String, version: String)
|
||||
|
||||
/** Contact information to a user.
|
||||
*
|
||||
* Used for defining authors and maintainers.
|
||||
@ -26,8 +21,7 @@ case class Contact(name: Option[String], email: Option[String]) {
|
||||
"At least one of fields `name` or `email` must be defined."
|
||||
)
|
||||
|
||||
/** @inheritdoc
|
||||
*/
|
||||
/** @inheritdoc */
|
||||
override def toString: String = {
|
||||
val space = if (name.isDefined && email.isDefined) " " else ""
|
||||
name.getOrElse("") + space + email.map(email => s"<$email>").getOrElse("")
|
||||
@ -35,15 +29,13 @@ case class Contact(name: Option[String], email: Option[String]) {
|
||||
}
|
||||
object Contact {
|
||||
|
||||
/** Fields for use when serializing the [[Contact]].
|
||||
*/
|
||||
/** Fields for use when serializing the [[Contact]]. */
|
||||
object Fields {
|
||||
val Name = "name"
|
||||
val Email = "email"
|
||||
}
|
||||
|
||||
/** [[Encoder]] instance for the [[Contact]].
|
||||
*/
|
||||
/** [[Encoder]] instance for the [[Contact]]. */
|
||||
implicit val encoder: Encoder[Contact] = { contact =>
|
||||
val name = contact.name.map(Fields.Name -> _.asJson)
|
||||
val email = contact.email.map(Fields.Email -> _.asJson)
|
||||
@ -78,14 +70,15 @@ object Contact {
|
||||
*
|
||||
* @param name package name
|
||||
* @param version package version
|
||||
* @param ensoVersion version of the Enso engine associated with the package,
|
||||
* can be set to `default` which defaults to the locally
|
||||
* installed version
|
||||
* @param license package license
|
||||
* @param authors name and contact information of the package author(s)
|
||||
* @param maintainers name and contact information of current package
|
||||
* maintainer(s)
|
||||
* @param dependencies a list of package dependencies
|
||||
* @param edition the Edition associated with the project; it implies the
|
||||
* engine version and dependency configuration to be used
|
||||
* @param preferLocalLibraries specifies if library resolution should prefer
|
||||
* local libraries over what is defined in the
|
||||
* edition
|
||||
* @param originalJson a Json object holding the original values that this
|
||||
* Config was created from, used to preserve configuration
|
||||
* keys that are not known
|
||||
@ -93,16 +86,15 @@ object Contact {
|
||||
case class Config(
|
||||
name: String,
|
||||
version: String,
|
||||
ensoVersion: EnsoVersion,
|
||||
license: String,
|
||||
authors: List[Contact],
|
||||
maintainers: List[Contact],
|
||||
dependencies: List[Dependency],
|
||||
originalJson: Json = Json.obj()
|
||||
edition: Editions.RawEdition,
|
||||
preferLocalLibraries: Boolean,
|
||||
originalJson: JsonObject = JsonObject()
|
||||
) {
|
||||
|
||||
/** Converts the configuration into a YAML representation.
|
||||
*/
|
||||
/** Converts the configuration into a YAML representation. */
|
||||
def toYaml: String =
|
||||
Printer.spaces2.copy(preserveOrder = true).pretty(Config.encoder(this))
|
||||
}
|
||||
@ -115,56 +107,107 @@ object Config {
|
||||
val license: String = "license"
|
||||
val author: String = "authors"
|
||||
val maintainer: String = "maintainers"
|
||||
val dependencies: String = "dependencies"
|
||||
val edition: String = "edition"
|
||||
val preferLocalLibraries = "prefer-local-libraries"
|
||||
}
|
||||
|
||||
implicit val decoder: Decoder[Config] = { json =>
|
||||
for {
|
||||
name <- json.get[String](JsonFields.name)
|
||||
version <- json.getOrElse[String](JsonFields.version)("dev")
|
||||
ensoVersion <-
|
||||
json.getOrElse[EnsoVersion](JsonFields.ensoVersion)(DefaultEnsoVersion)
|
||||
license <- json.getOrElse(JsonFields.license)("")
|
||||
author <- json.getOrElse[List[Contact]](JsonFields.author)(List())
|
||||
maintainer <- json.getOrElse[List[Contact]](JsonFields.maintainer)(List())
|
||||
dependencies <- json.getOrElse[List[Dependency]](JsonFields.dependencies)(
|
||||
List()
|
||||
)
|
||||
name <- json.get[String](JsonFields.name)
|
||||
version <- json.getOrElse[String](JsonFields.version)("dev")
|
||||
ensoVersion <- json.get[Option[EnsoVersion]](JsonFields.ensoVersion)
|
||||
edition <- json.get[Option[Editions.RawEdition]](JsonFields.edition)
|
||||
license <- json.getOrElse(JsonFields.license)("")
|
||||
author <- json.getOrElse[List[Contact]](JsonFields.author)(List())
|
||||
maintainer <- json.getOrElse[List[Contact]](JsonFields.maintainer)(List())
|
||||
preferLocal <-
|
||||
json.getOrElse[Boolean](JsonFields.preferLocalLibraries)(false)
|
||||
finalEdition <-
|
||||
editionOrVersionBackwardsCompatibility(edition, ensoVersion).left.map {
|
||||
error => DecodingFailure(error, json.history)
|
||||
}
|
||||
originals <- json.as[JsonObject]
|
||||
} yield Config(
|
||||
name,
|
||||
version,
|
||||
ensoVersion,
|
||||
license,
|
||||
author,
|
||||
maintainer,
|
||||
dependencies,
|
||||
json.value
|
||||
name = name,
|
||||
version = version,
|
||||
license = license,
|
||||
authors = author,
|
||||
maintainers = maintainer,
|
||||
edition = finalEdition,
|
||||
preferLocalLibraries = preferLocal,
|
||||
originalJson = originals
|
||||
)
|
||||
}
|
||||
|
||||
implicit val encoder: Encoder[Config] = { config =>
|
||||
val originals = config.originalJson
|
||||
val overrides = Json.obj(
|
||||
JsonFields.name -> config.name.asJson,
|
||||
JsonFields.version -> config.version.asJson,
|
||||
JsonFields.ensoVersion -> config.ensoVersion.asJson,
|
||||
JsonFields.license -> config.license.asJson,
|
||||
JsonFields.author -> config.authors.asJson,
|
||||
JsonFields.maintainer -> config.maintainers.asJson
|
||||
val overrides = Seq(
|
||||
JsonFields.name -> config.name.asJson,
|
||||
JsonFields.version -> config.version.asJson,
|
||||
JsonFields.edition -> config.edition.asJson,
|
||||
JsonFields.license -> config.license.asJson,
|
||||
JsonFields.author -> config.authors.asJson,
|
||||
JsonFields.maintainer -> config.maintainers.asJson
|
||||
)
|
||||
val base = originals.deepMerge(overrides)
|
||||
val withDeps =
|
||||
if (config.dependencies.nonEmpty)
|
||||
base.deepMerge(
|
||||
Json.obj(JsonFields.dependencies -> config.dependencies.asJson)
|
||||
)
|
||||
else base
|
||||
withDeps
|
||||
val preferLocalOverride =
|
||||
if (config.preferLocalLibraries)
|
||||
Seq(JsonFields.preferLocalLibraries -> true.asJson)
|
||||
else Seq()
|
||||
val overridesObject = JsonObject(
|
||||
overrides ++ preferLocalOverride: _*
|
||||
)
|
||||
originals.remove(JsonFields.ensoVersion).deepMerge(overridesObject).asJson
|
||||
}
|
||||
|
||||
/** Tries to parse the [[Config]] from a YAML string.
|
||||
*/
|
||||
/** Tries to parse the [[Config]] from a YAML string. */
|
||||
def fromYaml(yamlString: String): Try[Config] = {
|
||||
yaml.parser.parse(yamlString).flatMap(_.as[Config]).toTry
|
||||
}
|
||||
|
||||
/** Creates a simple edition that just defines the provided engine version.
|
||||
*
|
||||
* A compatibility layer for migrating from just specifying the engine
|
||||
* version to the edition system.
|
||||
*
|
||||
* TODO [RW] once the edition is actually used for loading libraries, this
|
||||
* may need to be revisited, because an edition created in this way will not
|
||||
* have any libraries present which is highly undesirable. We may either
|
||||
* remove the compatibility layer and return errors for the old format or
|
||||
* need to use the latest/default edition or some hardcoded edition for
|
||||
* compatibility.
|
||||
*/
|
||||
def makeCompatibilityEditionFromVersion(
|
||||
ensoVersion: EnsoVersion
|
||||
): Editions.RawEdition = Editions.Raw.Edition(
|
||||
parent = None,
|
||||
engineVersion = Some(ensoVersion),
|
||||
repositories = Map(),
|
||||
libraries = Map()
|
||||
)
|
||||
|
||||
/** A helper method that reconciles the old and new fields of the config
|
||||
* related to the edition.
|
||||
*
|
||||
* If an edition is present, it is just returned as-is. If the engine version
|
||||
* is specified, a special edition is created that specifies this particular
|
||||
* engine version and nothing else.
|
||||
*
|
||||
* If both fields are defined, an error is raised as the configuration may be
|
||||
* inconsistent - the `engine-version` field should only be present in old
|
||||
* configs and after migration to the edition format it should be removed.
|
||||
*/
|
||||
private def editionOrVersionBackwardsCompatibility(
|
||||
edition: Option[Editions.RawEdition],
|
||||
ensoVersion: Option[EnsoVersion]
|
||||
): Either[String, Editions.RawEdition] = (edition, ensoVersion) match {
|
||||
case (Some(_), Some(_)) =>
|
||||
Left(
|
||||
s"The deprecated `${JsonFields.ensoVersion}` should not be defined " +
|
||||
s"if the `${JsonFields.edition}` that replaces it is already defined."
|
||||
)
|
||||
case (Some(edition), _) => Right(edition)
|
||||
case _ =>
|
||||
val version = ensoVersion.getOrElse(DefaultEnsoVersion)
|
||||
Right(makeCompatibilityEditionFromVersion(version))
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package org.enso.pkg
|
||||
|
||||
import java.io.File
|
||||
|
||||
import cats.Show
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion}
|
||||
import org.enso.filesystem.FileSystem
|
||||
import org.enso.pkg.validation.NameValidation
|
||||
|
||||
import java.io.File
|
||||
import scala.io.Source
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.{Failure, Try, Using}
|
||||
|
||||
object CouldNotCreateDirectory extends Exception
|
||||
@ -202,14 +201,15 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
|
||||
authors: List[Contact] = List(),
|
||||
maintainers: List[Contact] = List()
|
||||
): Package[F] = {
|
||||
val edition = Config.makeCompatibilityEditionFromVersion(ensoVersion)
|
||||
val config = Config(
|
||||
name = NameValidation.normalizeName(name),
|
||||
version = version,
|
||||
ensoVersion = ensoVersion,
|
||||
license = "",
|
||||
authors = authors,
|
||||
maintainers = maintainers,
|
||||
dependencies = List()
|
||||
name = NameValidation.normalizeName(name),
|
||||
version = version,
|
||||
license = "",
|
||||
authors = authors,
|
||||
edition = edition,
|
||||
preferLocalLibraries = true,
|
||||
maintainers = maintainers
|
||||
)
|
||||
create(root, config)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package org.enso.pkg
|
||||
|
||||
import io.circe.Json
|
||||
import io.circe.{Json, JsonObject}
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.{DefaultEnsoVersion, SemVerEnsoVersion}
|
||||
import org.scalatest.{Inside, OptionValues}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
@ -29,21 +31,52 @@ class ConfigSpec
|
||||
|
||||
"deserialize the serialized representation to the original value" in {
|
||||
val config = Config(
|
||||
name = "placeholder",
|
||||
version = "dev",
|
||||
ensoVersion = DefaultEnsoVersion,
|
||||
license = "none",
|
||||
authors = List(),
|
||||
name = "placeholder",
|
||||
version = "dev",
|
||||
edition = Config.makeCompatibilityEditionFromVersion(
|
||||
SemVerEnsoVersion(SemVer(4, 5, 6))
|
||||
),
|
||||
license = "none",
|
||||
authors = List(),
|
||||
maintainers = List(
|
||||
Contact(Some("A"), Some("a@example.com")),
|
||||
Contact(Some("B"), None),
|
||||
Contact(None, Some("c@example.com"))
|
||||
),
|
||||
dependencies = List(Dependency("dependency", "0.0.0"))
|
||||
preferLocalLibraries = true
|
||||
)
|
||||
val deserialized = Config.fromYaml(config.toYaml).get
|
||||
val withoutJson = deserialized.copy(originalJson = Json.obj())
|
||||
val withoutJson = deserialized.copy(originalJson = JsonObject())
|
||||
withoutJson shouldEqual config
|
||||
}
|
||||
|
||||
"only require the name and use defaults for everything else" in {
|
||||
val parsed = Config.fromYaml("name: FooBar").get
|
||||
parsed.name shouldEqual "FooBar"
|
||||
parsed.edition.engineVersion should contain(DefaultEnsoVersion)
|
||||
}
|
||||
|
||||
"be backwards compatible but correctly migrate to new format on save" in {
|
||||
val oldFormat =
|
||||
"""name: FooBar
|
||||
|enso-version: 1.2.3
|
||||
|extra-key: extra-value
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(oldFormat).get
|
||||
|
||||
parsed.edition.engineVersion should contain(
|
||||
SemVerEnsoVersion(SemVer(1, 2, 3))
|
||||
)
|
||||
|
||||
val serialized = parsed.toYaml
|
||||
val parsedAgain = Config.fromYaml(serialized).get
|
||||
|
||||
parsedAgain.copy(originalJson = JsonObject()) shouldEqual
|
||||
parsed.copy(originalJson = JsonObject())
|
||||
|
||||
parsedAgain.originalJson("extra-key").flatMap(_.asString) should contain(
|
||||
"extra-value"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ package org.enso.projectmanager.infrastructure.languageserver
|
||||
import java.io.PrintWriter
|
||||
import java.lang.ProcessBuilder.Redirect
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory
|
||||
import org.enso.logger.masking.Masking
|
||||
import org.enso.loggingservice.LoggingServiceManager
|
||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner.{LanguageServerOptions, Runner}
|
||||
|
||||
import scala.util.Using
|
||||
@ -80,11 +80,16 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
|
||||
rpcPort = rpcPort,
|
||||
dataPort = dataPort
|
||||
)
|
||||
|
||||
val runner = new Runner(
|
||||
val configurationManager = new GlobalConfigurationManager(
|
||||
versionManager,
|
||||
distributionConfiguration.environment,
|
||||
descriptor.deferredLoggingServiceEndpoint
|
||||
distributionConfiguration.distributionManager
|
||||
)
|
||||
val runner = new Runner(
|
||||
runtimeVersionManager = versionManager,
|
||||
globalConfigurationManager = configurationManager,
|
||||
editionManager = distributionConfiguration.editionManager,
|
||||
environment = distributionConfiguration.environment,
|
||||
loggerConnection = descriptor.deferredLoggingServiceEndpoint
|
||||
)
|
||||
val runSettings = runner
|
||||
.startLanguageServer(
|
||||
|
@ -108,7 +108,7 @@ class ProjectFileRepository[
|
||||
name = pkg.name,
|
||||
kind = meta.kind,
|
||||
created = meta.created,
|
||||
engineVersion = pkg.config.ensoVersion,
|
||||
edition = pkg.config.edition,
|
||||
lastOpened = meta.lastOpened,
|
||||
path = Some(directory.toString),
|
||||
directoryCreationTime = directoryCreationTime
|
||||
|
@ -1,18 +1,18 @@
|
||||
package org.enso.projectmanager.model
|
||||
|
||||
import org.enso.editions.Editions
|
||||
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.util.UUID
|
||||
|
||||
import org.enso.pkg.{DefaultEnsoVersion, EnsoVersion}
|
||||
|
||||
/** Project entity.
|
||||
*
|
||||
* @param id a project id
|
||||
* @param name a project name
|
||||
* @param kind a project kind
|
||||
* @param created a project creation time
|
||||
* @param engineVersion version of the engine associated with the project
|
||||
* @param edition the edition configuration associated with the project
|
||||
* @param lastOpened a project last open time
|
||||
* @param path a path to the project structure
|
||||
*/
|
||||
@ -21,7 +21,7 @@ case class Project(
|
||||
name: String,
|
||||
kind: ProjectKind,
|
||||
created: OffsetDateTime,
|
||||
engineVersion: EnsoVersion = DefaultEnsoVersion,
|
||||
edition: Editions.RawEdition,
|
||||
lastOpened: Option[OffsetDateTime] = None,
|
||||
path: Option[String] = None,
|
||||
directoryCreationTime: Option[FileTime] = None
|
||||
|
@ -8,7 +8,7 @@ import org.enso.projectmanager.protocol.ProjectManagementApi._
|
||||
*
|
||||
* Do not remove this import.
|
||||
*/
|
||||
import org.enso.pkg.SemVerJson._
|
||||
import org.enso.editions.SemVerJson._
|
||||
|
||||
object JsonRpc {
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
package org.enso.projectmanager.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import io.circe.Json
|
||||
import io.circe.syntax._
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.EnsoVersion
|
||||
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||
import org.enso.pkg.EnsoVersion
|
||||
import org.enso.projectmanager.data.{
|
||||
EngineVersion,
|
||||
MissingComponentAction,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.enso.projectmanager.requesthandler
|
||||
|
||||
import akka.actor._
|
||||
import org.enso.pkg.DefaultEnsoVersion
|
||||
import org.enso.editions.DefaultEnsoVersion
|
||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||
import org.enso.projectmanager.control.core.syntax._
|
||||
import org.enso.projectmanager.control.effect.syntax._
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.projectmanager.service
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||
@ -12,8 +10,11 @@ import org.enso.projectmanager.service.ProjectServiceFailure.ProjectCreateFailed
|
||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerErrorRecoverySyntax._
|
||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||
import org.enso.runtimeversionmanager.config.GlobalConfigurationManager
|
||||
import org.enso.runtimeversionmanager.runner.Runner
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
/** A service for creating new project structures using the runner of the
|
||||
* specific engine version selected for the project.
|
||||
*/
|
||||
@ -36,11 +37,17 @@ class ProjectCreationService[
|
||||
val versionManager = RuntimeVersionManagerFactory(
|
||||
distributionConfiguration
|
||||
).makeRuntimeVersionManager(progressTracker, missingComponentAction)
|
||||
val configurationManager = new GlobalConfigurationManager(
|
||||
versionManager,
|
||||
distributionConfiguration.distributionManager
|
||||
)
|
||||
val runner =
|
||||
new Runner(
|
||||
versionManager,
|
||||
distributionConfiguration.environment,
|
||||
loggingServiceDescriptor.getEndpoint
|
||||
runtimeVersionManager = versionManager,
|
||||
globalConfigurationManager = configurationManager,
|
||||
editionManager = distributionConfiguration.editionManager,
|
||||
environment = distributionConfiguration.environment,
|
||||
loggerConnection = loggingServiceDescriptor.getEndpoint
|
||||
)
|
||||
|
||||
val settings =
|
||||
|
@ -1,16 +1,16 @@
|
||||
package org.enso.projectmanager.service
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import cats.MonadError
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion}
|
||||
import org.enso.pkg.Config
|
||||
import org.enso.projectmanager.control.core.syntax._
|
||||
import org.enso.projectmanager.control.core.{
|
||||
Applicative,
|
||||
CovariantFlatMap,
|
||||
Traverse
|
||||
}
|
||||
import org.enso.projectmanager.control.core.syntax._
|
||||
import org.enso.projectmanager.control.effect.syntax._
|
||||
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
||||
import org.enso.projectmanager.data.{
|
||||
@ -48,6 +48,8 @@ import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerEr
|
||||
import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory
|
||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/** Implementation of business logic for project management.
|
||||
*
|
||||
* @param validator a project validator
|
||||
@ -87,7 +89,16 @@ class ProjectService[
|
||||
_ <- validateName(name)
|
||||
_ <- checkIfNameExists(name)
|
||||
creationTime <- clock.nowInUtc()
|
||||
project = Project(projectId, name, UserProject, creationTime)
|
||||
defaultEdition = Config.makeCompatibilityEditionFromVersion(
|
||||
DefaultEnsoVersion
|
||||
)
|
||||
project = Project(
|
||||
id = projectId,
|
||||
name = name,
|
||||
kind = UserProject,
|
||||
created = creationTime,
|
||||
edition = defaultEdition
|
||||
)
|
||||
path <- repo.findPathForNewProject(project).mapError(toServiceFailure)
|
||||
_ <- log.debug(
|
||||
"Found a path [{}] for a new project [{}, {}].",
|
||||
@ -275,7 +286,7 @@ class ProjectService[
|
||||
}
|
||||
.mapRuntimeManagerErrors(th =>
|
||||
ProjectOpenFailed(
|
||||
s"Cannot install the required engine. ${th.getMessage}"
|
||||
s"Cannot install the required engine. $th"
|
||||
)
|
||||
)
|
||||
|
||||
@ -285,8 +296,9 @@ class ProjectService[
|
||||
project: Project,
|
||||
missingComponentAction: MissingComponentAction
|
||||
): F[ProjectServiceFailure, RunningLanguageServerInfo] = for {
|
||||
version <- resolveProjectVersion(project)
|
||||
version <- configurationService
|
||||
.resolveEnsoVersion(project.engineVersion)
|
||||
.resolveEnsoVersion(version)
|
||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
||||
ProjectOpenFailed(
|
||||
"Could not deduce the default version to use for the project: " +
|
||||
@ -345,15 +357,17 @@ class ProjectService[
|
||||
private def resolveProjectMetadata(
|
||||
project: Project
|
||||
): F[ProjectServiceFailure, ProjectMetadata] =
|
||||
configurationService
|
||||
.resolveEnsoVersion(project.engineVersion)
|
||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
||||
GlobalConfigurationAccessFailure(
|
||||
"Could not deduce the default version to use for the project: " +
|
||||
message
|
||||
)
|
||||
}
|
||||
.map(toProjectMetadata(_, project))
|
||||
for {
|
||||
version <- resolveProjectVersion(project)
|
||||
version <- configurationService
|
||||
.resolveEnsoVersion(version)
|
||||
.mapError { case ConfigurationFileAccessFailure(message) =>
|
||||
GlobalConfigurationAccessFailure(
|
||||
"Could not deduce the default version to use for the project: " +
|
||||
message
|
||||
)
|
||||
}
|
||||
} yield toProjectMetadata(version, project)
|
||||
|
||||
private def toProjectMetadata(
|
||||
engineVersion: SemVer,
|
||||
@ -439,4 +453,19 @@ class ProjectService[
|
||||
log.debug("Project name [{}] validated by [{}].", name, validator)
|
||||
}
|
||||
|
||||
private def resolveProjectVersion(
|
||||
project: Project
|
||||
): F[ProjectServiceFailure, EnsoVersion] =
|
||||
Sync[F]
|
||||
.blockingOp {
|
||||
distributionConfiguration.editionManager
|
||||
.resolveEngineVersion(project.edition)
|
||||
.get
|
||||
}
|
||||
.mapError { error =>
|
||||
ProjectServiceFailure.GlobalConfigurationAccessFailure(
|
||||
s"Could not resolve project engine version: ${error.getMessage}"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package org.enso.projectmanager.service.config
|
||||
|
||||
import io.circe.Json
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.pkg.{DefaultEnsoVersion, EnsoVersion, SemVerEnsoVersion}
|
||||
import org.enso.editions.{DefaultEnsoVersion, EnsoVersion, SemVerEnsoVersion}
|
||||
import org.enso.projectmanager.control.core.CovariantFlatMap
|
||||
import org.enso.projectmanager.control.effect.{ErrorChannel, Sync}
|
||||
import org.enso.projectmanager.service.config.GlobalConfigServiceFailure.ConfigurationFileAccessFailure
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.enso.projectmanager.service.config
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.pkg.EnsoVersion
|
||||
import org.enso.editions.EnsoVersion
|
||||
|
||||
/** A contract for the Global Config Service.
|
||||
*
|
||||
|
@ -1,17 +1,16 @@
|
||||
package org.enso.projectmanager.service.versionmanagement
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import com.typesafe.scalalogging.Logger
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.cli.task.{ProgressListener, TaskProgress}
|
||||
import org.enso.distribution.locking.Resource
|
||||
import org.enso.projectmanager.data.ProgressUnit
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
GraalVMVersion,
|
||||
RuntimeVersionManagementUserInterface
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.Resource
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
|
@ -2,11 +2,11 @@ package org.enso.projectmanager.service.versionmanagement
|
||||
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.cli.task.TaskProgress
|
||||
import org.enso.distribution.locking.Resource
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
GraalVMVersion,
|
||||
RuntimeVersionManagementUserInterface
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.Resource
|
||||
|
||||
/** A simple [[RuntimeVersionManagementUserInterface]] that does not allow to
|
||||
* install any versions and does not track any progress.
|
||||
|
@ -1,7 +1,11 @@
|
||||
package org.enso.projectmanager.versionmanagement
|
||||
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.runtimeversionmanager.Environment
|
||||
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||
import org.enso.distribution.locking.{
|
||||
ResourceManager,
|
||||
ThreadSafeFileLockManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
GraalVMComponentConfiguration,
|
||||
InstallerKind,
|
||||
@ -10,14 +14,7 @@ import org.enso.runtimeversionmanager.components.{
|
||||
RuntimeVersionManagementUserInterface,
|
||||
RuntimeVersionManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.{
|
||||
DistributionManager,
|
||||
TemporaryDirectoryManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.{
|
||||
ResourceManager,
|
||||
ThreadSafeFileLockManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||
import org.enso.runtimeversionmanager.releases.engine.{
|
||||
EngineRelease,
|
||||
@ -51,6 +48,9 @@ object DefaultDistributionConfiguration
|
||||
/** @inheritdoc */
|
||||
lazy val resourceManager = new ResourceManager(lockManager)
|
||||
|
||||
/** @inheritdoc */
|
||||
lazy val editionManager = EditionManager(distributionManager)
|
||||
|
||||
/** @inheritdoc */
|
||||
lazy val temporaryDirectoryManager =
|
||||
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
||||
|
@ -1,15 +1,12 @@
|
||||
package org.enso.projectmanager.versionmanagement
|
||||
|
||||
import org.enso.runtimeversionmanager.Environment
|
||||
import org.enso.distribution.locking.ResourceManager
|
||||
import org.enso.distribution.{DistributionManager, EditionManager, Environment}
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
RuntimeVersionManagementUserInterface,
|
||||
RuntimeVersionManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.{
|
||||
DistributionManager,
|
||||
TemporaryDirectoryManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
||||
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||
import org.enso.runtimeversionmanager.releases.ReleaseProvider
|
||||
import org.enso.runtimeversionmanager.releases.engine.EngineRelease
|
||||
import org.enso.runtimeversionmanager.runner.JVMSettings
|
||||
@ -31,6 +28,9 @@ trait DistributionConfiguration {
|
||||
/** A [[ResourceManager]] instance. */
|
||||
def resourceManager: ResourceManager
|
||||
|
||||
/** An [[EditionManager]] instance. */
|
||||
def editionManager: EditionManager
|
||||
|
||||
/** A [[TemporaryDirectoryManager]] instance. */
|
||||
def temporaryDirectoryManager: TemporaryDirectoryManager
|
||||
|
||||
|
@ -4,13 +4,13 @@ import java.io.File
|
||||
import java.nio.file.{Files, Path}
|
||||
import java.time.{OffsetDateTime, ZoneOffset}
|
||||
import java.util.UUID
|
||||
|
||||
import akka.testkit.TestActors.blackholeProps
|
||||
import akka.testkit._
|
||||
import io.circe.Json
|
||||
import io.circe.parser.parse
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.enso.distribution.OS
|
||||
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
||||
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
||||
import org.enso.loggingservice.printers.StderrPrinterWithColors
|
||||
@ -39,7 +39,6 @@ import org.enso.projectmanager.service.{
|
||||
ProjectService
|
||||
}
|
||||
import org.enso.projectmanager.test.{ObservableGenerator, ProgrammableClock}
|
||||
import org.enso.runtimeversionmanager.OS
|
||||
import org.enso.runtimeversionmanager.test.FakeReleases
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import pureconfig.ConfigSource
|
||||
|
@ -5,7 +5,7 @@ import akka.testkit.TestDuration
|
||||
import io.circe.Json
|
||||
import io.circe.syntax._
|
||||
import io.circe.literal._
|
||||
import org.enso.pkg.SemVerJson._
|
||||
import org.enso.editions.SemVerJson._
|
||||
import io.circe.parser.parse
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.projectmanager.data.{MissingComponentAction, Socket}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package org.enso.projectmanager
|
||||
|
||||
import java.nio.file.Path
|
||||
import org.enso.distribution.{DistributionManager, EditionManager}
|
||||
import org.enso.distribution.locking.ResourceManager
|
||||
|
||||
import java.nio.file.Path
|
||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||
import org.enso.runtimeversionmanager.components.{
|
||||
GraalVMComponentConfiguration,
|
||||
@ -9,11 +11,7 @@ import org.enso.runtimeversionmanager.components.{
|
||||
RuntimeVersionManagementUserInterface,
|
||||
RuntimeVersionManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.distribution.{
|
||||
DistributionManager,
|
||||
TemporaryDirectoryManager
|
||||
}
|
||||
import org.enso.runtimeversionmanager.locking.ResourceManager
|
||||
import org.enso.runtimeversionmanager.distribution.TemporaryDirectoryManager
|
||||
import org.enso.runtimeversionmanager.releases.engine.{
|
||||
EngineRelease,
|
||||
EngineReleaseProvider
|
||||
@ -66,6 +64,8 @@ class TestDistributionConfiguration(
|
||||
|
||||
lazy val resourceManager = new ResourceManager(lockManager)
|
||||
|
||||
lazy val editionManager: EditionManager = EditionManager(distributionManager)
|
||||
|
||||
lazy val temporaryDirectoryManager =
|
||||
new TemporaryDirectoryManager(distributionManager, resourceManager)
|
||||
|
||||
|
@ -6,7 +6,7 @@ import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.projectmanager.data.MissingComponentAction
|
||||
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
||||
import org.enso.testkit.RetrySpec
|
||||
import org.enso.pkg.SemVerJson._
|
||||
import org.enso.editions.SemVerJson._
|
||||
|
||||
class ProjectCreateMissingComponentsSpec
|
||||
extends BaseServerSpec
|
||||
|
@ -7,7 +7,7 @@ import java.util.UUID
|
||||
import io.circe.literal._
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.enso.pkg.SemVerJson._
|
||||
import org.enso.editions.SemVerJson._
|
||||
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
|
||||
import org.enso.testkit.{FlakySpec, RetrySpec}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user