Skip to content

Commit

Permalink
Greatly improve Quarkus udpate for Quarkiverse
Browse files Browse the repository at this point in the history
  • Loading branch information
ia3andy committed Feb 17, 2025
1 parent 221e4b2 commit f08f5c0
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,31 @@
package io.quarkus.devtools.commands.handlers;

import static io.quarkus.devtools.messagewriter.MessageIcons.UP_TO_DATE_ICON;
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.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.messagewriter.MessageIcons;
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.state.ProjectState;
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.*;
import io.quarkus.devtools.project.update.rewrite.QuarkusUpdateCommand;
import io.quarkus.devtools.project.update.rewrite.QuarkusUpdates;
import io.quarkus.devtools.project.update.rewrite.QuarkusUpdatesRepository;
Expand All @@ -43,6 +40,7 @@ public class UpdateProjectCommandHandler implements QuarkusCommandHandler {
public static final String UPDATE = "Update:";

public static final String ITEM_FORMAT = "%-7s %s";
private static final Logger log = LoggerFactory.getLogger(UpdateProjectCommandHandler.class);

@Override
public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException {
Expand Down Expand Up @@ -72,79 +70,81 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws
invocation.log().error(error);
return QuarkusCommandOutcome.failure(error);
}
if (Objects.equals(projectQuarkusPlatformBom.getVersion(), targetPlatformVersion)) {
ProjectInfoCommandHandler.logState(currentState, perModule, true, invocation.getQuarkusProject().log());
} else {
invocation.log().info("Instructions to update this project from '%s' to '%s':",
projectQuarkusPlatformBom.getVersion(), targetPlatformVersion);
final QuarkusProject quarkusProject = invocation.getQuarkusProject();
final ProjectState recommendedState = resolveRecommendedState(currentState, targetCatalog,
invocation.log());
final ProjectPlatformUpdateInfo platformUpdateInfo = resolvePlatformUpdateInfo(currentState,
recommendedState);
final ProjectExtensionsUpdateInfo extensionsUpdateInfo = ProjectUpdateInfos.resolveExtensionsUpdateInfo(
currentState,
recommendedState);
final QuarkusProject quarkusProject = invocation.getQuarkusProject();
final ProjectState recommendedState = resolveRecommendedState(currentState, targetCatalog,
invocation.log());
final ProjectPlatformUpdateInfo platformUpdateInfo = resolvePlatformUpdateInfo(currentState,
recommendedState);
final ProjectExtensionsUpdateInfo extensionsUpdateInfo = ProjectUpdateInfos.resolveExtensionsUpdateInfo(
currentState,
recommendedState);
boolean shouldUpdate = logUpdates(invocation.getQuarkusProject(), currentState, recommendedState, platformUpdateInfo,
extensionsUpdateInfo,
false, perModule,
quarkusProject.log());
final boolean noRewrite = invocation.getValue(UpdateProject.NO_REWRITE, false);
if (shouldUpdate && !noRewrite) {
final BuildTool buildTool = quarkusProject.getExtensionManager().getBuildTool();
String kotlinVersion = getMetadata(targetCatalog, "project", "properties", "kotlin-version");
final Optional<Integer> updateJavaVersion = resolveUpdateJavaVersion(extensionsUpdateInfo, projectJavaVersion);
QuarkusUpdates.ProjectUpdateRequest request = new QuarkusUpdates.ProjectUpdateRequest(
buildTool,
projectQuarkusPlatformBom.getVersion(),
targetPlatformVersion,
kotlinVersion,
updateJavaVersion,
extensionsUpdateInfo);
Path recipe = null;
try {

logUpdates(invocation.getQuarkusProject(), currentState, recommendedState, platformUpdateInfo,
extensionsUpdateInfo,
false, perModule,
quarkusProject.log());
final boolean noRewrite = invocation.getValue(UpdateProject.NO_REWRITE, false);
recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml");
final String quarkusUpdateRecipes = invocation.getValue(
UpdateProject.REWRITE_QUARKUS_UPDATE_RECIPES,
QuarkusUpdatesRepository.DEFAULT_UPDATE_RECIPES_VERSION);
final String additionalUpdateRecipes = invocation.getValue(
UpdateProject.REWRITE_ADDITIONAL_UPDATE_RECIPES,
null);
final FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), recipe,
QuarkusProjectHelper.artifactResolver(), buildTool, quarkusUpdateRecipes,
additionalUpdateRecipes, request);
invocation.log().info("OpenRewrite recipe generated: %s", recipe);
log.info("Running OpenRewrite recipe...");
log.info("");

if (!noRewrite) {
final BuildTool buildTool = quarkusProject.getExtensionManager().getBuildTool();
String kotlinVersion = getMetadata(targetCatalog, "project", "properties", "kotlin-version");
final OptionalInt minJavaVersion = extensionsUpdateInfo.getMinJavaVersion();
final Optional<Integer> updateJavaVersion;
if (minJavaVersion.isPresent()
&& projectJavaVersion.isPresent()
&& minJavaVersion.getAsInt() > projectJavaVersion.getAsInt()) {
updateJavaVersion = Optional.of(minJavaVersion.getAsInt());
} else {
updateJavaVersion = Optional.empty();
}
QuarkusUpdates.ProjectUpdateRequest request = new QuarkusUpdates.ProjectUpdateRequest(
String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION,
fetchResult.getRewritePluginVersion());
boolean rewriteDryRun = invocation.getValue(UpdateProject.REWRITE_DRY_RUN, false);
QuarkusUpdateCommand.handle(
invocation.log(),
buildTool,
projectQuarkusPlatformBom.getVersion(),
targetPlatformVersion,
kotlinVersion,
updateJavaVersion,
extensionsUpdateInfo);
Path recipe = null;
try {
recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml");
final String quarkusUpdateRecipes = invocation.getValue(
UpdateProject.REWRITE_QUARKUS_UPDATE_RECIPES,
QuarkusUpdatesRepository.DEFAULT_UPDATE_RECIPES_VERSION);
final String additionalUpdateRecipes = invocation.getValue(
UpdateProject.REWRITE_ADDITIONAL_UPDATE_RECIPES,
null);
final FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), recipe,
QuarkusProjectHelper.artifactResolver(), buildTool, quarkusUpdateRecipes,
additionalUpdateRecipes, request);
invocation.log().info("OpenRewrite recipe generated: %s", recipe);
quarkusProject.getProjectDirPath(),
rewritePluginVersion,
fetchResult.getRecipesGAV(),
recipe,
rewriteDryRun);

String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION,
fetchResult.getRewritePluginVersion());
boolean rewriteDryRun = invocation.getValue(UpdateProject.REWRITE_DRY_RUN, false);
QuarkusUpdateCommand.handle(
invocation.log(),
buildTool,
quarkusProject.getProjectDirPath(),
rewritePluginVersion,
fetchResult.getRecipesGAV(),
recipe,
rewriteDryRun);

} catch (IOException e) {
throw new QuarkusCommandException("Error while generating the project update script", e);
}
} catch (IOException e) {
throw new QuarkusCommandException("Error while generating the project update script", e);
}
}

