From e9006c1f767abcb760c90574254c6175b0f1b070 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Thu, 15 Jun 2023 17:14:51 +0200 Subject: [PATCH 1/3] Extract update info as data structures --- .../handlers/ProjectInfoCommandHandler.java | 159 +------ .../handlers/UpdateProjectCommandHandler.java | 418 +++--------------- .../devtools/project/state/ProjectStates.java | 116 +++++ .../project/update/ExtensionMapBuilder.java | 115 +++++ .../project/update/ExtensionUpdateInfo.java | 33 ++ .../devtools/project/update/PlatformInfo.java | 49 ++ .../update/ProjectExtensionsUpdateInfo.java | 45 ++ .../update/ProjectPlatformUpdateInfo.java | 43 ++ .../project/update/ProjectUpdateInfos.java | 269 +++++++++++ .../{ => rewrite}/QuarkusUpdateCommand.java | 4 +- .../{ => rewrite}/QuarkusUpdateException.java | 2 +- .../{ => rewrite}/QuarkusUpdateRecipe.java | 2 +- .../{ => rewrite}/QuarkusUpdateRecipeIO.java | 2 +- .../update/{ => rewrite}/QuarkusUpdates.java | 8 +- .../QuarkusUpdatesRepository.java | 2 +- .../{ => rewrite}/RewriteOperation.java | 2 +- .../UpdateDependencyVersionOperation.java | 4 +- ...dateManagedDependencyVersionOperation.java | 4 +- .../operations/UpdatePropertyOperation.java | 4 +- .../UpgradeGradlePluginOperation.java | 4 +- .../QuarkusUpdateRecipeIOTest.java | 6 +- .../QuarkusUpdatesRepositoryTest.java | 4 +- 22 files changed, 770 insertions(+), 525 deletions(-) create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/state/ProjectStates.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionUpdateInfo.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/PlatformInfo.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectPlatformUpdateInfo.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdateCommand.java (98%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdateException.java (85%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdateRecipe.java (97%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdateRecipeIO.java (97%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdates.java (89%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdatesRepository.java (99%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/RewriteOperation.java (88%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/operations/UpdateDependencyVersionOperation.java (87%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/operations/UpdateManagedDependencyVersionOperation.java (89%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/operations/UpdatePropertyOperation.java (86%) rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/{ => rewrite}/operations/UpgradeGradlePluginOperation.java (86%) rename independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdateRecipeIOTest.java (86%) rename independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/{ => rewrite}/QuarkusUpdatesRepositoryTest.java (79%) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java index 786b9de83d105..e05ade2a0acc4 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java @@ -2,20 +2,17 @@ import static io.quarkus.devtools.messagewriter.MessageIcons.OUT_OF_DATE_ICON; import static io.quarkus.devtools.messagewriter.MessageIcons.UP_TO_DATE_ICON; +import static io.quarkus.devtools.project.state.ProjectStates.resolveProjectState; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import io.quarkus.bootstrap.model.ApplicationModel; -import io.quarkus.bootstrap.workspace.WorkspaceModule; -import io.quarkus.bootstrap.workspace.WorkspaceModuleId; import io.quarkus.devtools.commands.ProjectInfo; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; @@ -25,12 +22,9 @@ import io.quarkus.devtools.project.state.ModuleState; import io.quarkus.devtools.project.state.ProjectState; import io.quarkus.devtools.project.state.TopExtensionDependency; +import io.quarkus.devtools.project.update.PlatformInfo; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; -import io.quarkus.maven.dependency.Dependency; -import io.quarkus.maven.dependency.DependencyFlags; -import io.quarkus.maven.dependency.ResolvedDependency; -import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.catalog.ExtensionOrigin; public class ProjectInfoCommandHandler implements QuarkusCommandHandler { @@ -58,13 +52,17 @@ protected static boolean logState(ProjectState projectState, boolean perModule, final Map providerInfo = new LinkedHashMap<>(); for (ArtifactCoords bom : projectState.getPlatformBoms()) { - providerInfo.computeIfAbsent(bom.getKey(), k -> new PlatformInfo()).imported = bom; + providerInfo.computeIfAbsent(bom.getKey(), k -> new PlatformInfo(bom, null)); } for (TopExtensionDependency dep : projectState.getExtensions()) { final ExtensionOrigin origin = dep.getOrigin(); if (origin != null && origin.isPlatform()) { - providerInfo.computeIfAbsent(origin.getBom().getKey(), k -> new PlatformInfo()).recommended = origin - .getBom(); + providerInfo.compute(origin.getBom().getKey(), (k, v) -> { + if (v == null) { + return new PlatformInfo(null, origin.getBom()); + } + return new PlatformInfo(v.getImported(), origin.getBom()); + }); } } @@ -74,19 +72,19 @@ protected static boolean logState(ProjectState projectState, boolean perModule, log.info("Quarkus platform BOMs:"); boolean recommendExtraImports = false; for (PlatformInfo platform : providerInfo.values()) { - if (platform.imported == null) { + if (!platform.isImported()) { recommendExtraImports = true; continue; } final StringBuilder sb = new StringBuilder(); - if (platform.recommended == null) { + if (platform.getRecommended() == null) { if (rectify) { sb.append(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.REMOVE, platform.imported.toCompactCoords())); + UpdateProjectCommandHandler.REMOVE, platform.getImported().toCompactCoords())); recommendationsAvailable = true; } else { sb.append(" "); - sb.append(platform.imported.toCompactCoords()); + sb.append(platform.getImported().toCompactCoords()); if (!projectState.getExtensions().isEmpty()) { // The extension check is for modules that are aggregating modules (e.g. parent POMs) // that import common BOMs. It's however not how it should be done. @@ -96,9 +94,9 @@ protected static boolean logState(ProjectState projectState, boolean perModule, } } else if (platform.isVersionUpdateRecommended()) { sb.append(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - rectify ? UpdateProjectCommandHandler.UPDATE : "", platform.imported.toCompactCoords())); + rectify ? UpdateProjectCommandHandler.UPDATE : "", platform.getImported().toCompactCoords())); if (rectify) { - sb.append(platform.imported.toCompactCoords()).append(" -> ") + sb.append(platform.getImported().toCompactCoords()).append(" -> ") .append(platform.getRecommendedVersion()); } else { sb.append(" ").append(OUT_OF_DATE_ICON.iconOrMessage()); @@ -106,17 +104,17 @@ protected static boolean logState(ProjectState projectState, boolean perModule, recommendationsAvailable = true; } else { sb.append(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, "", - platform.imported.toCompactCoords())) + platform.getImported().toCompactCoords())) .append(" ").append(UP_TO_DATE_ICON.iconOrMessage()); } log.info(sb.toString()); } if (rectify && recommendExtraImports) { for (PlatformInfo platform : providerInfo.values()) { - if (platform.imported == null) { + if (platform.getImported() == null) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.ADD, - platform.recommended.toCompactCoords())); + platform.getRecommended().toCompactCoords())); } } recommendationsAvailable = true; @@ -299,125 +297,4 @@ private static boolean logModuleInfo(ProjectState project, ModuleState module, P return recommendationsAvailable; } - protected static ProjectState resolveProjectState(ApplicationModel appModel, ExtensionCatalog currentCatalog) { - final ProjectState.Builder projectBuilder = ProjectState.builder(); - - final Collection importedPlatformBoms = appModel.getPlatforms().getImportedPlatformBoms(); - if (importedPlatformBoms.isEmpty()) { - return projectBuilder.build(); - } - - final Map extProviderBuilders = new LinkedHashMap<>(importedPlatformBoms.size()); - importedPlatformBoms.forEach(bom -> { - projectBuilder.addPlatformBom(bom); - extProviderBuilders.put(ExtensionProvider.key(bom, true), - ExtensionProvider.builder().setArtifact(bom).setPlatform(true)); - }); - - final Map projectModuleBuilders = new HashMap<>(); - final Map> directModuleDeps = new HashMap<>(); - - final WorkspaceModule appModule = appModel.getAppArtifact().getWorkspaceModule(); - if (appModule != null) { - final ModuleState.Builder module = ModuleState.builder().setWorkspaceModule(appModule).setMainModule(true); - projectModuleBuilders.put(appModule.getId(), module); - appModule.getDirectDependencies() - .forEach(d -> directModuleDeps.computeIfAbsent(d.getKey(), dk -> new ArrayList<>()).add(module)); - - for (Dependency constraint : appModule.getDirectDependencyConstraints()) { - if (extProviderBuilders.containsKey(constraint.toCompactCoords())) { - module.addPlatformBom(constraint); - } - } - - } - for (ResolvedDependency dep : appModel.getDependencies()) { - if (dep.getWorkspaceModule() != null) { - projectModuleBuilders.computeIfAbsent(dep.getWorkspaceModule().getId(), k -> { - final ModuleState.Builder module = ModuleState.builder() - .setWorkspaceModule(dep.getWorkspaceModule()); - dep.getWorkspaceModule().getDirectDependencies().forEach( - d -> directModuleDeps.computeIfAbsent(d.getKey(), dk -> new ArrayList<>()).add(module)); - return module; - }); - } - } - - final Map directExtDeps = new HashMap<>(); - for (ResolvedDependency dep : appModel.getDependencies()) { - if (dep.isFlagSet(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT)) { - directExtDeps.put(dep.getKey(), TopExtensionDependency.builder().setResolvedDependency(dep) - .setTransitive(!directModuleDeps.containsKey(dep.getKey()))); - } else if (dep.isRuntimeExtensionArtifact() && directModuleDeps.containsKey(dep.getKey())) { - directExtDeps.put(dep.getKey(), TopExtensionDependency.builder().setResolvedDependency(dep)); - } - } - - if (directExtDeps.isEmpty()) { - return projectBuilder.build(); - } - - currentCatalog.getExtensions().forEach(e -> { - final ArtifactKey key = e.getArtifact().getKey(); - final TopExtensionDependency.Builder dep = directExtDeps.get(key); - if (dep != null) { - dep.setCatalogMetadata(e); - } - }); - - for (TopExtensionDependency.Builder extBuilder : directExtDeps.values()) { - final List modules = directModuleDeps.getOrDefault(extBuilder.getKey(), - Collections.emptyList()); - final TopExtensionDependency dep = extBuilder.setTransitive(modules.isEmpty()).build(); - projectBuilder.addExtensionDependency(dep); - for (ModuleState.Builder module : modules) { - module.addExtensionDependency(dep); - } - final ExtensionProvider.Builder provider = extProviderBuilders.computeIfAbsent(dep.getProviderKey(), - k -> ExtensionProvider.builder().setOrigin(dep.getOrigin())); - provider.addExtension(dep); - } - - for (ExtensionProvider.Builder builder : extProviderBuilders.values()) { - projectBuilder.addExtensionProvider(builder.build()); - } - - for (ModuleState.Builder builder : projectModuleBuilders.values()) { - projectBuilder.addModule(builder.build()); - } - - return projectBuilder.build(); - } - - static class PlatformInfo { - ArtifactCoords imported; - ArtifactCoords recommended; - - boolean isVersionUpdateRecommended() { - return imported != null && recommended != null && !imported.getVersion().equals(recommended.getVersion()); - } - - String getRecommendedVersion() { - return recommended == null ? null : recommended.getVersion(); - } - - boolean isImported() { - return imported != null; - } - - boolean isToBeImported() { - return imported == null && recommended != null; - } - - ArtifactCoords getRecommendedCoords() { - return recommended == null ? imported : recommended; - } - - String getRecommendedProviderKey() { - if (recommended != null) { - return ExtensionProvider.key(recommended, true); - } - return ExtensionProvider.key(imported, true); - } - } } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index 612fef8f32b1c..e206cfd755d0f 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -1,46 +1,39 @@ package io.quarkus.devtools.commands.handlers; +import static io.quarkus.devtools.project.state.ProjectStates.resolveProjectState; +import static io.quarkus.devtools.project.update.ProjectUpdateInfos.resolvePlatformUpdateInfo; +import static io.quarkus.devtools.project.update.ProjectUpdateInfos.resolveRecommendedState; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.devtools.commands.UpdateProject; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; -import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler.PlatformInfo; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; -import io.quarkus.devtools.project.state.ExtensionProvider; -import io.quarkus.devtools.project.state.ModuleState; import io.quarkus.devtools.project.state.ProjectState; -import io.quarkus.devtools.project.state.TopExtensionDependency; -import io.quarkus.devtools.project.update.QuarkusUpdateCommand; -import io.quarkus.devtools.project.update.QuarkusUpdateException; -import io.quarkus.devtools.project.update.QuarkusUpdates; -import io.quarkus.devtools.project.update.QuarkusUpdatesRepository; +import io.quarkus.devtools.project.update.ExtensionUpdateInfo; +import io.quarkus.devtools.project.update.PlatformInfo; +import io.quarkus.devtools.project.update.ProjectExtensionsUpdateInfo; +import io.quarkus.devtools.project.update.ProjectPlatformUpdateInfo; +import io.quarkus.devtools.project.update.ProjectUpdateInfos; +import io.quarkus.devtools.project.update.rewrite.QuarkusUpdateCommand; +import io.quarkus.devtools.project.update.rewrite.QuarkusUpdateException; +import io.quarkus.devtools.project.update.rewrite.QuarkusUpdates; +import io.quarkus.devtools.project.update.rewrite.QuarkusUpdatesRepository; import io.quarkus.maven.dependency.ArtifactCoords; -import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.platform.tools.ToolsConstants; -import io.quarkus.registry.catalog.Extension; import io.quarkus.registry.catalog.ExtensionCatalog; -import io.quarkus.registry.catalog.ExtensionOrigin; -import io.quarkus.registry.catalog.selection.ExtensionOrigins; -import io.quarkus.registry.catalog.selection.OriginCombination; -import io.quarkus.registry.catalog.selection.OriginPreference; -import io.quarkus.registry.catalog.selection.OriginSelector; public class UpdateProjectCommandHandler implements QuarkusCommandHandler { public static final String ADD = "Add:"; @@ -55,7 +48,7 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws final String targetPlatformVersion = invocation.getValue(UpdateProject.TARGET_PLATFORM_VERSION); final boolean perModule = invocation.getValue(UpdateProject.PER_MODULE, false); - final ProjectState currentState = ProjectInfoCommandHandler.resolveProjectState(appModel, + final ProjectState currentState = resolveProjectState(appModel, invocation.getQuarkusProject().getExtensionsCatalog()); final ArtifactCoords projectQuarkusPlatformBom = getProjectQuarkusPlatformBOM(currentState); if (projectQuarkusPlatformBom == null) { @@ -68,7 +61,15 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws invocation.log().info("Instructions to update this project from '%s' to '%s':", projectQuarkusPlatformBom.getVersion(), targetPlatformVersion); final QuarkusProject quarkusProject = invocation.getQuarkusProject(); - logUpdates(currentState, targetCatalog, false, perModule, quarkusProject.log()); + final ProjectState recommendedState = resolveRecommendedState(currentState, targetCatalog, invocation.log()); + final ProjectPlatformUpdateInfo platformUpdateInfo = resolvePlatformUpdateInfo(currentState, + recommendedState); + final ProjectExtensionsUpdateInfo extensionsUpdateInfo = ProjectUpdateInfos.resolveExtensionsUpdateInfo( + currentState, + recommendedState); + + logUpdates(currentState, recommendedState, platformUpdateInfo, extensionsUpdateInfo, false, perModule, + quarkusProject.log()); final boolean noRewrite = invocation.getValue(UpdateProject.NO_REWRITE, false); if (!noRewrite) { @@ -129,7 +130,9 @@ private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentS return null; } - private static void logUpdates(ProjectState currentState, ExtensionCatalog recommendedCatalog, boolean recommendState, + private static void logUpdates(ProjectState currentState, ProjectState recommendedState, + ProjectPlatformUpdateInfo platformUpdateInfo, + ProjectExtensionsUpdateInfo extensionsUpdateInfo, boolean recommendState, boolean perModule, MessageWriter log) { if (currentState.getPlatformBoms().isEmpty()) { log.info("The project does not import any Quarkus platform BOM"); @@ -139,7 +142,6 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom log.info("Quarkus extension were not found among the project dependencies"); return; } - final ProjectState recommendedState = resolveRecommendedState(currentState, recommendedCatalog, log); if (currentState == recommendedState) { log.info("The project is up-to-date"); return; @@ -150,151 +152,83 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom return; } - // log instructions - final Map platformImports = new LinkedHashMap<>(); - for (ArtifactCoords c : currentState.getPlatformBoms()) { - final PlatformInfo info = new PlatformInfo(); - info.imported = c; - platformImports.put(c.getKey(), info); - } - List importVersionUpdates = new ArrayList<>(); - List newImports = new ArrayList<>(0); - for (ArtifactCoords c : recommendedState.getPlatformBoms()) { - final PlatformInfo importInfo = platformImports.computeIfAbsent(c.getKey(), k -> new PlatformInfo()); - importInfo.recommended = c; - if (importInfo.isToBeImported()) { - newImports.add(importInfo); - } else if (importInfo.isVersionUpdateRecommended()) { - importVersionUpdates.add(importInfo); - } - } - - log.info(""); - final boolean importsToBeRemoved = platformImports.values().stream().filter(p -> p.recommended == null).findFirst() - .isPresent(); - final boolean platformUpdatesAvailable = !importVersionUpdates.isEmpty() || !newImports.isEmpty() || importsToBeRemoved; - if (platformUpdatesAvailable) { + if (platformUpdateInfo.isPlatformUpdatesAvailable()) { log.info("Recommended Quarkus platform BOM updates:"); - if (!importVersionUpdates.isEmpty()) { - for (PlatformInfo importInfo : importVersionUpdates) { + if (!platformUpdateInfo.getImportVersionUpdates().isEmpty()) { + for (PlatformInfo importInfo : platformUpdateInfo.getImportVersionUpdates()) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.UPDATE, importInfo.imported.toCompactCoords()) + " -> " + UpdateProjectCommandHandler.UPDATE, importInfo.getImported().toCompactCoords()) + " -> " + importInfo.getRecommendedVersion()); } } - if (!newImports.isEmpty()) { - for (PlatformInfo importInfo : newImports) { + if (!platformUpdateInfo.getNewImports().isEmpty()) { + for (PlatformInfo importInfo : platformUpdateInfo.getNewImports()) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.ADD, importInfo.recommended.toCompactCoords())); + UpdateProjectCommandHandler.ADD, importInfo.getRecommended().toCompactCoords())); } } - if (importsToBeRemoved) { - for (PlatformInfo importInfo : platformImports.values()) { - if (importInfo.recommended == null) { + if (platformUpdateInfo.isImportsToBeRemoved()) { + for (PlatformInfo importInfo : platformUpdateInfo.getPlatformImports().values()) { + if (importInfo.getRecommended() == null) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.REMOVE, importInfo.imported.toCompactCoords())); + UpdateProjectCommandHandler.REMOVE, importInfo.getImported().toCompactCoords())); } } } log.info(""); } - final ExtensionMap extensionInfo = new ExtensionMap(currentState.getExtensions().size()); - for (TopExtensionDependency dep : currentState.getExtensions()) { - extensionInfo.add(new ExtensionInfo(dep)); - } - for (TopExtensionDependency dep : recommendedState.getExtensions()) { - final ExtensionInfo info = extensionInfo.get(dep.getKey()); - if (info != null) { - info.recommendedDep = dep; - } - } - final Map> versionedManagedExtensions = new LinkedHashMap<>(0); - final Map> removedExtensions = new LinkedHashMap<>(0); - final Map> addedExtensions = new LinkedHashMap<>(0); - final Map> nonPlatformExtensionUpdates = new LinkedHashMap<>(); - for (ExtensionInfo info : extensionInfo.values()) { - if (!info.isUpdateRecommended()) { - continue; - } - if (!info.currentDep.getKey().equals(info.getRecommendedDependency().getKey())) { - if (info.currentDep.isPlatformExtension()) { - removedExtensions.computeIfAbsent(info.currentDep.getProviderKey(), k -> new ArrayList<>()) - .add(info.currentDep.getArtifact()); - } else { - nonPlatformExtensionUpdates.computeIfAbsent(info.currentDep.getProviderKey(), k -> new ArrayList<>()) - .add(info); - } - if (info.getRecommendedDependency().isPlatformExtension()) { - addedExtensions.computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) - .add(info.getRecommendedDependency().getArtifact()); - } else { - nonPlatformExtensionUpdates - .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) - .add(info); - } - } else if (info.getRecommendedDependency().isPlatformExtension()) { - if (info.currentDep.isNonRecommendedVersion()) { - versionedManagedExtensions - .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) - .add(info); - } - } else if (!info.currentDep.getVersion().equals(info.getRecommendedDependency().getVersion())) { - nonPlatformExtensionUpdates - .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()).add(info); - } - } - - if (versionedManagedExtensions.isEmpty() - && removedExtensions.isEmpty() - && addedExtensions.isEmpty() - && nonPlatformExtensionUpdates.isEmpty()) { - if (!platformUpdatesAvailable) { + if (extensionsUpdateInfo.isEmpty()) { + if (!platformUpdateInfo.isPlatformUpdatesAvailable()) { log.info("The project is up-to-date"); } return; } - for (PlatformInfo platform : platformImports.values()) { + for (PlatformInfo platform : platformUpdateInfo.getPlatformImports().values()) { final String provider = platform.getRecommendedProviderKey(); - if (!versionedManagedExtensions.containsKey(provider) - && !removedExtensions.containsKey(provider) - && !addedExtensions.containsKey(provider)) { + if (!extensionsUpdateInfo.getVersionedManagedExtensions().containsKey(provider) + && !extensionsUpdateInfo.getRemovedExtensions().containsKey(provider) + && !extensionsUpdateInfo.getAddedExtensions().containsKey(provider)) { continue; } log.info("Extensions from " + platform.getRecommendedProviderKey() + ":"); - for (ExtensionInfo e : versionedManagedExtensions.getOrDefault(provider, Collections.emptyList())) { + for (ExtensionUpdateInfo e : extensionsUpdateInfo.getVersionedManagedExtensions().getOrDefault(provider, + Collections.emptyList())) { final StringBuilder sb = new StringBuilder(); sb.append(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.UPDATE, e.currentDep.getArtifact().toCompactCoords())); + UpdateProjectCommandHandler.UPDATE, e.getCurrentDep().getArtifact().toCompactCoords())); sb.append(" -> remove version (managed)"); log.info(sb.toString()); } - for (ArtifactCoords e : addedExtensions.getOrDefault(provider, Collections.emptyList())) { + for (ArtifactCoords e : extensionsUpdateInfo.getAddedExtensions().getOrDefault(provider, Collections.emptyList())) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.ADD, e.getKey().toGacString())); } - for (ArtifactCoords e : removedExtensions.getOrDefault(provider, Collections.emptyList())) { + for (ArtifactCoords e : extensionsUpdateInfo.getRemovedExtensions().getOrDefault(provider, + Collections.emptyList())) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.REMOVE, e.getKey().toGacString())); } log.info(""); } - if (!nonPlatformExtensionUpdates.isEmpty()) { - for (Map.Entry> provider : nonPlatformExtensionUpdates.entrySet()) { + if (!extensionsUpdateInfo.getNonPlatformExtensions().isEmpty()) { + for (Map.Entry> provider : extensionsUpdateInfo.getNonPlatformExtensions() + .entrySet()) { log.info("Extensions from " + provider.getKey() + ":"); - for (ExtensionInfo info : provider.getValue()) { - if (info.currentDep.isPlatformExtension()) { + for (ExtensionUpdateInfo info : provider.getValue()) { + if (info.getCurrentDep().isPlatformExtension()) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.ADD, info.getRecommendedDependency().getArtifact().toCompactCoords())); } else if (info.getRecommendedDependency().isPlatformExtension()) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.REMOVE, info.currentDep.getArtifact().toCompactCoords())); + UpdateProjectCommandHandler.REMOVE, info.getCurrentDep().getArtifact().toCompactCoords())); } else { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, - UpdateProjectCommandHandler.UPDATE, info.currentDep.getArtifact().toCompactCoords() + " -> " + UpdateProjectCommandHandler.UPDATE, + info.getCurrentDep().getArtifact().toCompactCoords() + " -> " + info.getRecommendedDependency().getVersion())); } } @@ -303,243 +237,6 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom } } - private static ProjectState resolveRecommendedState(ProjectState currentState, ExtensionCatalog latestCatalog, - MessageWriter log) { - if (currentState.getPlatformBoms().isEmpty()) { - return currentState; - } - if (currentState.getExtensions().isEmpty()) { - return currentState; - } - - final ExtensionMap extensionInfo = new ExtensionMap(); - for (TopExtensionDependency dep : currentState.getExtensions()) { - extensionInfo.add(new ExtensionInfo(dep)); - } - - for (Extension e : latestCatalog.getExtensions()) { - final ExtensionInfo candidate = extensionInfo.get(e.getArtifact().getKey()); - if (candidate != null && candidate.latestMetadata == null) { - // if the latestMetadata has already been initialized, it's already the preferred one - // that could happen if an artifact has relocated - candidate.latestMetadata = e; - } - } - - final List unknownExtensions = new ArrayList<>(0); - final List updateCandidates = new ArrayList<>(extensionInfo.size()); - final Map updateCandidatesByOrigin = new HashMap<>(); - for (ExtensionInfo i : extensionInfo.values()) { - if (i.latestMetadata == null) { - unknownExtensions.add(i); - } else { - updateCandidates.add(i.latestMetadata); - for (ExtensionOrigin o : i.latestMetadata.getOrigins()) { - updateCandidatesByOrigin.computeIfAbsent(o.getId(), k -> new ExtensionMap()).add(i); - } - } - } - - if (extensionInfo.isEmpty()) { - return currentState; - } - - if (!unknownExtensions.isEmpty()) { - log.warn( - "The configured Quarkus registries did not provide any compatibility information for the following extensions in the context of the currently recommended Quarkus platforms:"); - unknownExtensions.forEach(e -> log.warn(" " + e.currentDep.getArtifact().toCompactCoords())); - } - - final List recommendedOrigins; - try { - recommendedOrigins = getRecommendedOrigins(latestCatalog, updateCandidates); - } catch (QuarkusCommandException e) { - log.info("Failed to find a compatible configuration update for the project"); - return currentState; - } - - int collectedUpdates = 0; - for (ExtensionCatalog recommendedOrigin : recommendedOrigins) { - final ExtensionMap candidates = updateCandidatesByOrigin.get(recommendedOrigin.getId()); - for (Extension e : recommendedOrigin.getExtensions()) { - final ExtensionInfo info = candidates.get(e.getArtifact().getKey()); - if (info != null && info.recommendedMetadata == null) { - info.setRecommendedMetadata(e); - if (++collectedUpdates == updateCandidates.size()) { - break; - } - } - } - } - - final ProjectState.Builder stateBuilder = ProjectState.builder(); - for (ExtensionCatalog c : recommendedOrigins) { - if (c.isPlatform()) { - stateBuilder.addPlatformBom(c.getBom()); - } - } - - final Map extProviders = new LinkedHashMap<>(recommendedOrigins.size()); - for (ExtensionInfo info : extensionInfo.values()) { - final TopExtensionDependency ext = info.getRecommendedDependency(); - stateBuilder.addExtensionDependency(ext); - extProviders.computeIfAbsent(ext.getProviderKey(), k -> ExtensionProvider.builder().setOrigin(ext.getOrigin())) - .addExtension(ext); - } - - extProviders.values().forEach(b -> stateBuilder.addExtensionProvider(b.build())); - - for (ModuleState module : currentState.getModules()) { - final ModuleState.Builder moduleBuilder = ModuleState.builder() - .setMainModule(module.isMain()) - .setWorkspaceModule(module.getWorkspaceModule()); - for (TopExtensionDependency dep : module.getExtensions()) { - final TopExtensionDependency recommendedDep = extensionInfo.get(dep.getKey()).getRecommendedDependency(); - moduleBuilder.addExtensionDependency(recommendedDep); - final ExtensionOrigin origin = recommendedDep.getOrigin(); - if (origin != null && origin.isPlatform()) { - moduleBuilder.addPlatformBom(origin.getBom()); - } - } - stateBuilder.addModule(moduleBuilder.build()); - } - - return stateBuilder.build(); - } - - private static class ExtensionInfo { - final TopExtensionDependency currentDep; - Extension latestMetadata; - Extension recommendedMetadata; - TopExtensionDependency recommendedDep; - - ExtensionInfo(TopExtensionDependency currentDep) { - this.currentDep = currentDep; - } - - void setRecommendedMetadata(Extension e) { - this.recommendedMetadata = e; - if (!currentDep.getArtifact().getKey().equals(e.getArtifact().getKey())) { - } - } - - Extension getRecommendedMetadata() { - if (recommendedMetadata != null) { - return recommendedMetadata; - } - if (recommendedDep == null) { - return currentDep.getCatalogMetadata(); - } - return recommendedMetadata = currentDep.getCatalogMetadata(); - } - - TopExtensionDependency getRecommendedDependency() { - if (recommendedDep != null) { - return recommendedDep; - } - if (recommendedMetadata == null) { - return currentDep; - } - return recommendedDep = TopExtensionDependency.builder() - .setArtifact(recommendedMetadata.getArtifact()) - .setCatalogMetadata(recommendedMetadata) - .setTransitive(currentDep.isTransitive()) - .build(); - } - - boolean isUpdateRecommended() { - return getRecommendedDependency() != currentDep; - } - } - - private static List getRecommendedOrigins(ExtensionCatalog extensionCatalog, List extensions) - throws QuarkusCommandException { - final List extOrigins = new ArrayList<>(extensions.size()); - for (Extension e : extensions) { - addOrigins(extOrigins, e); - } - - final OriginCombination recommendedCombination = OriginSelector.of(extOrigins).calculateRecommendedCombination(); - if (recommendedCombination == null) { - final StringBuilder buf = new StringBuilder(); - buf.append("Failed to determine a compatible Quarkus version for the requested extensions: "); - buf.append(extensions.get(0).getArtifact().getKey().toGacString()); - for (int i = 1; i < extensions.size(); ++i) { - buf.append(", ").append(extensions.get(i).getArtifact().getKey().toGacString()); - } - throw new QuarkusCommandException(buf.toString()); - } - return recommendedCombination.getUniqueSortedOrigins().stream().map(o -> o.getCatalog()).collect(Collectors.toList()); - } - - private static void addOrigins(final List extOrigins, Extension e) { - ExtensionOrigins.Builder eoBuilder = null; - for (ExtensionOrigin o : e.getOrigins()) { - if (!(o instanceof ExtensionCatalog)) { - continue; - } - final ExtensionCatalog c = (ExtensionCatalog) o; - final OriginPreference op = (OriginPreference) c.getMetadata().get("origin-preference"); - if (op == null) { - continue; - } - if (eoBuilder == null) { - eoBuilder = ExtensionOrigins.builder(e.getArtifact().getKey()); - } - eoBuilder.addOrigin(c, op); - } - if (eoBuilder != null) { - extOrigins.add(eoBuilder.build()); - } - } - - private static class ExtensionMap { - final Map> extensionInfo; - final List list = new ArrayList<>(); - - ExtensionMap() { - this.extensionInfo = new LinkedHashMap<>(); - } - - ExtensionMap(int size) { - this.extensionInfo = new LinkedHashMap<>(size); - } - - void add(ExtensionInfo e) { - extensionInfo.put(e.currentDep.getArtifact().getArtifactId(), Collections.singletonList(e)); - list.add(e); - } - - ExtensionInfo get(ArtifactKey key) { - final List list = extensionInfo.get(key.getArtifactId()); - if (list == null || list.isEmpty()) { - return null; - } - if (list.size() == 1) { - return list.get(0); - } - for (ExtensionInfo e : list) { - if (e.currentDep.getKey().equals(key) - || e.getRecommendedDependency() != null && e.getRecommendedDependency().getKey().equals(key)) { - return e; - } - } - throw new IllegalArgumentException(key + " isn't found in the extension map"); - } - - Collection values() { - return list; - } - - int size() { - return extensionInfo.size(); - } - - boolean isEmpty() { - return extensionInfo.isEmpty(); - } - } - @SuppressWarnings({ "rawtypes", "unchecked" }) private T getMetadata(ExtensionCatalog catalog, String... path) { Object currentValue = catalog.getMetadata(); @@ -553,4 +250,5 @@ private T getMetadata(ExtensionCatalog catalog, String... path) { return (T) currentValue; } + } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/state/ProjectStates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/state/ProjectStates.java new file mode 100644 index 0000000000000..1364dbb3eee2e --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/state/ProjectStates.java @@ -0,0 +1,116 @@ +package io.quarkus.devtools.project.state; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.workspace.WorkspaceModule; +import io.quarkus.bootstrap.workspace.WorkspaceModuleId; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.DependencyFlags; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.registry.catalog.ExtensionCatalog; + +public final class ProjectStates { + + private ProjectStates() { + } + + public static ProjectState resolveProjectState(ApplicationModel appModel, ExtensionCatalog currentCatalog) { + final ProjectState.Builder projectBuilder = ProjectState.builder(); + + final Collection importedPlatformBoms = appModel.getPlatforms().getImportedPlatformBoms(); + if (importedPlatformBoms.isEmpty()) { + return projectBuilder.build(); + } + + final Map extProviderBuilders = new LinkedHashMap<>(importedPlatformBoms.size()); + importedPlatformBoms.forEach(bom -> { + projectBuilder.addPlatformBom(bom); + extProviderBuilders.put(ExtensionProvider.key(bom, true), + ExtensionProvider.builder().setArtifact(bom).setPlatform(true)); + }); + + final Map projectModuleBuilders = new HashMap<>(); + final Map> directModuleDeps = new HashMap<>(); + + final WorkspaceModule appModule = appModel.getAppArtifact().getWorkspaceModule(); + if (appModule != null) { + final ModuleState.Builder module = ModuleState.builder().setWorkspaceModule(appModule).setMainModule(true); + projectModuleBuilders.put(appModule.getId(), module); + appModule.getDirectDependencies() + .forEach(d -> directModuleDeps.computeIfAbsent(d.getKey(), dk -> new ArrayList<>()).add(module)); + + for (Dependency constraint : appModule.getDirectDependencyConstraints()) { + if (extProviderBuilders.containsKey(constraint.toCompactCoords())) { + module.addPlatformBom(constraint); + } + } + + } + for (ResolvedDependency dep : appModel.getDependencies()) { + if (dep.getWorkspaceModule() != null) { + projectModuleBuilders.computeIfAbsent(dep.getWorkspaceModule().getId(), k -> { + final ModuleState.Builder module = ModuleState.builder() + .setWorkspaceModule(dep.getWorkspaceModule()); + dep.getWorkspaceModule().getDirectDependencies().forEach( + d -> directModuleDeps.computeIfAbsent(d.getKey(), dk -> new ArrayList<>()).add(module)); + return module; + }); + } + } + + final Map directExtDeps = new HashMap<>(); + for (ResolvedDependency dep : appModel.getDependencies()) { + if (dep.isFlagSet(DependencyFlags.TOP_LEVEL_RUNTIME_EXTENSION_ARTIFACT)) { + directExtDeps.put(dep.getKey(), TopExtensionDependency.builder().setResolvedDependency(dep) + .setTransitive(!directModuleDeps.containsKey(dep.getKey()))); + } else if (dep.isRuntimeExtensionArtifact() && directModuleDeps.containsKey(dep.getKey())) { + directExtDeps.put(dep.getKey(), TopExtensionDependency.builder().setResolvedDependency(dep)); + } + } + + if (directExtDeps.isEmpty()) { + return projectBuilder.build(); + } + + currentCatalog.getExtensions().forEach(e -> { + final ArtifactKey key = e.getArtifact().getKey(); + final TopExtensionDependency.Builder dep = directExtDeps.get(key); + if (dep != null) { + dep.setCatalogMetadata(e); + } + }); + + for (TopExtensionDependency.Builder extBuilder : directExtDeps.values()) { + final List modules = directModuleDeps.getOrDefault(extBuilder.getKey(), + Collections.emptyList()); + final TopExtensionDependency dep = extBuilder.setTransitive(modules.isEmpty()).build(); + projectBuilder.addExtensionDependency(dep); + for (ModuleState.Builder module : modules) { + module.addExtensionDependency(dep); + } + final ExtensionProvider.Builder provider = extProviderBuilders.computeIfAbsent(dep.getProviderKey(), + k -> ExtensionProvider.builder().setOrigin(dep.getOrigin())); + provider.addExtension(dep); + } + + for (ExtensionProvider.Builder builder : extProviderBuilders.values()) { + projectBuilder.addExtensionProvider(builder.build()); + } + + for (ModuleState.Builder builder : projectModuleBuilders.values()) { + projectBuilder.addModule(builder.build()); + } + + return projectBuilder.build(); + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java new file mode 100644 index 0000000000000..e91c09f14670b --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java @@ -0,0 +1,115 @@ +package io.quarkus.devtools.project.update; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.devtools.project.state.TopExtensionDependency; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.registry.catalog.Extension; + +final class ExtensionMapBuilder { + final Map> extensionInfo; + final List list = new ArrayList<>(); + + public ExtensionMapBuilder() { + this.extensionInfo = new LinkedHashMap<>(); + } + + public ExtensionMapBuilder(int size) { + this.extensionInfo = new LinkedHashMap<>(size); + } + + public void add(ExtensionUpdateInfoBuilder e) { + extensionInfo.put(e.currentDep.getArtifact().getArtifactId(), Collections.singletonList(e)); + list.add(e); + } + + public ExtensionUpdateInfoBuilder get(ArtifactKey key) { + final List list = extensionInfo.get(key.getArtifactId()); + if (list == null || list.isEmpty()) { + return null; + } + if (list.size() == 1) { + return list.get(0); + } + for (ExtensionUpdateInfoBuilder e : list) { + final TopExtensionDependency recommendedDep = e.resolveRecommendedDep(); + if (e.currentDep.getKey().equals(key) + || recommendedDep != null && recommendedDep.getKey().equals(key)) { + return e; + } + } + throw new IllegalArgumentException(key + " isn't found in the extension map"); + } + + public Collection values() { + return list; + } + + public int size() { + return extensionInfo.size(); + } + + public boolean isEmpty() { + return extensionInfo.isEmpty(); + } + + public static final class ExtensionUpdateInfoBuilder { + private final TopExtensionDependency currentDep; + private Extension recommendedMetadata; + private TopExtensionDependency recommendedDep; + + private Extension latestMetadata; + + public ExtensionUpdateInfoBuilder(TopExtensionDependency currentDep) { + this.currentDep = currentDep; + } + + public TopExtensionDependency getCurrentDep() { + return currentDep; + } + + public Extension getRecommendedMetadata() { + return recommendedMetadata; + } + + public void setRecommendedMetadata(Extension e) { + this.recommendedMetadata = e; + } + + public ExtensionUpdateInfoBuilder setRecommendedDep(TopExtensionDependency recommendedDep) { + this.recommendedDep = recommendedDep; + return this; + } + + public ExtensionUpdateInfo build() { + return new ExtensionUpdateInfo(currentDep, currentDep.getCatalogMetadata(), resolveRecommendedDep()); + } + + public TopExtensionDependency resolveRecommendedDep() { + if (recommendedDep != null) { + return recommendedDep; + } + return recommendedMetadata == null ? currentDep + : TopExtensionDependency.builder() + .setArtifact(recommendedMetadata.getArtifact()) + .setCatalogMetadata(recommendedMetadata) + .setTransitive(currentDep.isTransitive()) + .build(); + } + + public Extension getLatestMetadata() { + return latestMetadata; + } + + public ExtensionUpdateInfoBuilder setLatestMetadata(Extension latestMetadata) { + this.latestMetadata = latestMetadata; + return this; + } + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionUpdateInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionUpdateInfo.java new file mode 100644 index 0000000000000..5d2b530460937 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionUpdateInfo.java @@ -0,0 +1,33 @@ +package io.quarkus.devtools.project.update; + +import io.quarkus.devtools.project.state.TopExtensionDependency; +import io.quarkus.registry.catalog.Extension; + +public final class ExtensionUpdateInfo { + private final TopExtensionDependency currentDep; + private final Extension recommendedMetadata; + private final TopExtensionDependency recommendedDep; + + public ExtensionUpdateInfo(TopExtensionDependency currentDep, Extension recommendedMetadata, + TopExtensionDependency recommendedDep) { + this.currentDep = currentDep; + this.recommendedMetadata = recommendedMetadata; + this.recommendedDep = recommendedDep; + } + + public TopExtensionDependency getCurrentDep() { + return currentDep; + } + + public Extension getRecommendedMetadata() { + return recommendedMetadata; + } + + public TopExtensionDependency getRecommendedDependency() { + return recommendedDep; + } + + public boolean isUpdateRecommended() { + return recommendedDep != currentDep; + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/PlatformInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/PlatformInfo.java new file mode 100644 index 0000000000000..020b9a94b90aa --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/PlatformInfo.java @@ -0,0 +1,49 @@ +package io.quarkus.devtools.project.update; + +import io.quarkus.devtools.project.state.ExtensionProvider; +import io.quarkus.maven.dependency.ArtifactCoords; + +public class PlatformInfo { + private final ArtifactCoords imported; + private final ArtifactCoords recommended; + + public PlatformInfo(ArtifactCoords imported, ArtifactCoords recommended) { + this.imported = imported; + this.recommended = recommended; + } + + public ArtifactCoords getImported() { + return imported; + } + + public ArtifactCoords getRecommended() { + return recommended; + } + + public boolean isVersionUpdateRecommended() { + return imported != null && recommended != null && !imported.getVersion().equals(recommended.getVersion()); + } + + public String getRecommendedVersion() { + return recommended == null ? null : recommended.getVersion(); + } + + public boolean isImported() { + return imported != null; + } + + public boolean isToBeImported() { + return imported == null && recommended != null; + } + + public ArtifactCoords getRecommendedCoords() { + return recommended == null ? imported : recommended; + } + + public String getRecommendedProviderKey() { + if (recommended != null) { + return ExtensionProvider.key(recommended, true); + } + return ExtensionProvider.key(imported, true); + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java new file mode 100644 index 0000000000000..e4cb0c6445605 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java @@ -0,0 +1,45 @@ +package io.quarkus.devtools.project.update; + +import java.util.List; +import java.util.Map; + +import io.quarkus.maven.dependency.ArtifactCoords; + +public class ProjectExtensionsUpdateInfo { + final Map> versionedManagedExtensions; + final Map> removedExtensions; + final Map> addedExtensions; + final Map> nonPlatformExtensions; + + public ProjectExtensionsUpdateInfo(Map> versionedManagedExtensions, + Map> removedExtensions, Map> addedExtensions, + Map> nonPlatformExtensionUpdate) { + this.versionedManagedExtensions = versionedManagedExtensions; + this.removedExtensions = removedExtensions; + this.addedExtensions = addedExtensions; + this.nonPlatformExtensions = nonPlatformExtensionUpdate; + } + + public Map> getVersionedManagedExtensions() { + return versionedManagedExtensions; + } + + public Map> getRemovedExtensions() { + return removedExtensions; + } + + public Map> getAddedExtensions() { + return addedExtensions; + } + + public Map> getNonPlatformExtensions() { + return nonPlatformExtensions; + } + + public boolean isEmpty() { + return versionedManagedExtensions.isEmpty() + && removedExtensions.isEmpty() + && addedExtensions.isEmpty() + && nonPlatformExtensions.isEmpty(); + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectPlatformUpdateInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectPlatformUpdateInfo.java new file mode 100644 index 0000000000000..1958ee644329e --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectPlatformUpdateInfo.java @@ -0,0 +1,43 @@ +package io.quarkus.devtools.project.update; + +import java.util.List; +import java.util.Map; + +import io.quarkus.maven.dependency.ArtifactKey; + +public class ProjectPlatformUpdateInfo { + private final Map platformImports; + private final List importVersionUpdates; + private final List newImports; + private final boolean importsToBeRemoved; + private final boolean platformUpdatesAvailable; + + public ProjectPlatformUpdateInfo(Map platformImports, List importVersionUpdates, + List newImports) { + this.platformImports = platformImports; + this.importVersionUpdates = importVersionUpdates; + this.newImports = newImports; + this.importsToBeRemoved = platformImports.values().stream().anyMatch(p -> p.getRecommended() == null); + this.platformUpdatesAvailable = !importVersionUpdates.isEmpty() || !newImports.isEmpty() || importsToBeRemoved; + } + + public boolean isImportsToBeRemoved() { + return importsToBeRemoved; + } + + public boolean isPlatformUpdatesAvailable() { + return platformUpdatesAvailable; + } + + public Map getPlatformImports() { + return platformImports; + } + + public List getImportVersionUpdates() { + return importVersionUpdates; + } + + public List getNewImports() { + return newImports; + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java new file mode 100644 index 0000000000000..34443d1a17209 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java @@ -0,0 +1,269 @@ +package io.quarkus.devtools.project.update; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.state.ExtensionProvider; +import io.quarkus.devtools.project.state.ModuleState; +import io.quarkus.devtools.project.state.ProjectState; +import io.quarkus.devtools.project.state.TopExtensionDependency; +import io.quarkus.devtools.project.update.ExtensionMapBuilder.ExtensionUpdateInfoBuilder; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.registry.catalog.Extension; +import io.quarkus.registry.catalog.ExtensionCatalog; +import io.quarkus.registry.catalog.ExtensionOrigin; +import io.quarkus.registry.catalog.selection.ExtensionOrigins; +import io.quarkus.registry.catalog.selection.OriginCombination; +import io.quarkus.registry.catalog.selection.OriginPreference; +import io.quarkus.registry.catalog.selection.OriginSelector; + +public final class ProjectUpdateInfos { + + private ProjectUpdateInfos() { + } + + public static ProjectExtensionsUpdateInfo resolveExtensionsUpdateInfo(ProjectState currentState, + ProjectState recommendedState) { + checkProjectState(currentState, recommendedState); + + final ExtensionMapBuilder extensionInfo = new ExtensionMapBuilder(currentState.getExtensions().size()); + for (TopExtensionDependency dep : currentState.getExtensions()) { + extensionInfo.add(new ExtensionUpdateInfoBuilder(dep)); + } + for (TopExtensionDependency dep : recommendedState.getExtensions()) { + final ExtensionUpdateInfoBuilder info = extensionInfo.get(dep.getKey()); + if (info != null) { + info.setRecommendedDep(dep); + } + } + final Map> versionedManagedExtensions = new LinkedHashMap<>(0); + final Map> removedExtensions = new LinkedHashMap<>(0); + final Map> addedExtensions = new LinkedHashMap<>(0); + final Map> nonPlatformExtensionUpdates = new LinkedHashMap<>(); + for (ExtensionUpdateInfoBuilder infoBuilder : extensionInfo.values()) { + final ExtensionUpdateInfo info = infoBuilder.build(); + if (!info.isUpdateRecommended()) { + continue; + } + if (!info.getCurrentDep().getKey().equals(info.getRecommendedDependency().getKey())) { + if (info.getCurrentDep().isPlatformExtension()) { + removedExtensions.computeIfAbsent(info.getCurrentDep().getProviderKey(), k -> new ArrayList<>()) + .add(info.getCurrentDep().getArtifact()); + } else { + nonPlatformExtensionUpdates.computeIfAbsent(info.getCurrentDep().getProviderKey(), k -> new ArrayList<>()) + .add(info); + } + if (info.getRecommendedDependency().isPlatformExtension()) { + addedExtensions.computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) + .add(info.getRecommendedDependency().getArtifact()); + } else { + nonPlatformExtensionUpdates + .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) + .add(info); + } + } else if (info.getRecommendedDependency().isPlatformExtension()) { + if (info.getCurrentDep().isNonRecommendedVersion()) { + versionedManagedExtensions + .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) + .add(info); + } + } else if (!info.getCurrentDep().getVersion().equals(info.getRecommendedDependency().getVersion())) { + nonPlatformExtensionUpdates + .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()).add(info); + } + } + return new ProjectExtensionsUpdateInfo(versionedManagedExtensions, removedExtensions, addedExtensions, + nonPlatformExtensionUpdates); + } + + public static ProjectPlatformUpdateInfo resolvePlatformUpdateInfo(ProjectState currentState, + ProjectState recommendedState) { + checkProjectState(currentState, recommendedState); + + final Map platformImports = new LinkedHashMap<>(); + for (ArtifactCoords c : currentState.getPlatformBoms()) { + final PlatformInfo info = new PlatformInfo(c, null); + platformImports.put(c.getKey(), info); + } + List importVersionUpdates = new ArrayList<>(); + List newImports = new ArrayList<>(0); + for (ArtifactCoords c : recommendedState.getPlatformBoms()) { + final PlatformInfo importInfo = platformImports.compute(c.getKey(), (k, v) -> { + if (v == null) { + return new PlatformInfo(null, c); + } + return new PlatformInfo(v.getImported(), c); + }); + if (importInfo.isToBeImported()) { + newImports.add(importInfo); + } else if (importInfo.isVersionUpdateRecommended()) { + importVersionUpdates.add(importInfo); + } + } + return new ProjectPlatformUpdateInfo(platformImports, importVersionUpdates, newImports); + } + + private static void checkProjectState(ProjectState currentState, ProjectState recommendedState) { + if (currentState.getPlatformBoms().isEmpty()) { + throw new IllegalStateException("The project does not import any Quarkus platform BOM"); + } + if (currentState.getExtensions().isEmpty()) { + throw new IllegalStateException("Quarkus extension were not found among the project dependencies"); + } + if (currentState == recommendedState) { + throw new IllegalStateException("The project is up-to-date"); + } + } + + public static ProjectState resolveRecommendedState(ProjectState currentState, ExtensionCatalog recommendedCatalog, + MessageWriter log) { + if (currentState.getPlatformBoms().isEmpty()) { + return currentState; + } + if (currentState.getExtensions().isEmpty()) { + return currentState; + } + + final ExtensionMapBuilder builder = new ExtensionMapBuilder(); + for (TopExtensionDependency dep : currentState.getExtensions()) { + builder.add(new ExtensionUpdateInfoBuilder(dep)); + } + + for (Extension e : recommendedCatalog.getExtensions()) { + final ExtensionUpdateInfoBuilder candidate = builder.get(e.getArtifact().getKey()); + if (candidate != null && candidate.getLatestMetadata() == null) { + // if the latestMetadata has already been initialized, it's already the preferred one + // that could happen if an artifact has relocated + candidate.setLatestMetadata(e); + } + } + + final List unknownExtensions = new ArrayList<>(0); + final List updateCandidates = new ArrayList<>(builder.size()); + final Map updateCandidatesByOrigin = new HashMap<>(); + for (ExtensionUpdateInfoBuilder i : builder.values()) { + if (i.getLatestMetadata() == null) { + unknownExtensions.add(i); + } else { + updateCandidates.add(i.getLatestMetadata()); + for (ExtensionOrigin o : i.getLatestMetadata().getOrigins()) { + updateCandidatesByOrigin.computeIfAbsent(o.getId(), k -> new ExtensionMapBuilder()).add(i); + } + } + } + + if (builder.isEmpty()) { + return currentState; + } + + if (!unknownExtensions.isEmpty()) { + log.warn( + "The configured Quarkus registries did not provide any compatibility information for the following extensions in the context of the currently recommended Quarkus platforms:"); + unknownExtensions.forEach(e -> log.warn(" " + e.getCurrentDep().getArtifact().toCompactCoords())); + } + + final List recommendedOrigins; + try { + recommendedOrigins = getRecommendedOrigins(recommendedCatalog, updateCandidates); + } catch (QuarkusCommandException e) { + log.warn("Failed to find a compatible configuration update for the project"); + return currentState; + } + + int collectedUpdates = 0; + for (ExtensionCatalog recommendedOrigin : recommendedOrigins) { + final ExtensionMapBuilder candidates = updateCandidatesByOrigin.get(recommendedOrigin.getId()); + for (Extension e : recommendedOrigin.getExtensions()) { + final ExtensionUpdateInfoBuilder info = candidates.get(e.getArtifact().getKey()); + if (info != null && info.getRecommendedMetadata() == null) { + info.setRecommendedMetadata(e); + if (++collectedUpdates == updateCandidates.size()) { + break; + } + } + } + } + + final ProjectState.Builder stateBuilder = ProjectState.builder(); + for (ExtensionCatalog c : recommendedOrigins) { + if (c.isPlatform()) { + stateBuilder.addPlatformBom(c.getBom()); + } + } + + final Map extProviders = new LinkedHashMap<>(recommendedOrigins.size()); + for (ExtensionUpdateInfoBuilder info : builder.values()) { + final TopExtensionDependency ext = info.resolveRecommendedDep(); + stateBuilder.addExtensionDependency(ext); + extProviders.computeIfAbsent(ext.getProviderKey(), k -> ExtensionProvider.builder().setOrigin(ext.getOrigin())) + .addExtension(ext); + } + + extProviders.values().forEach(b -> stateBuilder.addExtensionProvider(b.build())); + + for (ModuleState module : currentState.getModules()) { + final ModuleState.Builder moduleBuilder = ModuleState.builder() + .setMainModule(module.isMain()) + .setWorkspaceModule(module.getWorkspaceModule()); + for (TopExtensionDependency dep : module.getExtensions()) { + final TopExtensionDependency recommendedDep = builder.get(dep.getKey()).resolveRecommendedDep(); + moduleBuilder.addExtensionDependency(recommendedDep); + final ExtensionOrigin origin = recommendedDep.getOrigin(); + if (origin != null && origin.isPlatform()) { + moduleBuilder.addPlatformBom(origin.getBom()); + } + } + stateBuilder.addModule(moduleBuilder.build()); + } + + return stateBuilder.build(); + } + + private static List getRecommendedOrigins(ExtensionCatalog extensionCatalog, List extensions) + throws QuarkusCommandException { + final List extOrigins = new ArrayList<>(extensions.size()); + for (Extension e : extensions) { + addOrigins(extOrigins, e); + } + + final OriginCombination recommendedCombination = OriginSelector.of(extOrigins).calculateRecommendedCombination(); + if (recommendedCombination == null) { + final StringBuilder buf = new StringBuilder(); + buf.append("Failed to determine a compatible Quarkus version for the requested extensions: "); + buf.append(extensions.get(0).getArtifact().getKey().toGacString()); + for (int i = 1; i < extensions.size(); ++i) { + buf.append(", ").append(extensions.get(i).getArtifact().getKey().toGacString()); + } + throw new QuarkusCommandException(buf.toString()); + } + return recommendedCombination.getUniqueSortedOrigins().stream().map(o -> o.getCatalog()).collect(Collectors.toList()); + } + + private static void addOrigins(final List extOrigins, Extension e) { + ExtensionOrigins.Builder eoBuilder = null; + for (ExtensionOrigin o : e.getOrigins()) { + if (!(o instanceof ExtensionCatalog)) { + continue; + } + final ExtensionCatalog c = (ExtensionCatalog) o; + final OriginPreference op = (OriginPreference) c.getMetadata().get("origin-preference"); + if (op == null) { + continue; + } + if (eoBuilder == null) { + eoBuilder = ExtensionOrigins.builder(e.getArtifact().getKey()); + } + eoBuilder.addOrigin(c, op); + } + if (eoBuilder != null) { + extOrigins.add(eoBuilder.build()); + } + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java similarity index 98% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java index 9709f184cb539..41d4c6ccf6c17 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java @@ -1,6 +1,6 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; -import static io.quarkus.devtools.project.update.QuarkusUpdateRecipe.RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS; +import static io.quarkus.devtools.project.update.rewrite.QuarkusUpdateRecipe.RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS; import java.io.BufferedReader; import java.io.File; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateException.java similarity index 85% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateException.java index 6bceefbf0b9cb..755c396fdbe39 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateException.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; public class QuarkusUpdateException extends Exception { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipe.java similarity index 97% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipe.java index 6df006981e367..2d6339b98ae9d 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipe.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import java.util.ArrayList; import java.util.List; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java similarity index 97% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java index 964e0e44822ba..c6f6485ddb0d0 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import java.io.IOException; import java.nio.file.Files; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java similarity index 89% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java index 46140c32f074a..3520a54cadeda 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import java.io.IOException; import java.nio.file.Path; @@ -6,9 +6,9 @@ import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.update.QuarkusUpdatesRepository.FetchResult; -import io.quarkus.devtools.project.update.operations.UpdatePropertyOperation; -import io.quarkus.devtools.project.update.operations.UpgradeGradlePluginOperation; +import io.quarkus.devtools.project.update.rewrite.QuarkusUpdatesRepository.FetchResult; +import io.quarkus.devtools.project.update.rewrite.operations.UpdatePropertyOperation; +import io.quarkus.devtools.project.update.rewrite.operations.UpgradeGradlePluginOperation; public final class QuarkusUpdates { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java similarity index 99% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java index b3f86eab45342..1758eba2497df 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import java.io.IOException; import java.io.InputStream; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java similarity index 88% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java index 1cd866a5ef55e..7e69d0e413720 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import java.util.Map; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java similarity index 87% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java index 8f6c63b65e4fd..ea47c75b415c6 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java @@ -1,9 +1,9 @@ -package io.quarkus.devtools.project.update.operations; +package io.quarkus.devtools.project.update.rewrite.operations; import java.util.Map; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.update.RewriteOperation; +import io.quarkus.devtools.project.update.rewrite.RewriteOperation; public class UpdateDependencyVersionOperation implements RewriteOperation { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java similarity index 89% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java index 4959ee7d2e832..172e077f0a1a2 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java @@ -1,9 +1,9 @@ -package io.quarkus.devtools.project.update.operations; +package io.quarkus.devtools.project.update.rewrite.operations; import java.util.Map; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.update.RewriteOperation; +import io.quarkus.devtools.project.update.rewrite.RewriteOperation; public class UpdateManagedDependencyVersionOperation implements RewriteOperation { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java similarity index 86% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java index 464f625ec42f3..0929c5a8780ae 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java @@ -1,9 +1,9 @@ -package io.quarkus.devtools.project.update.operations; +package io.quarkus.devtools.project.update.rewrite.operations; import java.util.Map; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.update.RewriteOperation; +import io.quarkus.devtools.project.update.rewrite.RewriteOperation; public class UpdatePropertyOperation implements RewriteOperation { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpgradeGradlePluginOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java similarity index 86% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpgradeGradlePluginOperation.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java index 2ad0899601f87..2fdcbb2a5d71f 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpgradeGradlePluginOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java @@ -1,9 +1,9 @@ -package io.quarkus.devtools.project.update.operations; +package io.quarkus.devtools.project.update.rewrite.operations; import java.util.Map; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.update.RewriteOperation; +import io.quarkus.devtools.project.update.rewrite.RewriteOperation; public class UpgradeGradlePluginOperation implements RewriteOperation { diff --git a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIOTest.java similarity index 86% rename from independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java rename to independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIOTest.java index 54b7b59986984..1c231d8cc9dc9 100644 --- a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java +++ b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIOTest.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; import static org.assertj.core.api.Assertions.assertThat; @@ -6,8 +6,8 @@ import org.junit.jupiter.api.Test; -import io.quarkus.devtools.project.update.operations.UpdateManagedDependencyVersionOperation; -import io.quarkus.devtools.project.update.operations.UpdatePropertyOperation; +import io.quarkus.devtools.project.update.rewrite.operations.UpdateManagedDependencyVersionOperation; +import io.quarkus.devtools.project.update.rewrite.operations.UpdatePropertyOperation; class QuarkusUpdateRecipeIOTest { diff --git a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepositoryTest.java b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepositoryTest.java similarity index 79% rename from independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepositoryTest.java rename to independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepositoryTest.java index 5e4dd6d4935d6..9915e9bdf0fda 100644 --- a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepositoryTest.java +++ b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepositoryTest.java @@ -1,6 +1,6 @@ -package io.quarkus.devtools.project.update; +package io.quarkus.devtools.project.update.rewrite; -import static io.quarkus.devtools.project.update.QuarkusUpdatesRepository.shouldApplyRecipe; +import static io.quarkus.devtools.project.update.rewrite.QuarkusUpdatesRepository.shouldApplyRecipe; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.params.ParameterizedTest; From 848a6f25e513c209e6a14e9caf3609f44b853ccf Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Fri, 16 Jun 2023 16:57:11 +0200 Subject: [PATCH 2/3] Check for min java version when updating (partial) --- .../main/java/io/quarkus/cli/CreateApp.java | 2 +- .../main/java/io/quarkus/cli/CreateCli.java | 2 +- .../quarkus/cli/create/BaseCreateCommand.java | 4 +- .../cli/create/TargetLanguageGroup.java | 10 +- .../gradle/tasks/QuarkusPlatformTask.java | 16 ++- .../io/quarkus/maven/CreateJBangMojo.java | 3 +- .../io/quarkus/maven/CreateProjectMojo.java | 3 +- .../quarkus/maven/QuarkusProjectMojoBase.java | 3 +- ...rkusJBangCodestartProjectInputBuilder.java | 4 +- .../devtools/commands/CreateJBangProject.java | 3 +- .../devtools/commands/CreateProject.java | 3 +- .../commands/CreateProjectHelper.java | 51 +-------- .../handlers/CreateProjectCommandHandler.java | 4 +- .../handlers/UpdateProjectCommandHandler.java | 18 +++- .../quarkus/devtools/project/JavaVersion.java | 101 ++++++++++++++++++ .../devtools/project/QuarkusProject.java | 17 ++- .../project/QuarkusProjectHelper.java | 19 ++-- .../{commands => project}/SourceType.java | 2 +- .../buildfile/MavenProjectBuildFile.java | 17 ++- .../update/ProjectExtensionsUpdateInfo.java | 10 ++ .../update/rewrite/QuarkusUpdates.java | 19 ++-- .../JavaVersionTest.java} | 14 +-- .../gradle/QuarkusPluginFunctionalTest.java | 4 +- 23 files changed, 224 insertions(+), 105 deletions(-) create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/JavaVersion.java rename independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/{commands => project}/SourceType.java (94%) rename independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/{commands/CreateProjectHelperTest.java => project/JavaVersionTest.java} (75%) diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java index b2f307e9aff95..7630ef8928d3b 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java @@ -12,7 +12,7 @@ import io.quarkus.cli.create.TargetGAVGroup; import io.quarkus.cli.create.TargetLanguageGroup; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; -import io.quarkus.devtools.commands.SourceType; +import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.handlers.CreateJBangProjectCommandHandler; import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java index b240342b504d5..d17c38360d252 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java @@ -12,7 +12,7 @@ import io.quarkus.cli.create.TargetGAVGroup; import io.quarkus.cli.create.TargetLanguageGroup; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; -import io.quarkus.devtools.commands.SourceType; +import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.handlers.CreateJBangProjectCommandHandler; import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java index ef87e60871c70..dc58dcbb4a7a4 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java @@ -1,6 +1,6 @@ package io.quarkus.cli.create; -import static io.quarkus.devtools.commands.CreateProjectHelper.computeJavaVersion; +import static io.quarkus.devtools.project.JavaVersion.computeJavaVersion; import java.nio.file.Path; import java.util.Collection; @@ -17,7 +17,7 @@ import io.quarkus.cli.registry.ToggleRegistryClientMixin; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; import io.quarkus.devtools.commands.CreateProjectHelper; -import io.quarkus.devtools.commands.SourceType; +import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java index c889f156f8f0a..69f0a830d6ccd 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java @@ -5,9 +5,9 @@ import java.util.stream.Collectors; import io.quarkus.cli.common.OutputOptionMixin; -import io.quarkus.devtools.commands.CreateProjectHelper; -import io.quarkus.devtools.commands.SourceType; +import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.ParameterException; @@ -17,13 +17,13 @@ public class TargetLanguageGroup { static class VersionCandidates extends ArrayList { VersionCandidates() { - super(CreateProjectHelper.JAVA_VERSIONS_LTS.stream().map(String::valueOf).collect(Collectors.toList())); + super(JavaVersion.JAVA_VERSIONS_LTS.stream().map(String::valueOf).collect(Collectors.toList())); } } @CommandLine.Option(names = { - "--java" }, description = "Target Java version.\n Valid values: ${COMPLETION-CANDIDATES}", completionCandidates = VersionCandidates.class, defaultValue = CreateProjectHelper.DETECT_JAVA_RUNTIME_VERSION) - String javaVersion = CreateProjectHelper.DETECT_JAVA_RUNTIME_VERSION; + "--java" }, description = "Target Java version.\n Valid values: ${COMPLETION-CANDIDATES}", completionCandidates = VersionCandidates.class, defaultValue = JavaVersion.DETECT_JAVA_RUNTIME_VERSION) + String javaVersion = JavaVersion.DETECT_JAVA_RUNTIME_VERSION; @CommandLine.Option(names = { "--kotlin" }, description = "Use Kotlin") boolean kotlin = false; diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java index 5bc34c46318bd..f71a6eadaeb0a 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java @@ -15,9 +15,12 @@ import org.gradle.api.GradleException; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.tasks.TaskCollection; +import org.gradle.api.tasks.compile.JavaCompile; import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.devtools.project.buildfile.BuildFile; @@ -118,7 +121,6 @@ protected String quarkusCoreVersion() { } protected QuarkusProject getQuarkusProject(boolean limitExtensionsToImportedPlatforms) { - final GradleMessageWriter log = messageWriter(); final ExtensionCatalog catalog = extensionsCatalog(limitExtensionsToImportedPlatforms, log); @@ -136,7 +138,17 @@ protected QuarkusProject getQuarkusProject(boolean limitExtensionsToImportedPlat throw new GradleException( "Mixed DSL is not supported. Both build and settings file need to use either Kotlin or Groovy DSL"); } - return QuarkusProjectHelper.getProject(getProject().getProjectDir().toPath(), catalog, buildFile, log); + final JavaVersion javaVersion = resolveProjectJavaVersion(); + return QuarkusProjectHelper.getProject(getProject().getProjectDir().toPath(), catalog, buildFile, javaVersion, log); + } + + private JavaVersion resolveProjectJavaVersion() { + TaskCollection compileTasks = getProject().getTasks().withType(JavaCompile.class); + if (compileTasks.isEmpty()) { + return JavaVersion.NA; + } + final JavaCompile task = compileTasks.iterator().next(); + return new JavaVersion(task.getTargetCompatibility()); } protected GradleMessageWriter messageWriter() { diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateJBangMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateJBangMojo.java index 0cf1c27dcf8d0..2a8e407bdd1f5 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateJBangMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateJBangMojo.java @@ -26,6 +26,7 @@ import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.maven.utilities.MojoUtils; @@ -117,7 +118,7 @@ public void execute() throws MojoExecutionException { .artifactResolver(mvn) .build(); final CreateJBangProject createJBangProject = new CreateJBangProject(QuarkusProject.of(projectDirPath, catalog, - codestartsResourceLoader, log, BuildTool.MAVEN)) + codestartsResourceLoader, log, BuildTool.MAVEN, new JavaVersion(javaVersion))) .extensions(extensions) .javaVersion(javaVersion) .setValue(NO_JBANG_WRAPPER, noJBangWrapper); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index eef16672f6636..15fd70e323906 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -37,6 +37,7 @@ import io.quarkus.devtools.commands.CreateProjectHelper; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.maven.components.MavenVersionEnforcer; @@ -297,7 +298,7 @@ public void execute() throws MojoExecutionException { .artifactResolver(mvn) .build(); QuarkusProject newProject = QuarkusProject.of(projectDirPath, catalog, - codestartsResourceLoader, log, buildToolEnum); + codestartsResourceLoader, log, buildToolEnum, new JavaVersion(javaVersion, javaVersion)); final CreateProject createProject = new CreateProject(newProject) .groupId(projectGroupId) .artifactId(projectArtifactId) diff --git a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java index 2e5ed5a3ae2dc..16043a4d5aa76 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java @@ -29,6 +29,7 @@ import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.devtools.project.buildfile.MavenProjectBuildFile; @@ -106,7 +107,7 @@ public void execute() throws MojoExecutionException { final List codestartsResourceLoader = getCodestartResourceLoaders(resolveExtensionCatalog()); quarkusProject = QuarkusProject.of(baseDir(), resolveExtensionCatalog(), codestartsResourceLoader, - log, buildTool); + log, buildTool, JavaVersion.NA); } doExecute(quarkusProject, getMessageWriter()); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartProjectInputBuilder.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartProjectInputBuilder.java index 1bd1336c59300..9295b72897373 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartProjectInputBuilder.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/codestarts/jbang/QuarkusJBangCodestartProjectInputBuilder.java @@ -9,8 +9,8 @@ import io.quarkus.devtools.codestarts.CodestartProjectInputBuilder; import io.quarkus.devtools.codestarts.DataKey; import io.quarkus.devtools.codestarts.utils.NestedMaps; -import io.quarkus.devtools.commands.CreateProjectHelper; import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.extensions.Extensions; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; @@ -86,7 +86,7 @@ public boolean noJBangWrapper() { public QuarkusJBangCodestartProjectInput build() { if (!this.containsData("java")) { this.addData(NestedMaps - .unflatten(Map.of("java.version", String.valueOf(CreateProjectHelper.determineBestJavaLtsVersion())))); + .unflatten(Map.of("java.version", String.valueOf(JavaVersion.determineBestJavaLtsVersion())))); } return new QuarkusJBangCodestartProjectInput(this); } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateJBangProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateJBangProject.java index 0d381591770e1..8d619685532d7 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateJBangProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateJBangProject.java @@ -1,6 +1,6 @@ package io.quarkus.devtools.commands; -import static io.quarkus.devtools.commands.CreateProjectHelper.computeJavaVersion; +import static io.quarkus.devtools.project.JavaVersion.computeJavaVersion; import static java.util.Objects.requireNonNull; import java.util.HashMap; @@ -14,6 +14,7 @@ import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.commands.handlers.CreateJBangProjectCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.SourceType; public class CreateJBangProject { public interface CreateJBangProjectKey { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProject.java index 72a53d5c95ce6..b26d321e9fed6 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProject.java @@ -1,9 +1,9 @@ package io.quarkus.devtools.commands; import static io.quarkus.devtools.commands.CreateProject.CreateProjectKey.*; -import static io.quarkus.devtools.commands.CreateProjectHelper.computeJavaVersion; import static io.quarkus.devtools.commands.handlers.CreateProjectCodestartDataConverter.PlatformPropertiesKey.QUARKUS_GRADLE_PLUGIN_VERSION; import static io.quarkus.devtools.commands.handlers.CreateProjectCodestartDataConverter.PlatformPropertiesKey.QUARKUS_MAVEN_PLUGIN_VERSION; +import static io.quarkus.devtools.project.JavaVersion.computeJavaVersion; import static java.util.Objects.requireNonNull; import java.util.Collections; @@ -20,6 +20,7 @@ import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.SourceType; import io.quarkus.platform.tools.ToolsUtils; /** diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProjectHelper.java index 3796b66238c5c..015eef38812f0 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/CreateProjectHelper.java @@ -11,10 +11,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.lang.model.SourceVersion; @@ -25,6 +21,7 @@ import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; +import io.quarkus.devtools.project.SourceType; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.paths.PathTree; import io.quarkus.registry.catalog.Extension; @@ -32,13 +29,6 @@ public class CreateProjectHelper { - // ordering is important here, so let's keep them ordered - public static final SortedSet JAVA_VERSIONS_LTS = new TreeSet<>(List.of(11, 17)); - public static final int DEFAULT_JAVA_VERSION = 11; - private static final int MAX_LTS_SUPPORTED_BY_KOTLIN = 17; - public static final String DETECT_JAVA_RUNTIME_VERSION = "<>"; - private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("(\\d+)(?:\\..*)?"); - public static final String DEFAULT_GROUP_ID = "org.acme"; public static final String DEFAULT_ARTIFACT_ID = "code-with-quarkus"; public static final String DEFAULT_VERSION = "1.0.0-SNAPSHOT"; @@ -177,45 +167,6 @@ public static Path createOutputDirectory(String targetDirectory) { return outputPath; } - public static String computeJavaVersion(SourceType sourceType, String inputJavaVersion) { - Integer javaFeatureVersionTarget = null; - - if (inputJavaVersion != null && !DETECT_JAVA_RUNTIME_VERSION.equals(inputJavaVersion)) { - // Probably too much as we should push only the feature version but let's be as thorough as we used to be - Matcher matcher = JAVA_VERSION_PATTERN.matcher(inputJavaVersion); - if (matcher.matches()) { - javaFeatureVersionTarget = Integer.valueOf(matcher.group(1)); - } - } - - if (javaFeatureVersionTarget == null) { - javaFeatureVersionTarget = Runtime.version().feature(); - } - - int bestJavaLtsVersion = determineBestJavaLtsVersion(javaFeatureVersionTarget); - - if (SourceType.KOTLIN.equals(sourceType) - && bestJavaLtsVersion > MAX_LTS_SUPPORTED_BY_KOTLIN) { - bestJavaLtsVersion = MAX_LTS_SUPPORTED_BY_KOTLIN; - } - return String.valueOf(bestJavaLtsVersion); - } - - public static int determineBestJavaLtsVersion() { - return determineBestJavaLtsVersion(Runtime.version().feature()); - } - - public static int determineBestJavaLtsVersion(int runtimeVersion) { - int bestLtsVersion = DEFAULT_JAVA_VERSION; - for (int ltsVersion : JAVA_VERSIONS_LTS) { - if (ltsVersion > runtimeVersion) { - break; - } - bestLtsVersion = ltsVersion; - } - return bestLtsVersion; - } - public static Set sanitizeExtensions(Set extensions) { if (extensions == null) { return extensions = Set.of(); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java index 33ed3c0048ca3..0711461f91d0f 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java @@ -20,13 +20,13 @@ import io.quarkus.devtools.codestarts.CodestartType; import io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog; import io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartProjectInput; -import io.quarkus.devtools.commands.CreateProjectHelper; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.commands.handlers.CreateProjectCodestartDataConverter.CatalogKey; import io.quarkus.devtools.messagewriter.MessageIcons; import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.extensions.Extensions; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.platform.tools.ToolsUtils; @@ -232,7 +232,7 @@ private static void addOrigins(final List extOrigins, Extensio private void checkMinimumJavaVersion(String javaVersionString, List extensions) throws QuarkusCommandException { final List incompatibleExtensions = new ArrayList<>(); - final int javaVersion = javaVersionString == null ? CreateProjectHelper.DEFAULT_JAVA_VERSION + final int javaVersion = javaVersionString == null ? JavaVersion.DEFAULT_JAVA_VERSION : Integer.parseInt(javaVersionString); for (Extension extension : extensions) { Integer extMinJavaVersion = getMinimumJavaVersion(extension); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index e206cfd755d0f..83f9d2574e8b5 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -43,10 +43,10 @@ public class UpdateProjectCommandHandler implements QuarkusCommandHandler { @Override public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + invocation.log().info("Detected project Java version: %s", invocation.getQuarkusProject().getJavaVersion()); final ApplicationModel appModel = invocation.getValue(UpdateProject.APP_MODEL); final ExtensionCatalog targetCatalog = invocation.getValue(UpdateProject.TARGET_CATALOG); final String targetPlatformVersion = invocation.getValue(UpdateProject.TARGET_PLATFORM_VERSION); - final boolean perModule = invocation.getValue(UpdateProject.PER_MODULE, false); final ProjectState currentState = resolveProjectState(appModel, invocation.getQuarkusProject().getExtensionsCatalog()); @@ -68,7 +68,8 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws currentState, recommendedState); - logUpdates(currentState, recommendedState, platformUpdateInfo, extensionsUpdateInfo, false, perModule, + logUpdates(invocation.getQuarkusProject(), currentState, recommendedState, platformUpdateInfo, extensionsUpdateInfo, + false, perModule, quarkusProject.log()); final boolean noRewrite = invocation.getValue(UpdateProject.NO_REWRITE, false); @@ -80,7 +81,8 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws buildTool, projectQuarkusPlatformBom.getVersion(), targetPlatformVersion, - kotlinVersion); + kotlinVersion, + extensionsUpdateInfo.getMinJavaVersion()); Path recipe = null; try { recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml"); @@ -130,7 +132,7 @@ private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentS return null; } - private static void logUpdates(ProjectState currentState, ProjectState recommendedState, + private static void logUpdates(QuarkusProject project, ProjectState currentState, ProjectState recommendedState, ProjectPlatformUpdateInfo platformUpdateInfo, ProjectExtensionsUpdateInfo extensionsUpdateInfo, boolean recommendState, boolean perModule, MessageWriter log) { @@ -151,7 +153,6 @@ private static void logUpdates(ProjectState currentState, ProjectState recommend ProjectInfoCommandHandler.logState(recommendedState, perModule, false, log); return; } - if (platformUpdateInfo.isPlatformUpdatesAvailable()) { log.info("Recommended Quarkus platform BOM updates:"); if (!platformUpdateInfo.getImportVersionUpdates().isEmpty()) { @@ -235,6 +236,13 @@ private static void logUpdates(ProjectState currentState, ProjectState recommend log.info(""); } } + if (extensionsUpdateInfo.getMinJavaVersion().isPresent()) { + final int extensionsMinJavaVersion = extensionsUpdateInfo.getMinJavaVersion().getAsInt(); + if (extensionsMinJavaVersion > project.getJavaVersion().getAsInt()) { + log.warn("We detected that some of the updated extensions require an update of the Java version to: ", + extensionsMinJavaVersion); + } + } } @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/JavaVersion.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/JavaVersion.java new file mode 100644 index 0000000000000..edff9ab037a05 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/JavaVersion.java @@ -0,0 +1,101 @@ +package io.quarkus.devtools.project; + +import java.util.List; +import java.util.Objects; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class JavaVersion { + + public static final JavaVersion NA = new JavaVersion(); + + private final String version; + + private JavaVersion() { + this(null); + } + + public JavaVersion(String version) { + this.version = version; + } + + public boolean isEmpty() { + return version == null; + } + + public String getVersion() { + return version; + } + + public int getAsInt() { + return Integer.parseInt(version); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + JavaVersion that = (JavaVersion) o; + return Objects.equals(version, that.version); + } + + @Override + public int hashCode() { + return Objects.hash(version); + } + + @Override + public String toString() { + return version; + } + + // ordering is important here, so let's keep them ordered + public static final SortedSet JAVA_VERSIONS_LTS = new TreeSet<>(List.of(11, 17)); + public static final int DEFAULT_JAVA_VERSION = 11; + public static final int MAX_LTS_SUPPORTED_BY_KOTLIN = 17; + public static final String DETECT_JAVA_RUNTIME_VERSION = "<>"; + public static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("(\\d+)(?:\\..*)?"); + + public static int determineBestJavaLtsVersion() { + return determineBestJavaLtsVersion(Runtime.version().feature()); + } + + public static int determineBestJavaLtsVersion(int runtimeVersion) { + int bestLtsVersion = DEFAULT_JAVA_VERSION; + for (int ltsVersion : JAVA_VERSIONS_LTS) { + if (ltsVersion > runtimeVersion) { + break; + } + bestLtsVersion = ltsVersion; + } + return bestLtsVersion; + } + + public static String computeJavaVersion(SourceType sourceType, String inputJavaVersion) { + Integer javaFeatureVersionTarget = null; + + if (inputJavaVersion != null && !DETECT_JAVA_RUNTIME_VERSION.equals(inputJavaVersion)) { + // Probably too much as we should push only the feature version but let's be as thorough as we used to be + Matcher matcher = JAVA_VERSION_PATTERN.matcher(inputJavaVersion); + if (matcher.matches()) { + javaFeatureVersionTarget = Integer.valueOf(matcher.group(1)); + } + } + + if (javaFeatureVersionTarget == null) { + javaFeatureVersionTarget = Runtime.version().feature(); + } + + int bestJavaLtsVersion = determineBestJavaLtsVersion(javaFeatureVersionTarget); + + if (SourceType.KOTLIN.equals(sourceType) + && bestJavaLtsVersion > MAX_LTS_SUPPORTED_BY_KOTLIN) { + bestJavaLtsVersion = MAX_LTS_SUPPORTED_BY_KOTLIN; + } + return String.valueOf(bestJavaLtsVersion); + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java index 153aa5b1d6329..73a7da4518648 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java @@ -18,27 +18,30 @@ public final class QuarkusProject { private final ExtensionManager extensionManager; private final MessageWriter log; + private final JavaVersion javaVersion; + private QuarkusProject(Path projectDirPath, ExtensionCatalog catalog, List codestartResourceLoaders, MessageWriter log, - ExtensionManager extensionManager) { + ExtensionManager extensionManager, JavaVersion javaVersion) { this.projectDirPath = requireNonNull(projectDirPath, "projectDirPath is required"); this.catalog = requireNonNull(catalog, "catalog is required"); this.codestartResourceLoaders = requireNonNull(codestartResourceLoaders, "codestartResourceLoaders is required"); this.extensionManager = requireNonNull(extensionManager, "extensionManager is required"); this.log = (log == null ? MessageWriter.info() : log); + this.javaVersion = javaVersion; } public static QuarkusProject of(final Path projectDirPath, final ExtensionCatalog catalog, final List codestartResourceLoaders, final MessageWriter log, - final ExtensionManager extensionManager) { - return new QuarkusProject(projectDirPath, catalog, codestartResourceLoaders, log, extensionManager); + final ExtensionManager extensionManager, JavaVersion javaVersion) { + return new QuarkusProject(projectDirPath, catalog, codestartResourceLoaders, log, extensionManager, javaVersion); } public static QuarkusProject of(final Path projectDirPath, ExtensionCatalog catalog, final List codestartsResourceLoader, - final MessageWriter log, final BuildTool buildTool) { + final MessageWriter log, final BuildTool buildTool, JavaVersion javaVersion) { return new QuarkusProject(projectDirPath, catalog, codestartsResourceLoader, log, - buildTool.createExtensionManager(projectDirPath, catalog)); + buildTool.createExtensionManager(projectDirPath, catalog), javaVersion); } public Path getProjectDirPath() { @@ -49,6 +52,10 @@ public BuildTool getBuildTool() { return extensionManager.getBuildTool(); } + public JavaVersion getJavaVersion() { + return javaVersion; + } + public ExtensionManager getExtensionManager() { return extensionManager; } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java index 09502599e3ee4..d559cead7f548 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java @@ -23,6 +23,7 @@ public class QuarkusProjectHelper { private static ExtensionCatalogResolver catalogResolver; private static boolean registryClientEnabled; + static { initRegistryClientEnabled(); } @@ -66,7 +67,8 @@ public static QuarkusProject getProject(Path projectDir, BuildTool buildTool, St // TODO remove this method once the default registry becomes available return QuarkusProjectHelper.getProject(projectDir, getExtensionCatalog(quarkusVersion), - buildTool); + buildTool, + JavaVersion.NA); } @Deprecated @@ -100,21 +102,23 @@ public static QuarkusProject getProject(Path projectDir, BuildTool buildTool) { throw new RuntimeException("Failed to resolve the Quarkus extension catalog", e); } - return getProject(projectDir, catalog, buildTool, messageWriter()); + return getProject(projectDir, catalog, buildTool, JavaVersion.NA, messageWriter()); } - public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, BuildTool buildTool) { - return getProject(projectDir, catalog, buildTool, messageWriter()); + public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, BuildTool buildTool, + JavaVersion javaVersion) { + return getProject(projectDir, catalog, buildTool, javaVersion, messageWriter()); } public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, BuildTool buildTool, + JavaVersion javaVersion, MessageWriter log) { return QuarkusProject.of(projectDir, catalog, getCodestartResourceLoaders(catalog), - log, buildTool); + log, buildTool, javaVersion); } public static QuarkusProject getProject(Path projectDir, ExtensionManager extManager) throws RegistryResolutionException { - return getProject(projectDir, resolveExtensionCatalog(), extManager, messageWriter()); + return getProject(projectDir, resolveExtensionCatalog(), extManager, JavaVersion.NA, messageWriter()); } public static ExtensionCatalog resolveExtensionCatalog() throws RegistryResolutionException { @@ -122,9 +126,10 @@ public static ExtensionCatalog resolveExtensionCatalog() throws RegistryResoluti } public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, ExtensionManager extManager, + JavaVersion javaVersion, MessageWriter log) { return QuarkusProject.of(projectDir, catalog, getCodestartResourceLoaders(catalog), - log, extManager); + log, extManager, javaVersion); } public static ExtensionCatalogResolver getCatalogResolver() throws RegistryResolutionException { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/SourceType.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/SourceType.java similarity index 94% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/SourceType.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/SourceType.java index 7d1e56802cb49..3a25f39f205ab 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/SourceType.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/SourceType.java @@ -1,4 +1,4 @@ -package io.quarkus.devtools.commands; +package io.quarkus.devtools.project; import java.util.Collection; import java.util.Map; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java index 9758736e8e320..cc0a2169469c6 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java @@ -31,6 +31,7 @@ import io.quarkus.bootstrap.resolver.maven.workspace.ModelUtils; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.maven.dependency.ArtifactCoords; @@ -122,8 +123,22 @@ public static QuarkusProject getProject(Artifact projectPom, Model projectModel, projectModel, deps, managedDeps, projectProps, projectPom == null ? null : artifactResolver); final List codestartResourceLoaders = codestartLoadersBuilder().catalog(extensionCatalog) .artifactResolver(artifactResolver).build(); + final JavaVersion javaVersion = resolveJavaVersion(projectProps); return QuarkusProject.of(projectDir, extensionCatalog, - codestartResourceLoaders, log, extensionManager); + codestartResourceLoaders, log, extensionManager, javaVersion); + } + + private static JavaVersion resolveJavaVersion(Properties projectProps) { + if (projectProps.containsKey("maven.compiler.release")) { + return new JavaVersion(projectProps.getProperty("maven.compiler.release")); + } + if (projectProps.containsKey("maven.compiler.target")) { + return new JavaVersion(projectProps.getProperty("maven.compiler.target")); + } + if (projectProps.containsKey("maven.compiler.source")) { + return new JavaVersion(projectProps.getProperty("maven.compiler.source")); + } + return JavaVersion.NA; } private static MavenArtifactResolver getMavenResolver(Path projectDir) { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java index e4cb0c6445605..a9e488a8bdb88 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java @@ -2,8 +2,11 @@ import java.util.List; import java.util.Map; +import java.util.OptionalInt; +import java.util.stream.Stream; import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.platform.catalog.processor.ExtensionProcessor; public class ProjectExtensionsUpdateInfo { final Map> versionedManagedExtensions; @@ -36,6 +39,13 @@ public Map> getNonPlatformExtensions() { return nonPlatformExtensions; } + public OptionalInt getMinJavaVersion() { + return Stream.concat(getVersionedManagedExtensions().values().stream(), getNonPlatformExtensions().values().stream()) + .flatMap(List::stream) + .mapToInt(e -> ExtensionProcessor.getMinimumJavaVersion(e.getRecommendedMetadata())) + .max(); + } + public boolean isEmpty() { return versionedManagedExtensions.isEmpty() && removedExtensions.isEmpty() diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java index 3520a54cadeda..8321adc4813e4 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.OptionalInt; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.messagewriter.MessageWriter; @@ -52,20 +53,24 @@ public static FetchResult createRecipe(MessageWriter log, Path target, MavenArti public static class ProjectUpdateRequest { - public BuildTool buildTool; - public String currentVersion; - public String targetVersion; - public String kotlinVersion; + public final BuildTool buildTool; + public final String currentVersion; + public final String targetVersion; + public final String kotlinVersion; + public final OptionalInt minJavaVersion; - public ProjectUpdateRequest(String currentVersion, String targetVersion, String kotlinVersion) { - this(BuildTool.MAVEN, currentVersion, targetVersion, kotlinVersion); + public ProjectUpdateRequest(String currentVersion, String targetVersion, String kotlinVersion, + OptionalInt minJavaVersion) { + this(BuildTool.MAVEN, currentVersion, targetVersion, kotlinVersion, minJavaVersion); } - public ProjectUpdateRequest(BuildTool buildTool, String currentVersion, String targetVersion, String kotlinVersion) { + public ProjectUpdateRequest(BuildTool buildTool, String currentVersion, String targetVersion, String kotlinVersion, + OptionalInt minJavaVersion) { this.buildTool = buildTool; this.currentVersion = currentVersion; this.targetVersion = targetVersion; this.kotlinVersion = kotlinVersion; + this.minJavaVersion = minJavaVersion; } } } diff --git a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/commands/CreateProjectHelperTest.java b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/JavaVersionTest.java similarity index 75% rename from independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/commands/CreateProjectHelperTest.java rename to independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/JavaVersionTest.java index bb7a7774ed9c3..285a582e81b2c 100644 --- a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/commands/CreateProjectHelperTest.java +++ b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/JavaVersionTest.java @@ -1,15 +1,15 @@ -package io.quarkus.devtools.commands; +package io.quarkus.devtools.project; -import static io.quarkus.devtools.commands.CreateProjectHelper.DETECT_JAVA_RUNTIME_VERSION; -import static io.quarkus.devtools.commands.CreateProjectHelper.computeJavaVersion; -import static io.quarkus.devtools.commands.CreateProjectHelper.determineBestJavaLtsVersion; -import static io.quarkus.devtools.commands.SourceType.JAVA; -import static io.quarkus.devtools.commands.SourceType.KOTLIN; +import static io.quarkus.devtools.project.JavaVersion.DETECT_JAVA_RUNTIME_VERSION; +import static io.quarkus.devtools.project.JavaVersion.computeJavaVersion; +import static io.quarkus.devtools.project.JavaVersion.determineBestJavaLtsVersion; +import static io.quarkus.devtools.project.SourceType.JAVA; +import static io.quarkus.devtools.project.SourceType.KOTLIN; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; -class CreateProjectHelperTest { +class JavaVersionTest { @Test public void givenJavaVersion17ShouldReturn17() { diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java index a9a98ad034e3c..725aa50bb269c 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java @@ -1,6 +1,6 @@ package io.quarkus.gradle; -import static io.quarkus.devtools.commands.SourceType.JAVA; +import static io.quarkus.devtools.project.SourceType.JAVA; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; @@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableMap; import io.quarkus.devtools.commands.CreateProject; -import io.quarkus.devtools.commands.SourceType; +import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.test.devmode.util.DevModeTestUtils; From 0d28057de8d9c1a8b6ade5dad3ef41eebbcd78bd Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Mon, 19 Jun 2023 14:47:57 +0200 Subject: [PATCH 3/3] Update java version from recipe --- .../main/java/io/quarkus/cli/CreateApp.java | 2 +- .../main/java/io/quarkus/cli/CreateCli.java | 2 +- .../quarkus/cli/create/BaseCreateCommand.java | 2 +- .../cli/create/TargetLanguageGroup.java | 2 +- .../cli/registry/RegistryClientMixin.java | 3 +- .../io/quarkus/maven/CreateProjectMojo.java | 2 +- .../handlers/UpdateProjectCommandHandler.java | 54 ++++++++++--------- .../project/QuarkusProjectHelper.java | 4 ++ .../project/update/ExtensionMapBuilder.java | 3 +- .../update/ProjectExtensionsUpdateInfo.java | 37 +++++++++---- .../project/update/ProjectUpdateInfos.java | 16 ++++-- .../update/rewrite/QuarkusUpdateRecipeIO.java | 2 +- .../update/rewrite/QuarkusUpdates.java | 18 ++++--- .../update/rewrite/RewriteOperation.java | 10 +++- .../UpdateDependencyVersionOperation.java | 2 +- .../UpdateJavaVersionOperation.java | 36 +++++++++++++ ...dateManagedDependencyVersionOperation.java | 2 +- .../operations/UpdatePropertyOperation.java | 6 +-- .../UpgradeGradlePluginOperation.java | 6 +-- .../gradle/QuarkusPluginFunctionalTest.java | 2 +- 20 files changed, 144 insertions(+), 67 deletions(-) create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateJavaVersionOperation.java diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java index 7630ef8928d3b..09adc3e85a1f0 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java @@ -12,11 +12,11 @@ import io.quarkus.cli.create.TargetGAVGroup; import io.quarkus.cli.create.TargetLanguageGroup; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; -import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.handlers.CreateJBangProjectCommandHandler; import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.SourceType; import picocli.CommandLine; @CommandLine.Command(name = "app", header = "Create a Quarkus application project.", description = "%n" diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java index d17c38360d252..70fcd130c31fb 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java @@ -12,11 +12,11 @@ import io.quarkus.cli.create.TargetGAVGroup; import io.quarkus.cli.create.TargetLanguageGroup; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; -import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.handlers.CreateJBangProjectCommandHandler; import io.quarkus.devtools.commands.handlers.CreateProjectCommandHandler; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.SourceType; import picocli.CommandLine; @CommandLine.Command(name = "cli", header = "Create a Quarkus command-line project.", description = "%n" diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java index dc58dcbb4a7a4..439b135dce1cb 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java @@ -17,10 +17,10 @@ import io.quarkus.cli.registry.ToggleRegistryClientMixin; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; import io.quarkus.devtools.commands.CreateProjectHelper; -import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.SourceType; import io.quarkus.registry.RegistryResolutionException; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java index 69f0a830d6ccd..92cc237c0c486 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/TargetLanguageGroup.java @@ -5,9 +5,9 @@ import java.util.stream.Collectors; import io.quarkus.cli.common.OutputOptionMixin; -import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.JavaVersion; +import io.quarkus.devtools.project.SourceType; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.ParameterException; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java index 635a851c27ec4..7739f49d588f5 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java @@ -9,6 +9,7 @@ import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.devtools.commands.CreateProjectHelper; import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.maven.dependency.ArtifactCoords; @@ -62,7 +63,7 @@ public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusPlatfo + "Use the maven/gradle plugins when working with Quarkus 1.x projects."); } catalog = CreateProjectHelper.completeCatalog(catalog, extensions, QuarkusProjectHelper.artifactResolver()); - return QuarkusProjectHelper.getProject(projectRoot, catalog, buildTool, log); + return QuarkusProjectHelper.getProject(projectRoot, catalog, buildTool, JavaVersion.NA, log); } ExtensionCatalog getExtensionCatalog(TargetQuarkusPlatformGroup targetVersion, OutputOptionMixin log) diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index 15fd70e323906..254dded675ed4 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -298,7 +298,7 @@ public void execute() throws MojoExecutionException { .artifactResolver(mvn) .build(); QuarkusProject newProject = QuarkusProject.of(projectDirPath, catalog, - codestartsResourceLoader, log, buildToolEnum, new JavaVersion(javaVersion, javaVersion)); + codestartsResourceLoader, log, buildToolEnum, new JavaVersion(javaVersion)); final CreateProject createProject = new CreateProject(newProject) .groupId(projectGroupId) .artifactId(projectArtifactId) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index 83f9d2574e8b5..2f8ce063e2ce7 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -11,6 +11,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.devtools.commands.UpdateProject; @@ -76,13 +78,20 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws if (!noRewrite) { final BuildTool buildTool = quarkusProject.getExtensionManager().getBuildTool(); String kotlinVersion = getMetadata(targetCatalog, "project", "properties", "kotlin-version"); - + final OptionalInt minJavaVersion = extensionsUpdateInfo.getMinJavaVersion(); + final Optional updateJavaVersion; + if (minJavaVersion.isPresent() + && minJavaVersion.getAsInt() > invocation.getQuarkusProject().getJavaVersion().getAsInt()) { + updateJavaVersion = Optional.of(minJavaVersion.getAsInt()); + } else { + updateJavaVersion = Optional.empty(); + } QuarkusUpdates.ProjectUpdateRequest request = new QuarkusUpdates.ProjectUpdateRequest( buildTool, projectQuarkusPlatformBom.getVersion(), targetPlatformVersion, kotlinVersion, - extensionsUpdateInfo.getMinJavaVersion()); + updateJavaVersion); Path recipe = null; try { recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml"); @@ -91,6 +100,7 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws final QuarkusUpdatesRepository.FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), recipe, QuarkusProjectHelper.artifactResolver(), buildTool, updateRecipesVersion, request); + invocation.log().info("OpenRewrite recipe generated: %s", recipe); final String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION, fetchResult.getRewritePluginVersion()); final boolean rewriteDryRun = invocation.getValue(UpdateProject.REWRITE_DRY_RUN, false); @@ -108,15 +118,6 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws throw new QuarkusCommandException("Error while generating the project update script", e); } catch (QuarkusUpdateException e) { throw new QuarkusCommandException("Error while running the project update script", e); - } finally { - if (recipe != null) { - try { - Files.deleteIfExists(recipe); - } catch (IOException e) { - // ignore - } - } - } } } @@ -179,13 +180,19 @@ private static void logUpdates(QuarkusProject project, ProjectState currentState log.info(""); } - if (extensionsUpdateInfo.isEmpty()) { - if (!platformUpdateInfo.isPlatformUpdatesAvailable()) { - log.info("The project is up-to-date"); - } + if (extensionsUpdateInfo.isUpToDate() && !platformUpdateInfo.isPlatformUpdatesAvailable()) { + log.info("The project is up-to-date"); return; } + if (extensionsUpdateInfo.getMinJavaVersion().isPresent()) { + final Integer extensionsMinJavaVersion = extensionsUpdateInfo.getMinJavaVersion().getAsInt(); + if (extensionsMinJavaVersion > project.getJavaVersion().getAsInt()) { + log.warn("We detected that some of the updated extensions require an update of the Java version to: %s", + extensionsMinJavaVersion); + } + } + for (PlatformInfo platform : platformUpdateInfo.getPlatformImports().values()) { final String provider = platform.getRecommendedProviderKey(); if (!extensionsUpdateInfo.getVersionedManagedExtensions().containsKey(provider) @@ -202,14 +209,15 @@ private static void logUpdates(QuarkusProject project, ProjectState currentState sb.append(" -> remove version (managed)"); log.info(sb.toString()); } - for (ArtifactCoords e : extensionsUpdateInfo.getAddedExtensions().getOrDefault(provider, Collections.emptyList())) { + for (ExtensionUpdateInfo i : extensionsUpdateInfo.getAddedExtensions().getOrDefault(provider, + Collections.emptyList())) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.ADD, - e.getKey().toGacString())); + i.getRecommendedDependency().getKey().toGacString())); } - for (ArtifactCoords e : extensionsUpdateInfo.getRemovedExtensions().getOrDefault(provider, + for (ExtensionUpdateInfo i : extensionsUpdateInfo.getRemovedExtensions().getOrDefault(provider, Collections.emptyList())) { log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, UpdateProjectCommandHandler.REMOVE, - e.getKey().toGacString())); + i.getCurrentDep().getKey().toGacString())); } log.info(""); } @@ -236,13 +244,7 @@ private static void logUpdates(QuarkusProject project, ProjectState currentState log.info(""); } } - if (extensionsUpdateInfo.getMinJavaVersion().isPresent()) { - final int extensionsMinJavaVersion = extensionsUpdateInfo.getMinJavaVersion().getAsInt(); - if (extensionsMinJavaVersion > project.getJavaVersion().getAsInt()) { - log.warn("We detected that some of the updated extensions require an update of the Java version to: ", - extensionsMinJavaVersion); - } - } + } @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java index d559cead7f548..bf50251b319e1 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java @@ -110,6 +110,10 @@ public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalo return getProject(projectDir, catalog, buildTool, javaVersion, messageWriter()); } + public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, BuildTool buildTool) { + return getProject(projectDir, catalog, buildTool, JavaVersion.NA, messageWriter()); + } + public static QuarkusProject getProject(Path projectDir, ExtensionCatalog catalog, BuildTool buildTool, JavaVersion javaVersion, MessageWriter log) { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java index e91c09f14670b..d3b98c4af43ac 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ExtensionMapBuilder.java @@ -87,7 +87,8 @@ public ExtensionUpdateInfoBuilder setRecommendedDep(TopExtensionDependency recom } public ExtensionUpdateInfo build() { - return new ExtensionUpdateInfo(currentDep, currentDep.getCatalogMetadata(), resolveRecommendedDep()); + final TopExtensionDependency effectiveRecommendedDep = resolveRecommendedDep(); + return new ExtensionUpdateInfo(currentDep, effectiveRecommendedDep.getCatalogMetadata(), effectiveRecommendedDep); } public TopExtensionDependency resolveRecommendedDep() { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java index a9e488a8bdb88..b25631f6642d9 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectExtensionsUpdateInfo.java @@ -1,37 +1,47 @@ package io.quarkus.devtools.project.update; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.OptionalInt; import java.util.stream.Stream; -import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.devtools.project.JavaVersion; import io.quarkus.platform.catalog.processor.ExtensionProcessor; public class ProjectExtensionsUpdateInfo { + private final Map> managedExtensions; final Map> versionedManagedExtensions; - final Map> removedExtensions; - final Map> addedExtensions; + final Map> removedExtensions; + final Map> addedExtensions; final Map> nonPlatformExtensions; - public ProjectExtensionsUpdateInfo(Map> versionedManagedExtensions, - Map> removedExtensions, Map> addedExtensions, + public ProjectExtensionsUpdateInfo(Map> managedExtensions, + Map> versionedManagedExtensions, + Map> removedExtensions, + Map> addedExtensions, Map> nonPlatformExtensionUpdate) { + this.managedExtensions = managedExtensions; this.versionedManagedExtensions = versionedManagedExtensions; this.removedExtensions = removedExtensions; this.addedExtensions = addedExtensions; this.nonPlatformExtensions = nonPlatformExtensionUpdate; } + public Map> getManagedExtensions() { + return managedExtensions; + } + public Map> getVersionedManagedExtensions() { return versionedManagedExtensions; } - public Map> getRemovedExtensions() { + public Map> getRemovedExtensions() { return removedExtensions; } - public Map> getAddedExtensions() { + public Map> getAddedExtensions() { return addedExtensions; } @@ -40,13 +50,18 @@ public Map> getNonPlatformExtensions() { } public OptionalInt getMinJavaVersion() { - return Stream.concat(getVersionedManagedExtensions().values().stream(), getNonPlatformExtensions().values().stream()) - .flatMap(List::stream) - .mapToInt(e -> ExtensionProcessor.getMinimumJavaVersion(e.getRecommendedMetadata())) + return Stream.of(getManagedExtensions().values(), + getVersionedManagedExtensions().values(), + getNonPlatformExtensions().values(), + getAddedExtensions().values()) + .flatMap(Collection::stream) + .flatMap(Collection::stream) + .mapToInt(e -> Optional.ofNullable(ExtensionProcessor.getMinimumJavaVersion(e.getRecommendedMetadata())) + .orElse(JavaVersion.DEFAULT_JAVA_VERSION)) .max(); } - public boolean isEmpty() { + public boolean isUpToDate() { return versionedManagedExtensions.isEmpty() && removedExtensions.isEmpty() && addedExtensions.isEmpty() diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java index 34443d1a17209..bb3d978504fdf 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/ProjectUpdateInfos.java @@ -43,9 +43,10 @@ public static ProjectExtensionsUpdateInfo resolveExtensionsUpdateInfo(ProjectSta info.setRecommendedDep(dep); } } + final Map> managedExtensions = new LinkedHashMap<>(0); final Map> versionedManagedExtensions = new LinkedHashMap<>(0); - final Map> removedExtensions = new LinkedHashMap<>(0); - final Map> addedExtensions = new LinkedHashMap<>(0); + final Map> removedExtensions = new LinkedHashMap<>(0); + final Map> addedExtensions = new LinkedHashMap<>(0); final Map> nonPlatformExtensionUpdates = new LinkedHashMap<>(); for (ExtensionUpdateInfoBuilder infoBuilder : extensionInfo.values()) { final ExtensionUpdateInfo info = infoBuilder.build(); @@ -55,14 +56,14 @@ public static ProjectExtensionsUpdateInfo resolveExtensionsUpdateInfo(ProjectSta if (!info.getCurrentDep().getKey().equals(info.getRecommendedDependency().getKey())) { if (info.getCurrentDep().isPlatformExtension()) { removedExtensions.computeIfAbsent(info.getCurrentDep().getProviderKey(), k -> new ArrayList<>()) - .add(info.getCurrentDep().getArtifact()); + .add(new ExtensionUpdateInfo(info.getCurrentDep(), null, null)); } else { nonPlatformExtensionUpdates.computeIfAbsent(info.getCurrentDep().getProviderKey(), k -> new ArrayList<>()) .add(info); } if (info.getRecommendedDependency().isPlatformExtension()) { addedExtensions.computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) - .add(info.getRecommendedDependency().getArtifact()); + .add(new ExtensionUpdateInfo(null, info.getRecommendedMetadata(), info.getRecommendedDependency())); } else { nonPlatformExtensionUpdates .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) @@ -73,13 +74,18 @@ public static ProjectExtensionsUpdateInfo resolveExtensionsUpdateInfo(ProjectSta versionedManagedExtensions .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) .add(info); + } else { + managedExtensions + .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()) + .add(info); } } else if (!info.getCurrentDep().getVersion().equals(info.getRecommendedDependency().getVersion())) { nonPlatformExtensionUpdates .computeIfAbsent(info.getRecommendedDependency().getProviderKey(), k -> new ArrayList<>()).add(info); } } - return new ProjectExtensionsUpdateInfo(versionedManagedExtensions, removedExtensions, addedExtensions, + return new ProjectExtensionsUpdateInfo(managedExtensions, versionedManagedExtensions, removedExtensions, + addedExtensions, nonPlatformExtensionUpdates); } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java index c6f6485ddb0d0..0588ab47baba8 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateRecipeIO.java @@ -47,7 +47,7 @@ static String toYaml(QuarkusUpdateRecipe recipe) { q.putAll(QuarkusUpdateRecipe.QUARKUS_RECIPE); List recipeList = new ArrayList<>(); for (RewriteOperation o : recipe.getOperations()) { - recipeList.add(o.toMap(recipe.getBuildTool())); + recipeList.addAll(o.multi(recipe.getBuildTool())); } recipeList.addAll(recipe.getOtherRecipeNames()); q.put(QuarkusUpdateRecipe.RECIPE_LIST_KEY, recipeList); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java index 8321adc4813e4..b5c45ab54e708 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java @@ -2,12 +2,13 @@ import java.io.IOException; import java.nio.file.Path; -import java.util.OptionalInt; +import java.util.Optional; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.update.rewrite.QuarkusUpdatesRepository.FetchResult; +import io.quarkus.devtools.project.update.rewrite.operations.UpdateJavaVersionOperation; import io.quarkus.devtools.project.update.rewrite.operations.UpdatePropertyOperation; import io.quarkus.devtools.project.update.rewrite.operations.UpgradeGradlePluginOperation; @@ -25,7 +26,10 @@ public static FetchResult createRecipe(MessageWriter log, Path target, MavenArti request.targetVersion); QuarkusUpdateRecipe recipe = new QuarkusUpdateRecipe() .buildTool(request.buildTool); - + if (request.updateJavaVersion.isPresent()) { + final String javaVersion = request.updateJavaVersion.get().toString(); + recipe.addOperation(new UpdateJavaVersionOperation(javaVersion)); + } switch (request.buildTool) { case MAVEN: recipe.addOperation(new UpdatePropertyOperation("quarkus.platform.version", request.targetVersion)) @@ -57,20 +61,20 @@ public static class ProjectUpdateRequest { public final String currentVersion; public final String targetVersion; public final String kotlinVersion; - public final OptionalInt minJavaVersion; + public final Optional updateJavaVersion; public ProjectUpdateRequest(String currentVersion, String targetVersion, String kotlinVersion, - OptionalInt minJavaVersion) { - this(BuildTool.MAVEN, currentVersion, targetVersion, kotlinVersion, minJavaVersion); + Optional updateJavaVersion) { + this(BuildTool.MAVEN, currentVersion, targetVersion, kotlinVersion, updateJavaVersion); } public ProjectUpdateRequest(BuildTool buildTool, String currentVersion, String targetVersion, String kotlinVersion, - OptionalInt minJavaVersion) { + Optional updateJavaVersion) { this.buildTool = buildTool; this.currentVersion = currentVersion; this.targetVersion = targetVersion; this.kotlinVersion = kotlinVersion; - this.minJavaVersion = minJavaVersion; + this.updateJavaVersion = updateJavaVersion; } } } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java index 7e69d0e413720..e0557a7aa5b8e 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/RewriteOperation.java @@ -1,5 +1,6 @@ package io.quarkus.devtools.project.update.rewrite; +import java.util.List; import java.util.Map; import io.quarkus.devtools.project.BuildTool; @@ -17,5 +18,12 @@ public interface RewriteOperation { * @param buildTool * @return */ - Map toMap(BuildTool buildTool); + default Map single(BuildTool buildTool) { + return Map.of(); + } + + default List> multi(BuildTool buildTool) { + return List.of(single(buildTool)); + } + } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java index ea47c75b415c6..18c6f1c58741b 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateDependencyVersionOperation.java @@ -18,7 +18,7 @@ public UpdateDependencyVersionOperation(String groupId, String artifactId, Strin } @Override - public Map toMap(BuildTool buildTool) { + public Map single(BuildTool buildTool) { switch (buildTool) { case MAVEN: return Map.of("org.openrewrite.maven.UpgradeDependencyVersion", diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateJavaVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateJavaVersionOperation.java new file mode 100644 index 0000000000000..ffdd9e194d3a2 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateJavaVersionOperation.java @@ -0,0 +1,36 @@ +package io.quarkus.devtools.project.update.rewrite.operations; + +import java.util.List; +import java.util.Map; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.update.rewrite.RewriteOperation; + +public class UpdateJavaVersionOperation implements RewriteOperation { + + private final String newVersion; + + public UpdateJavaVersionOperation(String newVersion) { + this.newVersion = newVersion; + } + + @Override + public List> multi(BuildTool buildTool) { + switch (buildTool) { + case MAVEN: + return List.of( + Map.of("org.openrewrite.maven.ChangePropertyValue", + Map.of("key", "maven.compiler.source", "newValue", newVersion)), + Map.of("org.openrewrite.maven.ChangePropertyValue", + Map.of("key", "maven.compiler.target", "newValue", newVersion)), + Map.of("org.openrewrite.maven.ChangePropertyValue", + Map.of("key", "maven.compiler.release", "newValue", newVersion))); + case GRADLE: + return List.of(Map.of( + "org.openrewrite.gradle.UpdateJavaCompatibility", + Map.of("version", newVersion))); + default: + return List.of(); + } + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java index 172e077f0a1a2..1d1dc8677ae9d 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdateManagedDependencyVersionOperation.java @@ -18,7 +18,7 @@ public UpdateManagedDependencyVersionOperation(String groupId, String artifactId } @Override - public Map toMap(BuildTool buildTool) { + public Map single(BuildTool buildTool) { switch (buildTool) { case MAVEN: return Map.of("org.openrewrite.maven.ChangeManagedDependencyGroupIdAndArtifactId", diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java index 0929c5a8780ae..4a8cc0f65c2d3 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpdatePropertyOperation.java @@ -7,8 +7,8 @@ public class UpdatePropertyOperation implements RewriteOperation { - public String key; - public String newValue; + private final String key; + private final String newValue; public UpdatePropertyOperation(String key, String newValue) { this.key = key; @@ -16,7 +16,7 @@ public UpdatePropertyOperation(String key, String newValue) { } @Override - public Map toMap(BuildTool buildTool) { + public Map single(BuildTool buildTool) { switch (buildTool) { case MAVEN: return Map.of("org.openrewrite.maven.ChangePropertyValue", diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java index 2fdcbb2a5d71f..a621df7d7a07e 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/operations/UpgradeGradlePluginOperation.java @@ -7,8 +7,8 @@ public class UpgradeGradlePluginOperation implements RewriteOperation { - public String pluginIdPattern; - public String newVersion; + private final String pluginIdPattern; + private final String newVersion; public UpgradeGradlePluginOperation(String pluginIdPattern, String newVersion) { this.pluginIdPattern = pluginIdPattern; @@ -16,7 +16,7 @@ public UpgradeGradlePluginOperation(String pluginIdPattern, String newVersion) { } @Override - public Map toMap(BuildTool buildTool) { + public Map single(BuildTool buildTool) { switch (buildTool) { case GRADLE: return Map.of( diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java index 725aa50bb269c..0530f130015ca 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java @@ -20,9 +20,9 @@ import com.google.common.collect.ImmutableMap; import io.quarkus.devtools.commands.CreateProject; -import io.quarkus.devtools.project.SourceType; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProjectHelper; +import io.quarkus.devtools.project.SourceType; import io.quarkus.test.devmode.util.DevModeTestUtils; public class QuarkusPluginFunctionalTest extends QuarkusGradleDevToolsTestBase {