io.github.chains_project.maven_lockfile.FreezeDependencyMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-lockfile Show documentation
Show all versions of maven-lockfile Show documentation
This plugin is a state-of-the-art solution that can be used to validate the integrity
of a maven repository.
It does this by generating a lock file that contains the checksums of all the artifacts in the
repository.
The lock file can then be used to validate the integrity of the repository.
This guards the supply chain against malicious actors that might tamper with the artifacts in
the repository.
The newest version!
package io.github.chains_project.maven_lockfile;
import io.github.chains_project.maven_lockfile.data.LockFile;
import io.github.chains_project.maven_lockfile.graph.DependencyNode;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
/**
* Freeze the dependencies of a project. Every dependency will be locked to a specific version.
* For this, every version of direct dependencies in the pom file will be replaced with the version from the lock file.
* This also adds all transitive dependencies from the lock file inside the dependencyManagement section.
*
* If there exists no lock file, this fails.
*/
@Mojo(
name = "freeze",
defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE,
requiresOnline = true)
public class FreezeDependencyMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@Parameter(defaultValue = "pom.lockfile.xml", property = "pomLockfileOutput")
private String pomLockfileOutput;
/**
* Freezes the dependencies of the project. Every dependency will be locked to a specific version.
*
* @throws MojoExecutionException if the lock file is invalid or could not be read.
*/
public void execute() throws MojoExecutionException {
File pomFile = project.getFile();
File pomLockFile = new File(project.getBasedir(), pomLockfileOutput);
try {
LockFile lockFile = LockFile.readLockFile(LockFileFacade.getLockFilePath(project));
List filteredDependencies = getNearestVersionDependency(lockFile);
Model pomModel = readPomFile(pomFile);
updateDependencies(pomModel, filteredDependencies);
writePomLockFile(pomModel, pomLockFile);
} catch (IOException | XmlPullParserException e) {
throw new MojoExecutionException("Could not freeze versions", e);
}
}
private Model readPomFile(File pomFile) throws IOException, XmlPullParserException {
try (FileReader fileReader = new FileReader(pomFile)) {
MavenXpp3Reader reader = new MavenXpp3Reader();
return reader.read(fileReader);
}
}
private void updateDependencies(Model pomModel, List filteredDependencies) {
List pomDependencies = pomModel.getDependencies();
DependencyManagement dependencyManagement = pomModel.getDependencyManagement();
if (dependencyManagement == null) {
dependencyManagement = new DependencyManagement();
}
List dependencyManagementDependencies = dependencyManagement.getDependencies();
Map pomDependencyMap = createDependencyMap(pomDependencies);
Map dependencyManagementMap = createDependencyMap(dependencyManagementDependencies);
for (Dependency dep : filteredDependencies) {
String key = dep.getGroupId() + ":" + dep.getArtifactId();
Dependency pomDependency = pomDependencyMap.get(key);
Dependency dependencyManagementDep = dependencyManagementMap.get(key);
if (pomDependency != null) {
pomDependency.setVersion(dep.getVersion());
pomDependency.setScope(dep.getScope());
} else if (dependencyManagementDep != null) {
dependencyManagementDep.setVersion(dep.getVersion());
} else {
dependencyManagementDependencies.add(dep);
}
}
pomModel.setDependencies(pomDependencies);
dependencyManagement.setDependencies(dependencyManagementDependencies);
pomModel.setDependencyManagement(dependencyManagement);
}
private Map createDependencyMap(List dependencies) {
Map dependencyMap = new HashMap<>();
for (Dependency dep : dependencies) {
String key = dep.getGroupId() + ":" + dep.getArtifactId();
dependencyMap.put(key, dep);
}
return dependencyMap;
}
private void writePomLockFile(Model pomModel, File pomLockFile) throws IOException {
try (FileWriter fileWriter = new FileWriter(pomLockFile)) {
MavenXpp3Writer writer = new MavenXpp3Writer();
writer.write(fileWriter, pomModel);
}
}
private List getNearestVersionDependency(LockFile lockFileFromFile) {
var deps = lockFileFromFile.getDependencies();
Map nearestVersionMap = new HashMap<>();
Queue depQueue = new ArrayDeque<>(deps);
while (!depQueue.isEmpty()) {
var depNode = depQueue.poll();
Dependency dep = toMavenDependency(depNode);
String key = dep.getGroupId() + ":" + dep.getArtifactId();
if (depNode.isIncluded()) {
nearestVersionMap.put(key, dep);
}
depQueue.addAll(depNode.getChildren());
}
return new ArrayList<>(nearestVersionMap.values());
}
/**
* Converts a DependencyNode to a Maven Model Dependency.
*
* @param dep the DependencyNode to convert
* @return the converted Dependency
*/
private Dependency toMavenDependency(DependencyNode dep) {
Dependency mavenDep = new Dependency();
mavenDep.setGroupId(dep.getGroupId().getValue());
mavenDep.setArtifactId(dep.getArtifactId().getValue());
mavenDep.setVersion(dep.getVersion().getValue());
if (dep.getClassifier() != null) {
mavenDep.setClassifier(dep.getClassifier().getValue());
}
mavenDep.setScope(dep.getScope().getValue());
return mavenDep;
}
}