return QuarkusCommandOutcome.success();
}

private static Optional<Integer> resolveUpdateJavaVersion(ProjectExtensionsUpdateInfo extensionsUpdateInfo,
JavaVersion projectJavaVersion) {
final OptionalInt minJavaVersion = extensionsUpdateInfo.getMinJavaVersion();
final Optional<Integer> updateJavaVersion;
if (minJavaVersion.isPresent()
&& projectJavaVersion.isPresent()
&& minJavaVersion.getAsInt() > projectJavaVersion.getAsInt()) {
updateJavaVersion = Optional.of(minJavaVersion.getAsInt());
} else {
updateJavaVersion = Optional.empty();
}
return updateJavaVersion;
}

private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentState) {
for (ArtifactCoords c : currentState.getPlatformBoms()) {
if (c.getArtifactId().equals(ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID)
Expand All @@ -155,34 +155,41 @@ private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentS
return null;
}

private static void logUpdates(QuarkusProject project, ProjectState currentState, ProjectState recommendedState,
private static boolean logUpdates(QuarkusProject project, ProjectState currentState, ProjectState recommendedState,
ProjectPlatformUpdateInfo platformUpdateInfo,
ProjectExtensionsUpdateInfo extensionsUpdateInfo, boolean recommendState,
boolean perModule, MessageWriter log) {
printSeparator(log);
if (currentState.getPlatformBoms().isEmpty()) {
log.info("The project does not import any Quarkus platform BOM");
return;
printSeparator(log);
return false;
}
if (currentState.getExtensions().isEmpty()) {
log.info("No Quarkus extensions were found among the project dependencies");
return;
printSeparator(log);
return false;
}
if (currentState == recommendedState) {
log.info("The project is up-to-date");
return;
printSeparator(log);
return false;
}

if (recommendState) {
ProjectInfoCommandHandler.logState(recommendedState, perModule, false, log);
return;
printSeparator(log);
return false;
}

if (platformUpdateInfo.isPlatformUpdatesAvailable()) {
log.info("Recommended Quarkus platform BOM updates:");
if (!platformUpdateInfo.getImportVersionUpdates().isEmpty()) {
for (PlatformInfo importInfo : platformUpdateInfo.getImportVersionUpdates()) {
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
UpdateProjectCommandHandler.UPDATE, importInfo.getImported().toCompactCoords()) + " -> "
+ importInfo.getRecommendedVersion());
"", importInfo.getImported().getKey().toGacString() + ":["
+ importInfo.getImported().getVersion() + " -> " + importInfo.getRecommendedVersion()
+ "]"));
}
}
if (!platformUpdateInfo.getNewImports().isEmpty()) {
Expand All @@ -200,11 +207,13 @@ private static void logUpdates(QuarkusProject project, ProjectState currentState
}
}
log.info("");
}

