diff --git a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/FreezeDependencyMojo.java b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/FreezeDependencyMojo.java index 024f0607b..579d29935 100644 --- a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/FreezeDependencyMojo.java +++ b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/FreezeDependencyMojo.java @@ -1,6 +1,7 @@ package io.github.chains_project.maven_lockfile; import io.github.chains_project.maven_lockfile.data.LockFile; +import io.github.chains_project.maven_lockfile.data.MavenPlugin; import io.github.chains_project.maven_lockfile.graph.DependencyNode; import io.github.chains_project.maven_lockfile.reporting.PluginLogManager; import java.io.File; @@ -9,9 +10,11 @@ import java.io.IOException; import java.util.*; import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.plugin.AbstractMojo; @@ -66,6 +69,7 @@ public void execute() throws MojoExecutionException { List filteredDependencies = getNearestVersionDependency(lockFile); Model pomModel = readPomFile(pomFile); updateDependencies(pomModel, filteredDependencies); + updatePlugins(pomModel, lockFile.getMavenPlugins()); writePomLockFile(pomModel, pomLockFile); } catch (IOException | XmlPullParserException e) { throw new MojoExecutionException("Could not freeze versions", e); @@ -156,6 +160,9 @@ private Dependency toMavenDependency(DependencyNode dep) { if (dep.getClassifier() != null) { mavenDep.setClassifier(dep.getClassifier().getValue()); } + if (dep.getType() != null) { + mavenDep.setType(dep.getType().getValue()); + } mavenDep.setScope(dep.getScope().getValue()); return mavenDep; } @@ -170,4 +177,72 @@ private String convertSoftToExactVersionString(String version) { } return "[" + version + "]"; } + + /** + * Updates the plugins in the POM model with plugin dependencies from the lock file. + * + * @param pomModel the POM model to update + * @param mavenPlugins the set of Maven plugins from the lock file + */ + private void updatePlugins(Model pomModel, Set mavenPlugins) { + if (mavenPlugins == null || mavenPlugins.isEmpty()) { + return; + } + + Build build = pomModel.getBuild(); + if (build == null) { + build = new Build(); + pomModel.setBuild(build); + } + + List plugins = build.getPlugins(); + if (plugins == null) { + plugins = new ArrayList<>(); + build.setPlugins(plugins); + } + + Map existingPluginsMap = new HashMap<>(); + for (Plugin plugin : plugins) { + String key = plugin.getGroupId() + ":" + plugin.getArtifactId(); + existingPluginsMap.put(key, plugin); + } + + // Process each plugin from the lock file + for (MavenPlugin mavenPlugin : mavenPlugins) { + String key = mavenPlugin.getGroupId().getValue() + ":" + + mavenPlugin.getArtifactId().getValue(); + Plugin plugin = existingPluginsMap.get(key); + + if (plugin == null) { + // Plugin doesn't exist in the POM, create it + plugin = new Plugin(); + plugin.setGroupId(mavenPlugin.getGroupId().getValue()); + plugin.setArtifactId(mavenPlugin.getArtifactId().getValue()); + plugins.add(plugin); + } + + // Add plugin dependencies if they exist + Set pluginDependencies = mavenPlugin.getDependencies(); + if (pluginDependencies != null && !pluginDependencies.isEmpty()) { + List dependencies = new ArrayList<>(); + Queue depQueue = new ArrayDeque<>(pluginDependencies); + + while (!depQueue.isEmpty()) { + DependencyNode depNode = depQueue.poll(); + if (depNode.isIncluded()) { + Dependency dep = toMavenDependency(depNode); + String scope = dep.getScope() != null ? dep.getScope() : "compile"; + + // Plugin dependencies can only have scope: compile, runtime, or system + if (scope.equals("compile") || scope.equals("runtime") || scope.equals("system")) { + dependencies.add(dep); + } + } + depQueue.addAll(depNode.getChildren()); + } + + plugin.setDependencies(dependencies); + } + } + } } diff --git a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/data/ArtifactType.java b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/data/ArtifactType.java new file mode 100644 index 000000000..7142ae5e0 --- /dev/null +++ b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/data/ArtifactType.java @@ -0,0 +1,52 @@ +package io.github.chains_project.maven_lockfile.data; + +import com.google.common.base.Strings; +import java.util.Objects; + +/** + * A Maven artifact type specifies the packaging type of the artifact (e.g., jar, pom, war). + * The default type is "jar", so we return null for "jar" or null/empty input to avoid redundant data. + */ +public class ArtifactType implements Comparable { + public static ArtifactType of(String type) { + if (Strings.isNullOrEmpty(type) || "jar".equals(type)) { + return null; + } + return new ArtifactType(type); + } + + private final String value; + + private ArtifactType(String type) { + this.value = Objects.requireNonNull(type, "type is marked non-null but is null"); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "{" + " ArtifactType='" + getValue() + "'" + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof ArtifactType)) { + return false; + } + ArtifactType artifactType = (ArtifactType) o; + return Objects.equals(value, artifactType.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public int compareTo(ArtifactType o) { + return this.value.compareTo(o.value); + } +} diff --git a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyGraph.java b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyGraph.java index fd6f184a1..ac00c5f5d 100644 --- a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyGraph.java +++ b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyGraph.java @@ -5,6 +5,7 @@ import io.github.chains_project.maven_lockfile.checksum.AbstractChecksumCalculator; import io.github.chains_project.maven_lockfile.checksum.RepositoryInformation; import io.github.chains_project.maven_lockfile.data.ArtifactId; +import io.github.chains_project.maven_lockfile.data.ArtifactType; import io.github.chains_project.maven_lockfile.data.Classifier; import io.github.chains_project.maven_lockfile.data.GroupId; import io.github.chains_project.maven_lockfile.data.MavenScope; @@ -87,6 +88,7 @@ private static Optional createDependencyNode( var artifactId = ArtifactId.of(node.getArtifact().getArtifactId()); var version = VersionNumber.of(node.getArtifact().getVersion()); var classifier = Classifier.of(node.getArtifact().getClassifier()); + var type = ArtifactType.of(node.getArtifact().getType()); PluginLogManager.getLog().debug(String.format("Calculating checksum for %s", node.toNodeString())); var checksum = isRoot ? "" : calc.calculateArtifactChecksum(node.getArtifact()); var scope = MavenScope.fromString(node.getArtifact().getScope()); @@ -105,6 +107,7 @@ private static Optional createDependencyNode( groupId, version, classifier, + type, scope, repositoryInformation.getResolvedUrl(), repositoryInformation.getRepositoryId(), diff --git a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyNode.java b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyNode.java index 8a9eb5a20..f4bca7e0a 100644 --- a/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyNode.java +++ b/maven_plugin/src/main/java/io/github/chains_project/maven_lockfile/graph/DependencyNode.java @@ -2,6 +2,7 @@ import com.google.gson.annotations.Expose; import io.github.chains_project.maven_lockfile.data.ArtifactId; +import io.github.chains_project.maven_lockfile.data.ArtifactType; import io.github.chains_project.maven_lockfile.data.Classifier; import io.github.chains_project.maven_lockfile.data.GroupId; import io.github.chains_project.maven_lockfile.data.MavenScope; @@ -20,6 +21,7 @@ public class DependencyNode implements Comparable { private final ArtifactId artifactId; private final VersionNumber version; private final Classifier classifier; + private final ArtifactType type; private final String checksumAlgorithm; private final String checksum; private final MavenScope scope; @@ -42,6 +44,7 @@ public class DependencyNode implements Comparable { GroupId groupId, VersionNumber version, Classifier classifier, + ArtifactType type, MavenScope scope, ResolvedUrl resolved, RepositoryId repositoryId, @@ -51,6 +54,7 @@ public class DependencyNode implements Comparable { this.groupId = groupId; this.version = version; this.classifier = classifier; + this.type = type; this.checksumAlgorithm = checksumAlgorithm; this.checksum = checksum; this.children = new TreeSet<>(Comparator.comparing(DependencyNode::getComparatorString)); @@ -89,6 +93,12 @@ public VersionNumber getVersion() { public Classifier getClassifier() { return classifier; } + /** + * @return the artifact type or null if default (jar) + */ + public ArtifactType getType() { + return type; + } /** * @return the scope */ @@ -173,6 +183,7 @@ public int hashCode() { artifactId, version, classifier, + type, checksumAlgorithm, checksum, scope, @@ -195,6 +206,7 @@ public boolean equals(Object obj) { && Objects.equals(artifactId, other.artifactId) && Objects.equals(version, other.version) && Objects.equals(classifier, other.classifier) + && Objects.equals(type, other.type) && Objects.equals(checksumAlgorithm, other.checksumAlgorithm) && Objects.equals(checksum, other.checksum) && Objects.equals(scope, other.scope) @@ -233,10 +245,10 @@ public int compareTo(DependencyNode o) { @Override public String toString() { return "DependencyNode [groupId=" + groupId + ", artifactId=" + artifactId + ", version=" + version - + ", classifier=" + classifier + ", checksumAlgorithm=" + checksumAlgorithm + ", checksum=" + checksum - + ", scope=" + scope + ", resolved=" + resolved + ", repositoryId=" + repositoryId - + ", selectedVersion=" + selectedVersion + ", id=" + id + ", parent=" + parent + ", children=" - + children + "]"; + + ", classifier=" + classifier + ", type=" + type + ", checksumAlgorithm=" + checksumAlgorithm + + ", checksum=" + checksum + ", scope=" + scope + ", resolved=" + resolved + ", repositoryId=" + + repositoryId + ", selectedVersion=" + selectedVersion + ", id=" + id + ", parent=" + parent + + ", children=" + children + "]"; } public String getComparatorString() { diff --git a/maven_plugin/src/test/java/io/github/chains_project/maven_lockfile/graph/LockfileTest.java b/maven_plugin/src/test/java/io/github/chains_project/maven_lockfile/graph/LockfileTest.java index dbb23f226..ec9d6441e 100644 --- a/maven_plugin/src/test/java/io/github/chains_project/maven_lockfile/graph/LockfileTest.java +++ b/maven_plugin/src/test/java/io/github/chains_project/maven_lockfile/graph/LockfileTest.java @@ -4,6 +4,7 @@ import io.github.chains_project.maven_lockfile.checksum.ChecksumModes; import io.github.chains_project.maven_lockfile.data.ArtifactId; +import io.github.chains_project.maven_lockfile.data.ArtifactType; import io.github.chains_project.maven_lockfile.data.Config; import io.github.chains_project.maven_lockfile.data.Environment; import io.github.chains_project.maven_lockfile.data.GroupId; @@ -65,6 +66,7 @@ private DependencyNode dependencyNodeA(DependencyNode child1, DependencyNode chi GroupId.of("Ag"), VersionNumber.of("1"), null, + ArtifactType.of("pom"), MavenScope.RUNTIME, ResolvedUrl.Unresolved(), RepositoryId.None(), @@ -82,6 +84,7 @@ private DependencyNode dependencyNodeB() { GroupId.of("Bg"), VersionNumber.of("1"), null, + ArtifactType.of("jar"), MavenScope.RUNTIME, ResolvedUrl.Unresolved(), RepositoryId.None(), @@ -95,6 +98,7 @@ private DependencyNode dependencyNodeAChild1() { GroupId.of("Ag1"), VersionNumber.of("1"), null, + ArtifactType.of("war"), MavenScope.RUNTIME, ResolvedUrl.Unresolved(), RepositoryId.None(), @@ -108,6 +112,7 @@ private DependencyNode dependencyNodeAChild2() { GroupId.of("Ag2"), VersionNumber.of("1"), null, + ArtifactType.of("jar"), MavenScope.RUNTIME, ResolvedUrl.Unresolved(), RepositoryId.None(),