Skip to content

Commit

Permalink
Resolves #899: Corrected the invocation of Resolver to retrieve the t…
Browse files Browse the repository at this point in the history
…imestamped snapshot version; added unit tests for LockSnapshotsMojo for the case when no timestamped versions are found
  • Loading branch information
andrzejj0 committed Jan 8, 2023
1 parent 6af5d83 commit a6baf81
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 54 deletions.
8 changes: 8 additions & 0 deletions versions-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@
! Not deleting as it might be helpful with assessing performance measurements.
-->
<pomExclude>it-property-updates-report-002-slow/*</pomExclude>
<!--
! The below test is testing Resolver's resolveVersion being able
! to find a timestamped version of a snapshot. Disabled as it's not using
! the mrm-maven-plugin, and so depends on external artifacts; also:
! it's actually testing Resolver and not the plugin itself.
! It's there for debugging purposes if something goes wrong.
-->
<pomExclude>it-lock-snapshots-junit/*</pomExclude>
</pomExcludes>
<postBuildHookScript>verify</postBuildHookScript>
<filterProperties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:lock-snapshots
20 changes: 20 additions & 0 deletions versions-maven-plugin/src/it/it-lock-snapshots-junit5/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>localhost</groupId>
<artifactId>it-lock-snapshots-001</artifactId>
<version>1.0</version>
<packaging>pom</packaging>

<dependencies>
<dependency>
<!-- Can't get mrm-maven-plugin to provide maven-metadata -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>5.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def project = new XmlSlurper().parse( new File( basedir, 'pom.xml' ) )

assert !( project.dependencies.dependency.version =~ /-SNAPSHOT/ )
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -39,9 +40,12 @@
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.wagon.Wagon;
import org.codehaus.mojo.versions.api.PomHelper;
import org.codehaus.mojo.versions.api.VersionsHelper;
import org.codehaus.mojo.versions.api.recording.ChangeRecorder;
import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.VersionRequest;
import org.eclipse.aether.resolution.VersionResolutionException;
import org.eclipse.aether.resolution.VersionResult;

/**
* Attempts to resolve unlocked snapshot dependency versions to the locked timestamp versions used in the build. For
Expand Down Expand Up @@ -96,13 +100,13 @@ protected void update(ModifiedPomXMLEventReader pom)
if (getProject().getParent() != null && isProcessingParent()) {
lockParentSnapshot(pom, getProject().getParent());
}
} catch (IOException e) {
} catch (IOException | VersionResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}

private void lockSnapshots(ModifiedPomXMLEventReader pom, Collection<Dependency> dependencies)
throws XMLStreamException, MojoExecutionException {
protected void lockSnapshots(ModifiedPomXMLEventReader pom, Collection<Dependency> dependencies)
throws XMLStreamException, MojoExecutionException, VersionResolutionException {
for (Dependency dep : dependencies) {
if (isExcludeReactor() && isProducedByReactor(dep)) {
getLog().info("Ignoring reactor dependency: " + toString(dep));
Expand All @@ -121,24 +125,26 @@ private void lockSnapshots(ModifiedPomXMLEventReader pom, Collection<Dependency>
String version = dep.getVersion();
Matcher versionMatcher = TIMESTAMPED_SNAPSHOT_REGEX.matcher(version);
if (versionMatcher.find() && versionMatcher.end() == version.length()) {
String lockedVersion = resolveSnapshotVersion(dep);
if (!version.equals(lockedVersion)) {
Optional<String> lockedVersion = resolveSnapshotVersion(dep);
if (lockedVersion.isPresent()) {
if (PomHelper.setDependencyVersion(
pom,
dep.getGroupId(),
dep.getArtifactId(),
version,
lockedVersion,
lockedVersion.get(),
getProject().getModel())) {
getLog().info("Locked " + toString(dep) + " to version " + lockedVersion);
getLog().info("Locked " + toString(dep) + " to version " + lockedVersion.get());
}
} else {
getLog().info("No timestamped version for " + toString(dep) + " found.");
}
}
}
}

private void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject parent)
throws XMLStreamException, MojoExecutionException {
protected void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject parent)
throws XMLStreamException, VersionResolutionException {
if (parent == null) {
getLog().info("Project does not have a parent");
return;
Expand All @@ -154,65 +160,46 @@ private void lockParentSnapshot(ModifiedPomXMLEventReader pom, MavenProject pare

Matcher versionMatcher = TIMESTAMPED_SNAPSHOT_REGEX.matcher(parentVersion);
if (versionMatcher.find() && versionMatcher.end() == parentVersion.length()) {
String lockedParentVersion = resolveSnapshotVersion(parentArtifact);
if (!parentVersion.equals(lockedParentVersion)) {
if (PomHelper.setProjectParentVersion(pom, lockedParentVersion)) {
getLog().info("Locked parent " + parentArtifact + " to version " + lockedParentVersion);
Optional<String> lockedParentVersion = resolveSnapshotVersion(parentArtifact);
if (lockedParentVersion.isPresent()) {
if (PomHelper.setProjectParentVersion(pom, lockedParentVersion.get())) {
getLog().info("Locked parent " + parentArtifact + " to version " + lockedParentVersion.get());
}
} else {
getLog().info("No timestamped version for " + parentArtifact + " found.");
}
}
}

/**
* Determine the timestamp version of the snapshot artifact used in the build.
*
* @param artifact
* @return The timestamp version if exists, otherwise the original snapshot artifact version is returned.
* @param artifact artifact for which to retrieve the locked version
* @return The timestamp version if exists, otherwise {@link Optional#empty()}
* @throws VersionResolutionException thrown if version resolution fails
*/
private String resolveSnapshotVersion(Artifact artifact) {
private Optional<String> resolveSnapshotVersion(Artifact artifact) throws VersionResolutionException {
getLog().debug("Resolving snapshot version for artifact: " + artifact);

String lockedVersion = artifact.getVersion();

try {
aetherRepositorySystem.resolveArtifact(
session.getRepositorySession(),
new ArtifactRequest(
RepositoryUtils.toArtifact(artifact),
getProject().getRemoteProjectRepositories(),
getClass().getName()));

lockedVersion = artifact.getVersion();
} catch (Exception e) {
getLog().error(e);
}
return lockedVersion;
VersionResult versionResult = aetherRepositorySystem.resolveVersion(
session.getRepositorySession(),
new VersionRequest(
RepositoryUtils.toArtifact(artifact),
getProject().getRemoteProjectRepositories(),
getClass().getSimpleName()));
return Optional.ofNullable(versionResult.getVersion())
.filter(v -> !String.valueOf(artifact.getVersion()).equals(v));
}

/**
* Determine the timestamp version of the snapshot dependency used in the build.
*
* @param dep
* @return The timestamp version if exists, otherwise the original snapshot dependency version is returned.
* @param dep dependency for which to retrieve the locked version
* @return The timestamp version if exists, otherwise {@link Optional#empty()}
* @throws MojoExecutionException thrown if retrieval of {@link VersionsHelper} fails
* @throws VersionResolutionException thrown if version resolution fails
*/
private String resolveSnapshotVersion(Dependency dep) {
getLog().debug("Resolving snapshot version for dependency: " + dep);

String lockedVersion = dep.getVersion();

try {
Artifact depArtifact = getHelper().createDependencyArtifact(dep);
aetherRepositorySystem.resolveArtifact(
session.getRepositorySession(),
new ArtifactRequest(
RepositoryUtils.toArtifact(depArtifact),
getProject().getRemoteProjectRepositories(),
getClass().getName()));

lockedVersion = depArtifact.getVersion();
} catch (Exception e) {
getLog().error(e);
}
return lockedVersion;
private Optional<String> resolveSnapshotVersion(Dependency dep)
throws MojoExecutionException, VersionResolutionException {
return resolveSnapshotVersion(getHelper().createDependencyArtifact(dep));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.codehaus.mojo.versions;
/*
* Copyright MojoHaus and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import javax.xml.stream.XMLStreamException;

import java.util.List;
import java.util.function.UnaryOperator;

import org.apache.maven.artifact.DefaultArtifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub;
import org.apache.maven.project.MavenProject;
import org.codehaus.mojo.versions.api.PomHelper;
import org.codehaus.mojo.versions.utils.DependencyBuilder;
import org.codehaus.mojo.versions.utils.MockUtils;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.resolution.VersionRequest;
import org.eclipse.aether.resolution.VersionResolutionException;
import org.eclipse.aether.resolution.VersionResult;
import org.junit.Test;
import org.mockito.MockedStatic;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

/**
* Unit tests for {@link LockSnapshotsMojo}
*/
public class LockSnapshotsMojoTest {

private LockSnapshotsMojo createMojo(RepositorySystem repositorySystem) {
return new LockSnapshotsMojo(null, repositorySystem, null, null) {
{
reactorProjects = emptyList();
project = new MavenProject(new Model() {
{
setGroupId("default-group");
setArtifactId("default-project");
setVersion("1.0-SNAPSHOT");
}

@Override
public void setDependencies(List<Dependency> dependencies) {
super.setDependencies(singletonList(
DependencyBuilder.dependencyWith("default-group", "default-artifact", "1.0-SNAPSHOT")));
}
});
session = MockUtils.mockMavenSession();
}
};
}

private RepositorySystem mockRepositorySystem(UnaryOperator<String> versionProducer)
throws VersionResolutionException {
RepositorySystem repositorySystem = mock(RepositorySystem.class);
when(repositorySystem.resolveVersion(any(), any())).then(i -> {
VersionRequest request = i.getArgument(1);
return new VersionResult(request)
.setVersion(versionProducer.apply(request.getArtifact().getVersion()));
});
return repositorySystem;
}

@Test
public void testNoTimestampedDependencyFoundNull()
throws XMLStreamException, MojoExecutionException, VersionResolutionException {
RepositorySystem repositorySystem = mockRepositorySystem(v -> null);

LockSnapshotsMojo mojo = createMojo(repositorySystem);
try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
pomHelper
.when(() -> PomHelper.setDependencyVersion(any(), any(), any(), any(), any(), any()))
.thenThrow(new RuntimeException("Not supposed to modify the dependency"));
mojo.lockSnapshots(null, mojo.project.getDependencies());
}
}

@Test
public void testNoTimestampedDependencyFoundSameVersion()
throws XMLStreamException, MojoExecutionException, VersionResolutionException {
RepositorySystem repositorySystem = mockRepositorySystem(UnaryOperator.identity());

LockSnapshotsMojo mojo = createMojo(repositorySystem);
try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
pomHelper
.when(() -> PomHelper.setDependencyVersion(any(), any(), any(), any(), any(), any()))
.thenThrow(new RuntimeException("Not supposed to modify the dependency"));
mojo.lockSnapshots(null, mojo.project.getDependencies());
}
}

@Test
public void testNoTimestampedParentFoundNull()
throws XMLStreamException, MojoExecutionException, VersionResolutionException {
RepositorySystem repositorySystem = mockRepositorySystem(v -> null);

LockSnapshotsMojo mojo = createMojo(repositorySystem);
try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
pomHelper
.when(() -> PomHelper.setProjectParentVersion(any(), any()))
.thenThrow(new RuntimeException("Not supposed to modify the parent"));
mojo.lockParentSnapshot(
null,
new MavenProject(new Model() {
{
setGroupId("default-group");
setArtifactId("default-parent");
setVersion("1.0-SNAPSHOT");
}
}) {
{
setArtifact(new DefaultArtifact(
"default-group",
"default-parent",
"1.0-SNAPSHOT",
"compile",
"pom",
null,
new DefaultArtifactHandlerStub("jar")));
}
});
}
}

@Test
public void testNoTimestampedParentFoundSameVersion()
throws XMLStreamException, MojoExecutionException, VersionResolutionException {
RepositorySystem repositorySystem = mockRepositorySystem(UnaryOperator.identity());

LockSnapshotsMojo mojo = createMojo(repositorySystem);
try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
pomHelper
.when(() -> PomHelper.setProjectParentVersion(any(), any()))
.thenThrow(new RuntimeException("Not supposed to modify the parent"));
mojo.lockParentSnapshot(
null,
new MavenProject(new Model() {
{
setGroupId("default-group");
setArtifactId("default-parent");
setVersion("1.0-SNAPSHOT");
}
}) {
{
setArtifact(new DefaultArtifact(
"default-group",
"default-parent",
"1.0-SNAPSHOT",
"compile",
"pom",
null,
new DefaultArtifactHandlerStub("jar")));
}
});
}
}
}

0 comments on commit a6baf81

Please sign in to comment.