package com.itemis.maven.plugins.unleash.steps.checks; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import org.apache.maven.model.Build; import org.apache.maven.model.BuildBase; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; import org.apache.maven.model.Profile; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Settings; import com.google.common.base.Objects; import com.google.common.collect.Collections2; import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.itemis.maven.aether.ArtifactCoordinates; import com.itemis.maven.plugins.cdi.CDIMojoProcessingStep; import com.itemis.maven.plugins.cdi.ExecutionContext; import com.itemis.maven.plugins.cdi.annotations.ProcessingStep; import com.itemis.maven.plugins.cdi.logging.Logger; import com.itemis.maven.plugins.unleash.util.PomPropertyResolver; import com.itemis.maven.plugins.unleash.util.ReleaseUtil; import com.itemis.maven.plugins.unleash.util.functions.PluginToCoordinates; import com.itemis.maven.plugins.unleash.util.functions.ProjectToString; import com.itemis.maven.plugins.unleash.util.predicates.IsSnapshotPlugin; /** * Checks that none of the project modules references SNAPSHOT plugins since this would potentially lead to * non-reproducible release artifacts. * * @author <a href="mailto:[email protected]">Stanley Hillner</a> * @since 1.0.0 */ @ProcessingStep(id = "checkPlugins", description = "Checks that the projects do not use SNAPSHOT plugins to avoid unreproducible release aritfacts.", requiresOnline = false) public class CheckPluginVersions implements CDIMojoProcessingStep { @Inject private Logger log; @Inject @Named("reactorProjects") private List<MavenProject> reactorProjects; @Inject private PluginDescriptor pluginDescriptor; @Inject @Named("profiles") private List<String> profiles; @Inject @Named("releaseArgs") private Properties releaseArgs; @Inject private Settings settings; @Override public void execute(ExecutionContext context) throws MojoExecutionException, MojoFailureException { this.log.info("Checking that none of the reactor projects contains SNAPSHOT plugins."); Map<MavenProject, PomPropertyResolver> propertyResolvers = Maps .newHashMapWithExpectedSize(this.reactorProjects.size()); Multimap<MavenProject, ArtifactCoordinates> snapshotsByProject = HashMultimap.create(); for (MavenProject project : this.reactorProjects) { this.log.debug("\tChecking plugins of reactor project '" + ProjectToString.INSTANCE.apply(project) + "':"); PomPropertyResolver propertyResolver = new PomPropertyResolver(project, this.settings, this.profiles, this.releaseArgs); propertyResolvers.put(project, propertyResolver); snapshotsByProject.putAll(project, getSnapshotsFromManagement(project, propertyResolver)); snapshotsByProject.putAll(project, getSnapshots(project, propertyResolver)); snapshotsByProject.putAll(project, getSnapshotsFromAllProfiles(project, propertyResolver)); removePluginForIntegrationTests(snapshotsByProject); } failIfSnapshotsAreReferenced(snapshotsByProject, propertyResolvers); } private void failIfSnapshotsAreReferenced(Multimap<MavenProject, ArtifactCoordinates> snapshotsByProject, Map<MavenProject, PomPropertyResolver> propertyResolvers) throws MojoFailureException { if (!snapshotsByProject.values().isEmpty()) { this.log.error( "\tThere are references to SNAPSHOT plugins! The following list contains all SNAPSHOT plugins grouped by module:"); for (MavenProject project : snapshotsByProject.keySet()) { PomPropertyResolver propertyResolver = propertyResolvers.get(project); Collection<ArtifactCoordinates> snapshots = snapshotsByProject.get(project); if (!snapshots.isEmpty()) { this.log.error("\t\t[PROJECT] " + ProjectToString.INSTANCE.apply(project)); for (ArtifactCoordinates plugin : snapshots) { String resolvedVersion = propertyResolver.expandPropertyReferences(plugin.getVersion()); String coordinates = plugin.toString(); if (!Objects.equal(resolvedVersion, plugin.getVersion())) { coordinates = coordinates + " (resolves to " + resolvedVersion + ")"; } this.log.error("\t\t\t[PLUGIN] " + coordinates); } } } throw new MojoFailureException("The project cannot be released due to one or more SNAPSHOT plugins!"); } } private Set<ArtifactCoordinates> getSnapshotsFromManagement(MavenProject project, PomPropertyResolver propertyResolver) { this.log.debug("\t\tChecking managed plugins"); Build build = project.getBuild(); if (build != null) { PluginManagement pluginManagement = build.getPluginManagement(); if (pluginManagement != null) { Collection<Plugin> snapshots = Collections2.filter(pluginManagement.getPlugins(), new IsSnapshotPlugin(propertyResolver)); return Sets.newHashSet(Collections2.transform(snapshots, PluginToCoordinates.INSTANCE)); } } return Collections.emptySet(); } private Set<ArtifactCoordinates> getSnapshots(MavenProject project, PomPropertyResolver propertyResolver) { this.log.debug("\t\tChecking direct plugin references"); Build build = project.getBuild(); if (build != null) { Collection<Plugin> snapshots = Collections2.filter(build.getPlugins(), new IsSnapshotPlugin(propertyResolver)); return Sets.newHashSet(Collections2.transform(snapshots, PluginToCoordinates.INSTANCE)); } return Collections.emptySet(); } private Set<ArtifactCoordinates> getSnapshotsFromAllProfiles(MavenProject project, PomPropertyResolver propertyResolver) { Set<ArtifactCoordinates> snapshots = Sets.newHashSet(); List<Profile> profiles = project.getModel().getProfiles(); if (profiles != null) { for (Profile profile : profiles) { snapshots.addAll(getSnapshotsFromManagement(profile, propertyResolver)); snapshots.addAll(getSnapshots(profile, propertyResolver)); } } return snapshots; } private Set<ArtifactCoordinates> getSnapshotsFromManagement(Profile profile, PomPropertyResolver propertyResolver) { this.log.debug("\t\tChecking managed plugins of profile '" + profile.getId() + "'"); BuildBase build = profile.getBuild(); if (build != null) { PluginManagement pluginManagement = build.getPluginManagement(); if (pluginManagement != null) { Collection<Plugin> snapshots = Collections2.filter(pluginManagement.getPlugins(), new IsSnapshotPlugin(propertyResolver)); return Sets.newHashSet(Collections2.transform(snapshots, PluginToCoordinates.INSTANCE)); } } return Collections.emptySet(); } private Set<ArtifactCoordinates> getSnapshots(Profile profile, PomPropertyResolver propertyResolver) { this.log.debug("\t\tChecking direct plugin references of profile '" + profile.getId() + "'"); BuildBase build = profile.getBuild(); if (build != null) { Collection<Plugin> snapshots = Collections2.filter(build.getPlugins(), new IsSnapshotPlugin(propertyResolver)); return Sets.newHashSet(Collections2.transform(snapshots, PluginToCoordinates.INSTANCE)); } return Collections.emptySet(); } // Removes the unleash plugin itself from the list of violating dependencies if the integration test mode is enabled. private void removePluginForIntegrationTests(Multimap<MavenProject, ArtifactCoordinates> snapshotsByProject) { if (ReleaseUtil.isIntegrationtest()) { for (Iterator<Entry<MavenProject, ArtifactCoordinates>> i = snapshotsByProject.entries().iterator(); i .hasNext();) { Entry<MavenProject, ArtifactCoordinates> entry = i.next(); if (Objects.equal(entry.getValue(), PluginToCoordinates.INSTANCE.apply(this.pluginDescriptor.getPlugin()))) { i.remove(); } } } } }