Reload project's config on rename (#7179)

Package's config information, once loaded, never changed. While there is typically no need for it, this was problematic when the config became out-of-sync with the filesystem, like in the case of project rename action.
In rename, the config's properties would be updated in the FS, but that would never be reflected in module's package. Therefore further compilations would continue to ask for the old namespace.

Most of the changes are cosmetic (s/`.config`/`.getConfig()`) except for the new `reloadConfig` method on `Package` that is being called in `RenameProjectCmd` handler.

Closes #7062.

# Important Notes
The reported `ExecutionFailed` error should have been mostly fixed already via #7143. This change makes sure that all the related warnings are gone as well and the compiler uses the updated namespace.
This commit is contained in:
Hubert Plociniczak 2023-07-03 11:36:19 +02:00 committed by GitHub
parent b78a7e74e8
commit 3c93c25a5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 73 additions and 46 deletions

View File

@ -31,7 +31,7 @@ class EditionReferenceResolver(
}
/** Returns the configuration of the current project. */
def getCurrentProjectConfig: Try[Config] = Try { projectPackage.config }
def getCurrentProjectConfig: Try[Config] = Try { projectPackage.getConfig() }
/** Resolves all edition dependencies of an edition identified by
* [[EditionReference]].

View File

@ -245,7 +245,7 @@ class LocalLibraryManager(
*/
private def findCurrentProjectEdition(): Option[Editions.RawEdition] = {
val pkg = PackageManager.Default.loadPackage(currentProjectRoot).get
pkg.config.edition
pkg.getConfig().edition
}
}

View File

