diff --git a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/ExtensionChangesMojo.java b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/ExtensionChangesMojo.java index 7431afcc..0518b2a3 100644 --- a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/ExtensionChangesMojo.java +++ b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/ExtensionChangesMojo.java @@ -15,8 +15,10 @@ import io.quarkus.registry.config.RegistryConfig; import io.quarkus.registry.util.PlatformArtifacts; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -24,8 +26,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -41,6 +41,9 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.Version; @Mojo(name = "extension-changes", threadSafe = true, requiresProject = false) public class ExtensionChangesMojo extends AbstractMojo { @@ -57,15 +60,44 @@ public class ExtensionChangesMojo extends AbstractMojo { @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true) List repos; + /** + * Maven artifact coordinates of a platform member BOM extensions from which + * should be compared to a previous version of the BOM specified by either + * {@link #previousBom} parameter or registry configured either with + * the {@link #registryConfig} or the {@link #registry} + */ @Parameter(property = "bom", required = true) String bom; + /** + * Maven artifact coordinates of the previous version of the platform member BOM + * {@link #bom} should be compared to. If not configured, the previous version of + * the platform member BOM will be obtained from the registry configured with + * either the {@link #registryConfig} or {@link #registry} + */ @Parameter(property = "previousBom", required = false) String previousBom; + /** + * Extension registry config file used to obtain the previous version of + * the platform member BOM configured with {@link #bom} + */ + @Parameter(property = "registryConfig", required = false) + File registryConfig; + + /** + * Extension registry ID that should be used to obtain the previous version of + * the platform member BOM configured with {@link #bom} + */ @Parameter(property = "registry", required = false, defaultValue = "registry.quarkus.io") String registry; + /** + * File to save the report to. If not configured the output will be logged in the terminal. + */ + @Parameter(property = "outputFile", required = false) + File outputFile; + @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -126,20 +158,27 @@ public void execute() throws MojoExecutionException, MojoFailureException { statusList.add(ExtensionStatus.classpathUpdate(prev.getKey(), prev.getValue())); } } else { - statusList.add(ExtensionStatus.versionUpdate(prev.getKey(), prev.getValue())); + statusList.add(ExtensionStatus.versionUpdate(prev.getKey(), newVersion)); } } for (var e : extensions.entrySet()) { statusList.add(ExtensionStatus.newStatus(e.getKey(), e.getValue())); } - var json = new StringWriter(); - try (BufferedWriter writer = new BufferedWriter(json)) { - CatalogMapperHelper.serialize(statusList, json); + if (outputFile != null) { + outputFile.getParentFile().mkdirs(); + } + StringWriter json = null; + try (BufferedWriter writer = outputFile == null + ? new BufferedWriter(json = new StringWriter()) + : Files.newBufferedWriter(outputFile.toPath())) { + CatalogMapperHelper.serialize(statusList, writer); } catch (IOException e) { - throw new RuntimeException(e); + throw new MojoExecutionException("Failed to serialize extension changes report to JSON", e); + } + if (json != null) { + getLog().info(json.getBuffer().toString()); } - System.out.println(json.getBuffer().toString()); } public static class ExtensionStatus { @@ -277,18 +316,38 @@ private static ExtensionCatalog resolveExtensionCatalog(ArtifactCoords catalogCo private ArtifactCoords getPreviousBom(MavenArtifactResolver resolver, ExtensionCatalog catalog) throws MojoExecutionException { + + var extResolverBuilder = ExtensionCatalogResolver.builder() + .artifactResolver(resolver) + .messageWriter(new MojoMessageWriter(getLog())); + if (registryConfig != null) { + if (!registryConfig.exists()) { + throw new MojoExecutionException(registryConfig + " does not exist"); + } + if (registryConfig.isDirectory()) { + throw new MojoExecutionException(registryConfig + " is a directory"); + } + try { + extResolverBuilder.config(RegistriesConfig.fromFile(registryConfig.toPath())); + } catch (IOException e) { + throw new MojoExecutionException("Failed to parse registry configuration file " + registryConfig, e); + } + } else { + if (registry == null || registry.isEmpty()) { + throw new MojoExecutionException("Registry ID has not been provided"); + } + extResolverBuilder.config(RegistriesConfig.builder() + .setRegistries(List.of( + RegistryConfig.builder() + .setId(registry) + .build())) + .build()); + + } + final ExtensionCatalogResolver catalogResolver; try { - catalogResolver = ExtensionCatalogResolver.builder() - .artifactResolver(resolver) - .messageWriter(new MojoMessageWriter(getLog())) - .config(RegistriesConfig.builder() - .setRegistries(List.of( - RegistryConfig.builder() - .setId(registry) - .build())) - .build()) - .build(); + catalogResolver = extResolverBuilder.build(); } catch (RegistryResolutionException e) { throw new MojoExecutionException("Failed to initialize extension catalog resolver for registry " + registry, e); } @@ -322,11 +381,23 @@ private ArtifactCoords getPreviousBom(MavenArtifactResolver resolver, ExtensionC throw new MojoExecutionException("Failed to resolve stream " + currentPlatform + ":" + currentStream, e); } - final ArtifactVersion current = new DefaultArtifactVersion(currentVersion); - ArtifactVersion previous = null; + final GenericVersionScheme versionScheme = new GenericVersionScheme(); + final Version current; + try { + current = versionScheme.parseVersion(currentVersion); + } catch (InvalidVersionSpecificationException e) { + throw new MojoExecutionException(e); + } + + Version previous = null; PlatformRelease previousRelease = null; for (var pr : prevStream.getReleases()) { - var v = new DefaultArtifactVersion(pr.getVersion().toString()); + final Version v; + try { + v = versionScheme.parseVersion(pr.getVersion().toString()); + } catch (InvalidVersionSpecificationException e) { + throw new MojoExecutionException(e); + } if (current.compareTo(v) > 0 && (previous == null || previous.compareTo(v) < 0)) { previous = v; diff --git a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java index 56e7d83c..95b63b43 100644 --- a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java +++ b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java @@ -274,6 +274,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { generateMavenRepoZipModule(pom); } + generateExtensionChangesModule(pom); addReleaseProfile(pom); persistPom(pom); @@ -477,6 +478,86 @@ private static boolean isExtensionCandidate(Artifact a, Collection exten return true; } + private void generateExtensionChangesModule(Model parentPom) throws MojoExecutionException { + final String artifactId = "quarkus-extension-changes"; + final Model pom = newModel(); + pom.setArtifactId(artifactId); + pom.setPackaging(ArtifactCoords.TYPE_POM); + pom.setName(getNameBase(parentPom) + " " + artifactIdToName(artifactId)); + parentPom.addModule(artifactId); + final File pomXml = getPomFile(parentPom, artifactId); + pom.setPomFile(pomXml); + final Parent parent = new Parent(); + parent.setGroupId(ModelUtils.getGroupId(parentPom)); + parent.setArtifactId(parentPom.getArtifactId()); + parent.setVersion(ModelUtils.getVersion(parentPom)); + parent.setRelativePath(pomXml.toPath().getParent().relativize(parentPom.getProjectDirectory().toPath()).toString()); + pom.setParent(parent); + Utils.skipInstallAndDeploy(pom); + + Plugin plugin = new Plugin(); + Build build = pom.getBuild(); + if (build == null) { + build = new Build(); + pom.setBuild(build); + } + build.addPlugin(plugin); + plugin.setGroupId(pluginDescriptor().getGroupId()); + plugin.setArtifactId(pluginDescriptor().getArtifactId()); + + final String profileId = "extensionChanges"; + Profile profile = new Profile(); + profile.setId(profileId); + pom.addProfile(profile); + final Activation activation = new Activation(); + profile.setActivation(activation); + final ActivationProperty ap = new ActivationProperty(); + activation.setProperty(ap); + ap.setName(profileId); + + build = new Build(); + profile.setBuild(build); + + PluginManagement pm = new PluginManagement(); + build.setPluginManagement(pm); + plugin = new Plugin(); + pm.addPlugin(plugin); + plugin.setGroupId(pluginDescriptor().getGroupId()); + plugin.setArtifactId(pluginDescriptor().getArtifactId()); + + final StringBuilder sb = new StringBuilder(); + for (PlatformMemberImpl m : members.values()) { + if (!m.config.isHidden() && m.config.isEnabled()) { + sb.append("quarkus-platform-bom:extension-changes@").append(m.generatedBomCoords().getArtifactId()) + .append(' '); + } + } + build.setDefaultGoal(sb.toString()); + + Path outputDir = buildDir.toPath().resolve("extension-changes"); + final String prefix = pomXml.toPath().getParent().relativize(outputDir).toString(); + for (PlatformMemberImpl m : members.values()) { + if (m.config.isHidden() || !m.config.isEnabled()) { + continue; + } + final PluginExecution exec = new PluginExecution(); + plugin.addExecution(exec); + exec.setId(m.generatedBomCoords().getArtifactId()); + exec.setPhase("process-resources"); + exec.addGoal("extension-changes"); + final Xpp3Dom config = new Xpp3Dom("configuration"); + exec.setConfiguration(config); + config.addChild(textDomElement("bom", + m.generatedBomCoords().getGroupId() + ":" + m.generatedBomCoords().getArtifactId() + ":" + + getDependencyVersion(pom, m.descriptorCoords()))); + config.addChild( + textDomElement("outputFile", + prefix + "/" + m.generatedBomCoords().getArtifactId() + "-extension-changes.json")); + } + + persistPom(pom); + } + private void generateDepsToBuildModule(Model parentPom) throws MojoExecutionException { generateDepsToBuildModule(parentPom, "quarkus-dependencies-to-build", "depsToBuild", "dependencies-to-build", "-deps-to-build.txt", false); @@ -1936,9 +2017,9 @@ private void addQuarkusBuildConfig(Model pom, Dependency appArtifactDep) { * Returns either a property expression that should be used in place of the actual artifact version * or the actual artifact version, in case no property was found that could represent the version * - * @param testArtifact test artifact coords + * @param artifactGroupId test artifact groupId + * @param version test artifact version * @return property expression or the actual version - * @throws MojoExecutionException in case of a failure */ private String getTestArtifactVersion(String artifactGroupId, String version) { if (pomPropsByValues.isEmpty()) {