if (extensionsUpdateInfo.shouldUpdateExtensions() && !platformUpdateInfo.isPlatformUpdatesAvailable()) {
log.info("The project is up-to-date");
return;
} else if (!extensionsUpdateInfo.shouldUpdateExtensions()) {
log.info("The project is up-to-date " + MessageIcons.UP_TO_DATE_ICON.iconOrMessage());
printSeparator(log);
return false;
} else {
log.info("Quarkus platform BOM(s) are up-to-date " + MessageIcons.UP_TO_DATE_ICON.iconOrMessage());
log.info("");
}

if (extensionsUpdateInfo.getMinJavaVersion().isPresent() && project.getJavaVersion().isPresent()) {
Expand All @@ -217,50 +226,80 @@ private static void logUpdates(QuarkusProject project, ProjectState currentState

for (PlatformInfo platform : platformUpdateInfo.getPlatformImports().values()) {
final String provider = platform.getRecommendedProviderKey();
if (!extensionsUpdateInfo.containsProvider(provider)) {
final List<ExtensionUpdateInfo> extensions = extensionsUpdateInfo.extensionsByProvider().getOrDefault(provider,
Collections.emptyList()).stream().filter(ExtensionUpdateInfo::isUpdateRecommended).toList();
if (extensions.isEmpty()) {
continue;
}
log.info("Extensions from " + platform.getRecommendedProviderKey() + ":");

for (ExtensionUpdateInfo e : extensionsUpdateInfo.extensionsByProvider().getOrDefault(provider,
Collections.emptyList())) {
log.info("Extensions from " + platform.getRecommendedProviderKey() + " to update:");
for (ExtensionUpdateInfo e : extensions) {

final ExtensionUpdateInfo.VersionUpdateType versionUpdateType = e.getVersionUpdateType();

if (e.hasKeyChanged()) {
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
UpdateProjectCommandHandler.UPDATE,
e.getCurrentDep().getArtifact().toCompactCoords() + " -> "
+ e.getRecommendedDependency().getArtifact().toCompactCoords()));
"",
updateInfo(e)));
} else {
switch (versionUpdateType) {
case PLATFORM_MANAGED:
// The extension update is done when updating the platform
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT, "",
e.getCurrentDep().getArtifact().getKey().toGacString())
+ " (already managed by platform BOM)");
break;
case RECOMMEND_PLATFORM_MANAGED:
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
UpdateProjectCommandHandler.UPDATE,
"",
e.getCurrentDep().getArtifact().toCompactCoords()
+ " -> drop version (managed by platform)"));
break;
case ADD_VERSION:
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
UpdateProjectCommandHandler.UPDATE,
"",
e.getRecommendedDependency().getArtifact().toCompactCoords()
+ " -> add version (managed by platform)"));
break;
case UPDATE_VERSION:
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
UpdateProjectCommandHandler.UPDATE,
e.getCurrentDep().getArtifact().toCompactCoords() + " -> "
+ e.getRecommendedDependency().getVersion()));
"",
updateInfo(e)));
break;
}
}
}
log.info("");
}
final List<ExtensionUpdateInfo> simpleVersionUpdates = extensionsUpdateInfo.getSimpleVersionUpdates();
if (!simpleVersionUpdates.isEmpty()) {
log.info("Other extensions to update:");
for (ExtensionUpdateInfo u : simpleVersionUpdates) {
if (u.getVersionUpdateType() == ExtensionUpdateInfo.VersionUpdateType.PLATFORM_MANAGED) {
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
"",
u.getCurrentDep().getArtifact().toCompactCoords()
+ " -> drop version (managed by platform)"));
} else {
log.info(String.format(UpdateProjectCommandHandler.ITEM_FORMAT,
"",
updateInfo(u)));
}
}
}

printSeparator(log);
return true;
}

private static void printSeparator(MessageWriter log) {
log.info("");
log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
log.info("");
}

private static String updateInfo(ExtensionUpdateInfo e) {
return e.getCurrentDep().getArtifact().getKey().toGacString() + ":[" + e.getCurrentDep().getVersion() + " -> "
+ e.getRecommendedDependency().getVersion() + "]";
}

@SuppressWarnings({ "rawtypes", "unchecked" })
Expand Down
Loading

0 comments on commit f08f5c0

Please sign in to comment.