@ -27,13 +27,16 @@ class ProjectSettingsManager(
private def loadSettings(): Try[SettingsResponse] = for {
pkg <- PackageManager.Default.loadPackage(projectRoot)
edition = pkg.config.edition.getOrElse(DefaultEdition.getDefaultEdition)
} yield SettingsResponse(edition.parent, pkg.config.preferLocalLibraries)
edition = pkg
.getConfig()
.edition
.getOrElse(DefaultEdition.getDefaultEdition)
} yield SettingsResponse(edition.parent, pkg.getConfig().preferLocalLibraries)
private def setParentEdition(editionName: String): Try[SettingsUpdated] =
for {
pkg <- PackageManager.Default.loadPackage(projectRoot)
newEdition = pkg.config.edition match {
newEdition = pkg.getConfig().edition match {
case Some(edition) => edition.copy(parent = Some(editionName))
case None => Editions.Raw.Edition(parent = Some(editionName))
}

View File

@ -10,8 +10,8 @@ import org.enso.languageserver.util.UnhandledLogging
import org.enso.languageserver.workspace.WorkspaceApi.ProjectInfo
import org.enso.logger.masking.MaskedPath
import org.enso.pkg.{Config => PkgConfig}
import java.io.{File, FileInputStream}
import java.nio.charset.StandardCharsets
import java.io.{File, FileReader}
/** A request handler for `workspace/openFile` commands.
*/
@ -26,10 +26,7 @@ class ProjectInfoHandler(languageServerConfig: Config)
if (configFile.exists()) {
val projectConfig = PkgConfig.fromYaml(
new String(
new FileInputStream(configFile).readAllBytes(),
StandardCharsets.UTF_8
)
new FileReader(configFile)
)
if (projectConfig.isSuccess) {
val projectInfo = ProjectInfo.Result(

View File

@ -145,7 +145,7 @@ final class SuggestionsHandler(
MaskedPath(config.projectContentRoot.file.toPath),
t
),
pkg => self ! ProjectNameUpdated(pkg.config.name)
pkg => self ! ProjectNameUpdated(pkg.getConfig().name)
)
val requestId = UUID.randomUUID()
runtimeConnector ! Api.Request(requestId, Api.GetTypeGraphRequest())

View File

@ -463,10 +463,10 @@ class LibrariesTest extends BaseServerTest {
val mainPackage = libraryRoot.resolve("main.tgz")
assert(Files.exists(mainPackage))
val pkg = PackageManager.Default.loadPackage(libraryRoot.toFile).get
pkg.config.authors should contain theSameElementsAs Seq(
pkg.getConfig().authors should contain theSameElementsAs Seq(
Contact(name = Some("user"), email = Some("example@example.com"))
)
pkg.config.maintainers should contain theSameElementsAs Seq(
pkg.getConfig().maintainers should contain theSameElementsAs Seq(
Contact(name = Some("only-name"), email = None),
Contact(name = None, email = Some("foo@example.com"))
)

View File

@ -49,7 +49,7 @@ object DependencyPreinstaller {
val editionResolver = EditionResolver(editionProvider)
val edition = editionResolver
.resolve(
pkg.config.edition.getOrElse(DefaultEdition.getDefaultEdition)
pkg.getConfig().edition.getOrElse(DefaultEdition.getDefaultEdition)
) match {
case Left(error) =>
throw new RuntimeException(
@ -58,7 +58,7 @@ object DependencyPreinstaller {
case Right(value) => value
}
val preferLocalLibraries = pkg.config.preferLocalLibraries
val preferLocalLibraries = pkg.getConfig().preferLocalLibraries
val (localLibraryProvider, publishedLibraryProvider) =
DefaultLibraryProvider.makeProviders(

View File

@ -43,6 +43,13 @@ class RenameProjectCmd(
val projectModules = getProjectModules
projectModules.foreach { module =>
module.setIndexed(false)
val newConfig = module.getPackage.reloadConfig()
if (newConfig.isFailure) {
logger.log(
Level.WARNING,
s"Failed to reload package's config: ${newConfig.failed.get.getMessage}"
)
}
ctx.endpoint.sendToClient(
Api.Response(
Api.SuggestionsDatabaseModuleUpdateNotification(

View File

@ -271,7 +271,9 @@ class RuntimeComponentsTest
// check the registered component groups
val components = context.languageContext.getPackageRepository.getComponents
components.get(LibraryName("Enso_Test", "Test")).value shouldEqual
context.pkg.config.componentGroups
context.pkg
.getConfig()
.componentGroups
.getOrElse(fail("Unexpected config value."))
components
@ -348,7 +350,9 @@ class RuntimeComponentsTest
val components = context.languageContext.getPackageRepository.getComponents
components.get(LibraryName("Enso_Test", "Test")).value shouldEqual
context.pkg.config.componentGroups
context.pkg
.getConfig()
.componentGroups
.getOrElse(fail("Unexpected config value."))
components

View File

@ -92,7 +92,7 @@ public final class ImportExportCache extends Cache<ImportExportCache.CachedBindi
var pathSegments = new String[]{
pkg.namespace(),
pkg.name(),
pkg.config().version(),
pkg.getConfig().version(),
Info.ensoVersion(),
libraryName.namespace()
};

View File

@ -112,7 +112,7 @@ public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCac
pathSegmentsJava.addAll(Arrays.asList(
pkg.namespace(),
pkg.name(),
pkg.config().version(),
pkg.getConfig().version(),
Info.ensoVersion()
));
pathSegmentsJava.addAll(qualName.pathAsJava());

View File

@ -95,7 +95,7 @@ public final class SuggestionsCache
var pathSegments = new String[]{
pkg.namespace(),
pkg.name(),
pkg.config().version(),
pkg.getConfig().version(),
Info.ensoVersion(),
libraryName.namespace()
};

View File

@ -128,7 +128,7 @@ public abstract class EnsoProjectNode extends Node {
private static Atom createProjectDescriptionAtom(EnsoContext ctx, Package<TruffleFile> pkg) {
EnsoFile rootPath = new EnsoFile(pkg.root().normalize());
Object cfg = ctx.getEnvironment().asGuestValue(pkg.config());
Object cfg = ctx.getEnvironment().asGuestValue(pkg.getConfig());
return ctx.getBuiltins()
.getProjectDescription()
.getUniqueConstructor()

View File

@ -463,7 +463,7 @@ object PackageRepository {
): Either[Error, Unit] =
if (loadedComponents.contains(pkg.libraryName)) Right(())
else {
pkg.config.componentGroups match {
pkg.getConfig().componentGroups match {
case Left(err) =>
Left(Error.PackageLoadingError(err.getMessage()))
case Right(componentGroups) =>
@ -760,7 +760,7 @@ object PackageRepository {
.map(v => Editions.Raw.Edition(parent = Some(v)))
.orElse(
projectPackage
.flatMap(_.config.edition)
.flatMap(_.getConfig().edition)
)
.getOrElse(DefaultEdition.getDefaultEdition)
@ -777,7 +777,7 @@ object PackageRepository {
languageHome = homeManager,
edition = edition,
preferLocalLibraries =
projectPackage.exists(_.config.preferLocalLibraries)
projectPackage.exists(_.getConfig().preferLocalLibraries)
)
new Default(
resolvingLibraryProvider,

View File

@ -66,6 +66,7 @@ case class LibraryResolver(
case Some(parentEdition) =>
resolveLibraryFromEdition(libraryName, parentEdition)
case None =>
new Exception("library not found").printStackTrace()
Left(
LibraryResolutionError(
s"The library `$libraryName` is not defined within " +

View File

@ -44,9 +44,9 @@ class LibraryUploader(dependencyExtractor: DependencyExtractor[File]) {
)(implicit ec: ExecutionContext): Try[Unit] = Try {
FileSystem.withTemporaryDirectory("enso-upload") { tmpDir =>
val pkg = PackageManager.Default.loadPackage(projectRoot.toFile).get
val version = SemVer(pkg.config.version).getOrElse {
val version = SemVer(pkg.getConfig().version).getOrElse {
throw new IllegalStateException(
s"Project version [${pkg.config.version}] is not a valid semver " +
s"Project version [${pkg.getConfig().version}] is not a valid semver " +
s"string."
)
}

View File

@ -13,6 +13,7 @@ import org.enso.editions.{
SemVerEnsoVersion
}
import java.io.Reader
import scala.util.Try
/** An extra project dependency.
@ -250,6 +251,11 @@ object Config {
yaml.parser.parse(yamlString).flatMap(_.as[Config]).toTry
}
/** Tries to parse the [[Config]] directly from the Reader */
def fromYaml(reader: Reader): Try[Config] = {
yaml.parser.parse(reader).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

View File

@ -25,9 +25,9 @@ case class SourceFile[F](qualifiedName: QualifiedName, file: F)
* @param config the metadata contained in the package configuration
* @param fileSystem the file system access module
*/
case class Package[F](
root: F,
config: Config,
class Package[F](
val root: F,
initialConfig: Config,
implicit val fileSystem: FileSystem[F]
) {
import FileSystem.Syntax
@ -47,13 +47,25 @@ case class Package[F](
.getChild(Package.cacheDirName)
.getChild(Package.suggestionsCacheDirName)
private[this] var config: Config = initialConfig
def getConfig(): Config = config
/** Reloads the config from file system */
def reloadConfig(): Try[Config] = {
val configFile = root.getChild(Package.configFileName)
val newConfig = Using(configFile.newBufferedReader)(Config.fromYaml).flatten
newConfig.foreach(config = _)
newConfig
}
/** Sets the package name.
*
* @param newName the new package name
* @return a package with the updated name
*/
def setPackageName(newName: String): Package[F] =
this.copy(config = config.copy(name = newName))
def setPackageName(newName: String): Package[F] = {
new Package(root, config.copy(name = newName), fileSystem)
}
/** Stores the package metadata on the hard drive. If the package does not exist,
* creates the required directory structure.
@ -126,7 +138,7 @@ case class Package[F](
* valid anymore.
*/
def updateConfig(update: Config => Config): Package[F] = {
val newPkg = copy(config = update(config))
val newPkg = new Package(root, update(config), fileSystem)
newPkg.saveConfig()
newPkg
}
@ -234,7 +246,7 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
config: Config,
template: Template
): Package[F] = {
val pkg = Package(root, config, fileSystem)
val pkg = new Package(root, config, fileSystem)
pkg.save()
copyResources(pkg, template)
pkg
@ -300,18 +312,15 @@ class PackageManager[F](implicit val fileSystem: FileSystem[F]) {
val result =
if (!root.exists) Failure(PackageManager.PackageNotFound())
else {
def readConfig(file: F): Try[String] =
def readConfig(file: F): Try[Config] =
if (file.exists)
Using(file.newBufferedReader) { reader =>
reader.lines().iterator().asScala.mkString("\n")
}
Using(file.newBufferedReader)(Config.fromYaml).flatten
else Failure(PackageManager.PackageNotFound())
val configFile = root.getChild(Package.configFileName)
for {
resultStr <- readConfig(configFile)
result <- Config.fromYaml(resultStr)
} yield Package(root, result, fileSystem)
result <- readConfig(configFile)
} yield new Package(root, result, fileSystem)
}
result.recoverWith {
case packageLoadingException: PackageManager.PackageLoadingException =>

View File

@ -109,7 +109,7 @@ class ProjectFileRepository[
namespace = pkg.namespace,
kind = meta.kind,
created = meta.created,
edition = pkg.config.edition,
edition = pkg.getConfig().edition,
lastOpened = meta.lastOpened,
path = Some(directory.toString),
directoryCreationTime = directoryCreationTime
@ -156,7 +156,7 @@ class ProjectFileRepository[
for {
project <- getProject(projectId)
projectPackage <- getPackage(new File(project.path.get))
} yield projectPackage.config.name
} yield projectPackage.getConfig().name
}
/** @inheritdoc */
@ -166,7 +166,7 @@ class ProjectFileRepository[
for {
project <- getProject(projectId)
projectPackage <- getPackage(new File(project.path.get))
} yield projectPackage.config.namespace
} yield projectPackage.getConfig().namespace
}
private def loadPackage(

View File

@ -15,7 +15,7 @@ class Project(
) {
/** The edition associated with the project. */
def edition: Option[Editions.RawEdition] = pkg.config.edition
def edition: Option[Editions.RawEdition] = pkg.getConfig().edition
/** The package name of the project. */
def name: String = pkg.name
@ -24,5 +24,5 @@ class Project(
def path: Path = pkg.root.toPath
/** Project configuration. */
def config: Config = pkg.config
def config: Config = pkg.getConfig()
}