Use Runner to Create Projects in Launcher (#1116)

This commit is contained in:
Radosław Waśko 2020-09-01 13:23:27 +02:00 committed by GitHub
parent eb208301db
commit 60d0c2ae45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 278 additions and 151 deletions

View File

@ -45,15 +45,15 @@ This document describes available command-line options of the Enso launcher.
### `new`
Create a new, empty project in a specified directory. By default uses the
`default` enso version, which can be overriden with `--version`.
`default` enso version, which can be overriden with `--use-enso-version`.
Examples:
```bash
> enso new project1 --path /somewhere/on/the/filesystem
# creates project called project1 in the specified directory
# creates project called Project1 in the specified directory
# using the `default` Enso version
> enso new project2 --version 2.0.1
> enso new project2 --use-enso-version 2.0.1
# creates the project in the current directory, using the 2.0.1 version
```
@ -83,7 +83,7 @@ Installs a portable Enso distribution into system-defined directories, as
explained in
[Installed Enso Distribution Layout](./distribution.md#installed-enso-distribution-layout).
By default, it asks the user for confirmation, but this can be skipped by adding
a `--yes` flag.
the `--auto-confirm` flag.
Examples:
@ -101,7 +101,8 @@ Uninstalls an installed Enso distribution from the installation location
described in
[Installed Enso Distribution Layout](./distribution.md#installed-enso-distribution-layout).
It removes the universal launcher and all components. By default, it asks the
user for confirmation, but this can be skipped by adding a `--yes` flag.
user for confirmation, but this can be skipped by adding a `--auto-confirm`
flag.
Examples:
@ -153,18 +154,18 @@ default version is 2.0.1
### `config`
Can be used to manage project configuration or global user configuration (if
outside a project or with the `--global` flag).
Can be used to manage global user configuration.
If only the config path is provided, currently configured value is printed.
If only the config path is provided, currently configured value is printed. If
the provided value is an empty string, the given key is removed from the config.
Examples:
```bash
> enso config --global author.name Example User
> enso config author.name Example User
> enso config author.name
Example User
> enso config author.name Example User # Sets `author.name`.
> enso config author.email # Prints current value.
user@example.com
> enso config author.name "" # Removes the value from the config.
```
### `run`
@ -249,7 +250,7 @@ Print this summary of available command and their usage.
## General Options
### `--version`
### `--use-enso-version`
Overrides the inferred (project local or `default`) version when running a
command.

View File

@ -47,9 +47,42 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
* values are used to set a default author and maintainer for the created
* project.
*/
def newProject(name: String, path: Option[Path]): Unit = {
def newProject(
name: String,
path: Option[Path],
versionOverride: Option[SemVer],
useSystemJVM: Boolean,
jvmOpts: Seq[(String, String)],
additionalArguments: Seq[String]
): Unit = {
val actualPath = path.getOrElse(Launcher.workingDirectory.resolve(name))
projectManager.newProject(name, actualPath)
val version =
versionOverride.getOrElse(configurationManager.defaultVersion)
val globalConfig = configurationManager.getConfig
val exitCode = runner
.createCommand(
runner
.newProject(
path = actualPath,
name = name,
version = version,
authorName = globalConfig.authorName,
authorEmail = globalConfig.authorEmail,
additionalArguments = additionalArguments
)
.get,
JVMSettings(useSystemJVM, jvmOpts)
)
.run()
.get
if (exitCode == 0) {
Logger.info(s"Project created in `$actualPath` using version $version.")
} else {
Logger.error("Project creation failed.")
sys.exit(exitCode)
}
}
/**

View File

@ -54,15 +54,38 @@ object Main {
private def newCommand: Command[Config => Unit] =
Command("new", "Create a new Enso project.", related = Seq("create")) {
val nameOpt = Opts.positionalArgument[String]("NAME", "Project name.")
val nameOpt = Opts.positionalArgument[String]("PROJECT-NAME")
val pathOpt = Opts.optionalArgument[Path](
"PATH",
"Path where to create the project. If not specified, a directory " +
"called NAME is created in the current directory."
"PATH specifies where to create the project. If it is not specified, " +
"a directory called PROJECT-NAME is created in the current directory."
)
val additionalArgs = Opts.additionalArguments()
(nameOpt, pathOpt) mapN { (name, path) => (config: Config) =>
Launcher(config).newProject(name, path)
(
nameOpt,
pathOpt,
versionOverride,
systemJVMOverride,
jvmOpts,
additionalArgs
) mapN {
(
name,
path,
versionOverride,
systemJVMOverride,
jvmOpts,
additionalArgs
) => (config: Config) =>
Launcher(config).newProject(
name = name,
path = path,
versionOverride = versionOverride,
useSystemJVM = systemJVMOverride,
jvmOpts = jvmOpts,
additionalArguments = additionalArgs
)
}
}
@ -83,7 +106,7 @@ object Main {
Opts.optionalParameter[SemVer](
"use-enso-version",
"VERSION",
"Overrides the Enso version that would normally be used"
"Override the Enso version that would normally be used."
)
private def runCommand: Command[Config => Unit] =
@ -241,14 +264,16 @@ object Main {
private def upgradeCommand: Command[Config => Unit] =
Command("upgrade", "Upgrade the launcher.") {
val version = Opts.optionalArgument[String](
"version",
"Version to upgrade to. " +
"VERSION",
"VERSION specifies which launcher version to upgrade to. " +
"If not provided, defaults to latest version."
)
version map { version => (_: Config) =>
version match {
case Some(version) => println(s"Upgrade launcher to $version")
case None => println("Upgrade launcher to latest version")
case Some(version) =>
println(s"(Not implemented) Upgrade launcher to $version.")
case None =>
println("(Not implemented) Upgrade launcher to latest version.")
}
}
}
@ -256,14 +281,10 @@ object Main {
private def installEngineCommand: Subcommand[Config => Unit] =
Subcommand(
"engine",
"Installs the specified engine version, defaulting to the latest if " +
"Installs the specified engine VERSION, defaulting to the latest if " +
"unspecified."
) {
val version = Opts.optionalArgument[SemVer](
"VERSION",
"VERSION specifies the engine version to install. If not provided, the" +
"latest version is installed."
)
val version = Opts.optionalArgument[SemVer]("VERSION")
version map { version => (config: Config) =>
version match {
case Some(value) =>

View File

@ -30,6 +30,29 @@ class Runner(
*/
protected val currentWorkingDirectory: Path = Path.of(".")
def newProject(
path: Path,
name: String,
version: SemVer,
authorName: Option[String],
authorEmail: Option[String],
additionalArguments: Seq[String]
): Try[RunSettings] =
Try {
val authorNameOption =
authorName.map(Seq("--new-project-author-name", _)).getOrElse(Seq())
val authorEmailOption =
authorEmail.map(Seq("--new-project-author-email", _)).getOrElse(Seq())
val arguments =
Seq(
"--new",
path.toAbsolutePath.normalize.toString,
"--new-project-name",
name
) ++ authorNameOption ++ authorEmailOption ++ additionalArguments
RunSettings(version, arguments)
}
/**
* Creates [[RunSettings]] for launching the REPL.
*

View File

@ -2,10 +2,8 @@ package org.enso.launcher.project
import java.nio.file.Path
import nl.gn0s1s.bump.SemVer
import org.enso.launcher.Logger
import org.enso.launcher.config.GlobalConfigurationManager
import org.enso.pkg.{PackageManager, SemVerEnsoVersion}
import org.enso.pkg.PackageManager
import scala.util.{Failure, Try}
@ -19,41 +17,6 @@ class ProjectManager(globalConfigurationManager: GlobalConfigurationManager) {
private val packageManager = PackageManager.Default
/**
* Creates a new project at the specified path with the given name.
*
* If the version is not provided, the default Enso engine version is used.
* If `author.name` or `author.email` are set in the global config, their
* values are used to set a default author and maintainer for the created
* project.
*
* @param name specifies the name of the project
* @param path specifies where the project should be created
* @param ensoVersion if provided, specifies an exact Enso version that the
* project should be associated with
*/
def newProject(
name: String,
path: Path,
ensoVersion: Option[SemVer] = None
): Unit = {
val globalConfig = globalConfigurationManager.getConfig
val pkg = packageManager.create(
root = path.toFile,
name = name,
ensoVersion = SemVerEnsoVersion(
ensoVersion.getOrElse(globalConfigurationManager.defaultVersion)
)
)
globalConfig.defaultAuthor.foreach { contact =>
pkg.updateConfig(config =>
config.copy(authors = List(contact), maintainers = List(contact))
)
}
Logger.info(s"Project created in `$path`.")
}
/**
* Tries to load the project at the provided `path`.
*/

View File

@ -2,6 +2,7 @@ package org.enso.launcher.components
import java.nio.file.Path
import nl.gn0s1s.bump.SemVer
import org.enso.launcher.cli.GlobalCLIOptions
import org.enso.launcher.installation.DistributionManager
import org.enso.launcher.releases.{
@ -9,6 +10,7 @@ import org.enso.launcher.releases.{
GraalCEReleaseProvider
}
import org.enso.launcher.{Environment, FakeEnvironment, WithTemporaryDirectory}
import org.enso.pkg.{PackageManager, SemVerEnsoVersion}
import org.scalatest.OptionValues
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
@ -66,4 +68,15 @@ class ComponentsManagerTest
* See [[makeManagers]] for details.
*/
def makeComponentsManager(): ComponentsManager = makeManagers()._2
/**
* Creates a new project using the default package manager.
*/
def newProject(name: String, path: Path, version: SemVer): Unit = {
PackageManager.Default.create(
root = path.toFile,
name = name,
ensoVersion = SemVerEnsoVersion(version)
)
}
}

View File

@ -12,11 +12,11 @@ import org.enso.launcher.project.ProjectManager
class RunnerSpec extends ComponentsManagerTest {
private val defaultEngineVersion = SemVer(0, 0, 0, Some("default"))
case class TestSetup(runner: Runner, projectManager: ProjectManager)
def makeFakeRunner(
cwdOverride: Option[Path] = None,
extraEnv: Map[String, String] = Map.empty
): TestSetup = {
): Runner = {
val (distributionManager, componentsManager, env) = makeManagers(extraEnv)
val configurationManager =
new GlobalConfigurationManager(componentsManager, distributionManager) {
@ -28,14 +28,14 @@ class RunnerSpec extends ComponentsManagerTest {
new Runner(projectManager, configurationManager, componentsManager, env) {
override protected val currentWorkingDirectory: Path = cwd
}
TestSetup(runner, projectManager)
runner
}
"Runner" should {
"create a command from settings" in {
Logger.suppressWarnings {
val envOptions = "-Xfrom-env -Denv=env"
val TestSetup(runner, _) =
val runner =
makeFakeRunner(extraEnv = Map("ENSO_JVM_OPTS" -> envOptions))
val runSettings = RunSettings(SemVer(0, 0, 0), Seq("arg1", "--flag2"))
@ -84,8 +84,37 @@ class RunnerSpec extends ComponentsManagerTest {
}
}
"create project with name, default author (if specified) and additional " +
"arguments" in {
val runner = makeFakeRunner()
val projectPath = getTestDirectory / "project"
val authorName = "Author Name"
val authorEmail = "author@example.com"
val additionalArgument = "additional arg"
val runSettings = runner
.newProject(
path = projectPath,
name = "ProjectName",
version = defaultEngineVersion,
authorName = Some(authorName),
authorEmail = Some(authorEmail),
additionalArguments = Seq(additionalArgument)
)
.get
runSettings.version shouldEqual defaultEngineVersion
runSettings.runnerArguments should contain(additionalArgument)
val commandLine = runSettings.runnerArguments.mkString(" ")
commandLine should include(
s"--new ${projectPath.toAbsolutePath.normalize}"
)
commandLine should include("--new-project-name ProjectName")
commandLine should include(s"--new-project-author-name $authorName")
commandLine should include(s"--new-project-author-email $authorEmail")
}
"run repl with default version and additional arguments" in {
val TestSetup(runner, _) = makeFakeRunner()
val runner = makeFakeRunner()
val runSettings = runner
.repl(
projectPath = None,
@ -101,18 +130,14 @@ class RunnerSpec extends ComponentsManagerTest {
}
"run repl in project context" in {
val TestSetup(runnerOutside, projectManager) = makeFakeRunner()
val runnerOutside = makeFakeRunner()
val version = SemVer(0, 0, 0, Some("repl-test"))
version should not equal defaultEngineVersion // sanity check
val projectPath = getTestDirectory / "project"
val normalizedPath = projectPath.toAbsolutePath.normalize.toString
projectManager.newProject(
"test",
projectPath,
Some(version)
)
newProject("test", projectPath, version)
val outsideProject = runnerOutside
.repl(
@ -126,7 +151,7 @@ class RunnerSpec extends ComponentsManagerTest {
outsideProject.runnerArguments.mkString(" ") should
(include(s"--in-project $normalizedPath") and include("--repl"))
val TestSetup(runnerInside, _) = makeFakeRunner(Some(projectPath))
val runnerInside = makeFakeRunner(Some(projectPath))
val insideProject = runnerInside
.repl(
projectPath = None,
@ -154,15 +179,11 @@ class RunnerSpec extends ComponentsManagerTest {
}
"run language server" in {
val TestSetup(runner, projectManager) = makeFakeRunner()
val runner = makeFakeRunner()
val version = SemVer(0, 0, 0, Some("language-server-test"))
val projectPath = getTestDirectory / "project"
projectManager.newProject(
"test",
projectPath,
Some(version)
)
newProject("test", projectPath, version)
val options = LanguageServerOptions(
rootId = UUID.randomUUID(),
@ -201,16 +222,12 @@ class RunnerSpec extends ComponentsManagerTest {
}
"run a project" in {
val TestSetup(runnerOutside, projectManager) = makeFakeRunner()
val runnerOutside = makeFakeRunner()
val version = SemVer(0, 0, 0, Some("run-test"))
val projectPath = getTestDirectory / "project"
val normalizedPath = projectPath.toAbsolutePath.normalize.toString
projectManager.newProject(
"test",
projectPath,
Some(version)
)
newProject("test", projectPath, version)
val outsideProject = runnerOutside
.run(
@ -224,7 +241,7 @@ class RunnerSpec extends ComponentsManagerTest {
outsideProject.runnerArguments.mkString(" ") should
include(s"--run $normalizedPath")
val TestSetup(runnerInside, _) = makeFakeRunner(Some(projectPath))
val runnerInside = makeFakeRunner(Some(projectPath))
val insideProject = runnerInside
.run(
path = None,
@ -265,13 +282,9 @@ class RunnerSpec extends ComponentsManagerTest {
"run a script outside of a project even if cwd is inside project" in {
val version = SemVer(0, 0, 0, Some("run-test"))
val projectPath = getTestDirectory / "project"
val TestSetup(runnerInside, projectManager) =
val runnerInside =
makeFakeRunner(cwdOverride = Some(projectPath))
projectManager.newProject(
"test",
projectPath,
Some(version)
)
newProject("test", projectPath, version)
val outsideFile = getTestDirectory / "Main.enso"
val normalizedPath = outsideFile.toAbsolutePath.normalize.toString
@ -294,15 +307,11 @@ class RunnerSpec extends ComponentsManagerTest {
}
"run a script inside of a project" in {
val version = SemVer(0, 0, 0, Some("run-test"))
val projectPath = getTestDirectory / "project"
val normalizedProjectPath = projectPath.toAbsolutePath.normalize.toString
val TestSetup(runnerOutside, projectManager) = makeFakeRunner()
projectManager.newProject(
"test",
projectPath,
Some(version)
)
val version = SemVer(0, 0, 0, Some("run-test"))
val projectPath = getTestDirectory / "project"
val normalizedProjectPath = projectPath.toAbsolutePath.normalize.toString
val runnerOutside = makeFakeRunner()
newProject("test", projectPath, version)
val insideFile = projectPath / "src" / "Main.enso"
val normalizedFilePath = insideFile.toAbsolutePath.normalize.toString
@ -322,7 +331,7 @@ class RunnerSpec extends ComponentsManagerTest {
}
"get default version outside of project" in {
val TestSetup(runner, _) = makeFakeRunner()
val runner = makeFakeRunner()
val (runSettings, whichEngine) = runner
.version(useJSON = true)
.get
@ -335,16 +344,11 @@ class RunnerSpec extends ComponentsManagerTest {
}
"get project version inside of project" in {
val version = SemVer(0, 0, 0, Some("version-test"))
val projectPath = getTestDirectory / "project"
val name = "Testname"
val TestSetup(runnerInside, projectManager) =
makeFakeRunner(cwdOverride = Some(projectPath))
projectManager.newProject(
name,
projectPath,
Some(version)
)
val version = SemVer(0, 0, 0, Some("version-test"))
val projectPath = getTestDirectory / "project"
val name = "Testname"
val runnerInside = makeFakeRunner(cwdOverride = Some(projectPath))
newProject(name, projectPath, version)
val (runSettings, whichEngine) = runnerInside
.version(useJSON = false)
.get

View File

@ -1,19 +1,14 @@
package org.enso.launcher.project
import nl.gn0s1s.bump.SemVer
import org.enso.launcher.{FakeEnvironment, WithTemporaryDirectory}
import org.enso.launcher.components.ComponentsManagerTest
import org.enso.launcher.config.GlobalConfigurationManager
import org.enso.launcher.installation.DistributionManager
import org.enso.pkg.Contact
import org.scalatest.{Inside, OptionValues}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ProjectManagerSpec
extends AnyWordSpec
with Matchers
with WithTemporaryDirectory
with FakeEnvironment
extends ComponentsManagerTest
with Inside
with OptionValues {
private val defaultEnsoVersion = SemVer(0, 0, 0, Some("default"))
@ -28,7 +23,7 @@ class ProjectManagerSpec
}
"ProjectManager" should {
"create a new project with correct structure" in {
"create a new project with correct structure" ignore {
val (configManager, projectManager) = makeProjectManager()
val author = Contact(Some("author"), Some("a@example.com"))
@ -40,7 +35,9 @@ class ProjectManagerSpec
)
val projectDir = getTestDirectory.resolve("proj1")
projectManager.newProject("Test Project", projectDir)
// TODO [RW] create new project using runner.jar (to be added with #1046)
// this test may then be moved to the `runner` package
projectDir.toFile should exist
projectDir.resolve("src").resolve("Main.enso").toFile should exist
@ -54,7 +51,7 @@ class ProjectManagerSpec
"find projects in parent directories" in {
val (_, projectManager) = makeProjectManager()
val projectDir = getTestDirectory.resolve("proj1")
projectManager.newProject("Test Project", projectDir)
newProject("Test Project", projectDir, defaultEnsoVersion)
projectManager.findProject(projectDir).get should be(defined)
projectManager.findProject(projectDir.resolve("src")).get should

View File

@ -4,10 +4,11 @@ import java.io.File
import java.util.UUID
import cats.implicits._
import nl.gn0s1s.bump.SemVer
import org.apache.commons.cli.{Option => CliOption, _}
import org.enso.languageserver.boot
import org.enso.languageserver.boot.LanguageServerConfig
import org.enso.pkg.PackageManager
import org.enso.pkg.{Contact, PackageManager, SemVerEnsoVersion}
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
import org.enso.version.VersionDescription
import org.graalvm.polyglot.{PolyglotException, Value}
@ -18,19 +19,22 @@ import scala.util.Try
/** The main CLI entry point class. */
object Main {
private val RUN_OPTION = "run"
private val HELP_OPTION = "help"
private val NEW_OPTION = "new"
private val REPL_OPTION = "repl"
private val LANGUAGE_SERVER_OPTION = "server"
private val INTERFACE_OPTION = "interface"
private val RPC_PORT_OPTION = "rpc-port"
private val DATA_PORT_OPTION = "data-port"
private val ROOT_ID_OPTION = "root-id"
private val ROOT_PATH_OPTION = "path"
private val IN_PROJECT_OPTION = "in-project"
private val VERSION_OPTION = "version"
private val JSON_OPTION = "json"
private val RUN_OPTION = "run"
private val HELP_OPTION = "help"
private val NEW_OPTION = "new"
private val PROJECT_NAME_OPTION = "new-project-name"
private val PROJECT_AUTHOR_NAME_OPTION = "new-project-author-name"
private val PROJECT_AUTHOR_EMAIL_OPTION = "new-project-author-email"
private val REPL_OPTION = "repl"
private val LANGUAGE_SERVER_OPTION = "server"
private val INTERFACE_OPTION = "interface"
private val RPC_PORT_OPTION = "rpc-port"
private val DATA_PORT_OPTION = "data-port"
private val ROOT_ID_OPTION = "root-id"
private val ROOT_PATH_OPTION = "path"
private val IN_PROJECT_OPTION = "in-project"
private val VERSION_OPTION = "version"
private val JSON_OPTION = "json"
/**
* Builds the [[Options]] object representing the CLI syntax.
@ -61,6 +65,35 @@ object Main {
.longOpt(NEW_OPTION)
.desc("Creates a new Enso project.")
.build
val newProjectNameOpt = CliOption.builder
.hasArg(true)
.numberOfArgs(1)
.argName("name")
.longOpt(PROJECT_NAME_OPTION)
.desc(
s"Specifies a project name when creating a project using --$NEW_OPTION."
)
.build
val newProjectAuthorNameOpt = CliOption.builder
.hasArg(true)
.numberOfArgs(1)
.argName("name")
.longOpt(PROJECT_AUTHOR_NAME_OPTION)
.desc(
s"Specifies the name of the author and maintainer of the project " +
s"created using --$NEW_OPTION."
)
.build
val newProjectAuthorEmailOpt = CliOption.builder
.hasArg(true)
.numberOfArgs(1)
.argName("email")
.longOpt(PROJECT_AUTHOR_EMAIL_OPTION)
.desc(
s"Specifies the email of the author and maintainer of the project " +
s"created using --$NEW_OPTION."
)
.build
val lsOption = CliOption.builder
.longOpt(LANGUAGE_SERVER_OPTION)
.desc("Runs Language Server")
@ -125,6 +158,9 @@ object Main {
.addOption(repl)
.addOption(run)
.addOption(newOpt)
.addOption(newProjectNameOpt)
.addOption(newProjectAuthorNameOpt)
.addOption(newProjectAuthorEmailOpt)
.addOption(lsOption)
.addOption(interfaceOption)
.addOption(rpcPortOption)
@ -155,10 +191,39 @@ object Main {
/**
* Handles the `--new` CLI option.
*
* Creates a project at the provided path. If the nameOption is provided it
* specifies the project name, otherwise the name is generated automatically.
* The Enso version used in the project is set to the version of this runner.
*
* @param path root path of the newly created project
* @param nameOption specifies the name of the created project
* @param authorName if set, sets the name of the author and maintainer
* @param authorEmail if set, sets the email of the author and maintainer
*/
private def createNew(path: String): Unit = {
PackageManager.Default.getOrCreate(new File(path))
private def createNew(
path: String,
nameOption: Option[String],
authorName: Option[String],
authorEmail: Option[String]
): Unit = {
val root = new File(path)
val name = nameOption.getOrElse(PackageManager.Default.generateName(root))
val currentVersion = SemVer(buildinfo.Info.ensoVersion).getOrElse {
throw new IllegalStateException(
"Fatal error: Enso version included in buildinfo is not a valid " +
"semver string, this should never happen."
)
}
val authors =
if (authorName.isEmpty && authorEmail.isEmpty) List()
else List(Contact(name = authorName, email = authorEmail))
PackageManager.Default.create(
root = root,
name = name,
ensoVersion = SemVerEnsoVersion(currentVersion),
authors = authors,
maintainers = authors
)
exitSuccess()
}
@ -408,7 +473,12 @@ object Main {
exitSuccess()
}
if (line.hasOption(NEW_OPTION)) {
createNew(line.getOptionValue(NEW_OPTION))
createNew(
path = line.getOptionValue(NEW_OPTION),
nameOption = Option(line.getOptionValue(PROJECT_NAME_OPTION)),
authorName = Option(line.getOptionValue(PROJECT_AUTHOR_NAME_OPTION)),
authorEmail = Option(line.getOptionValue(PROJECT_AUTHOR_EMAIL_OPTION))
)
}
if (line.hasOption(RUN_OPTION)) {
run(

View File

@ -214,16 +214,18 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
def create(
root: F,
name: String,
version: String = "0.0.1",
ensoVersion: EnsoVersion = DefaultEnsoVersion
version: String = "0.0.1",
ensoVersion: EnsoVersion = DefaultEnsoVersion,
authors: List[Contact] = List(),
maintainers: List[Contact] = List()
): Package[F] = {
val config = Config(
name = normalizeName(name),
version = version,
ensoVersion = ensoVersion,
license = "",
authors = List(),
maintainers = List(),
authors = authors,
maintainers = maintainers,
dependencies = List()
)
create(root, config)