Skip to content

Remove spring managed dependency with version consideration #602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 25, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

import io.micrometer.core.lang.Nullable;
import lombok.*;
import org.openrewrite.semver.LatestRelease;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Getter
Expand Down Expand Up @@ -66,6 +68,15 @@ public String toString() {
"</dependency>";
}

public boolean isRecentThen(Dependency that){
return this.equals(that) && comparator().compare(this, that) >= 0;
}

private Comparator<Dependency> comparator(){
LatestRelease latestRelease = new LatestRelease(null);
return Comparator.comparing(Dependency::getVersion, latestRelease::compare);
}

private String exclusionString() {
if (exclusions.isEmpty()) {
return "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.springframework.sbm.build.api;

import org.openrewrite.maven.internal.MavenPomDownloader;
import org.openrewrite.maven.tree.GroupArtifactVersion;
import org.openrewrite.maven.tree.MavenRepository;
import org.springframework.sbm.openrewrite.RewriteExecutionContext;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
* This class holds all the dependencies included in a spring artifact
*/
public class SpringManagedDependencies {

private static List<MavenRepository> SPRING_REPOSITORIES = List.of(
new MavenRepository("spring-release", "https://repo.spring.io/release", true, false, null, null)
);

private List<org.openrewrite.maven.tree.Dependency> dependencies;
private static Map<GroupArtifactVersion, SpringManagedDependencies> INSTANCES = new HashMap<>();

public static SpringManagedDependencies by(String groupId, String artifact, String version){
final GroupArtifactVersion groupArtifactVersion =
new GroupArtifactVersion(groupId, artifact, version);

INSTANCES.computeIfAbsent(groupArtifactVersion, SpringManagedDependencies::new);
return INSTANCES.get(groupArtifactVersion);
}

private SpringManagedDependencies(GroupArtifactVersion groupArtifactVersion){
dependencies = new MavenPomDownloader(Collections.emptyMap(), new RewriteExecutionContext())
.download(groupArtifactVersion, null, null, SPRING_REPOSITORIES)
.getDependencies();
}

public Stream<Dependency> stream(){
return dependencies.stream()
.map(d -> Dependency.builder()
.groupId(d.getGroupId())
.artifactId(d.getArtifactId())
.version(d.getVersion())
.build()
);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.springframework.sbm.build.migration.actions;

import org.springframework.sbm.build.api.Dependency;
import org.springframework.sbm.build.api.SpringManagedDependencies;
import org.springframework.sbm.engine.context.ProjectContext;
import org.springframework.sbm.engine.recipe.AbstractAction;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.openrewrite.maven.tree.Scope.Compile;

/**
* The action removes the dependencies directly managed by Spring from the project dependencies
* Add this action at the end of recipe so that any spring artifact inclusions as part of the
* other actions are also included while removing the dependencies.
*/
public class RemoveManagedDependencies extends AbstractAction {

@Override
public void apply(ProjectContext context) {
//FIXME handle multi-module projects
final List<Dependency> springManagedDependencies = context.getBuildFile()
.getDeclaredDependencies(Compile)
.stream()
.filter(this::isSpringFrameworkDependency)
.map(d -> SpringManagedDependencies.by(d.getGroupId(),d.getArtifactId(),d.getVersion()))
.flatMap(SpringManagedDependencies::stream)
.distinct()
.collect(Collectors.toList());

Predicate<Dependency> isAlreadyManagedBySpring = d -> springManagedDependencies
.stream()
.filter(d::equals)
.anyMatch(s -> s.isRecentThen(d));

final List<Dependency> dependenciesToBeRemoved = context.getBuildFile()
.getDeclaredDependencies(Compile)
.stream()
.filter(isAlreadyManagedBySpring)
.collect(Collectors.toList());

RemoveDependencies removeDependenciesAction = new RemoveDependencies();
removeDependenciesAction.setDependencies(dependenciesToBeRemoved);
removeDependenciesAction.apply(context);
}

private boolean isSpringFrameworkDependency(Dependency dependency){
return dependency.getGroupId().startsWith("org.springframework");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.springframework.sbm.build.api;

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class SpringManagedDependenciesTest {

@Test
public void pullBootStarter274Dependencies_expectJakartaAnnotationDependency(){
String jakartaCoordinates = "jakarta.annotation:jakarta.annotation-api:1.3.5";

assertThat( SpringManagedDependencies.by("org.springframework.boot", "spring-boot-starter", "2.7.4")
.stream()
.map(Dependency::getCoordinates)
.anyMatch(jakartaCoordinates::equals)
).isTrue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.springframework.sbm.build.migration.actions;

import org.junit.jupiter.api.Test;
import org.openrewrite.semver.LatestRelease;
import org.springframework.sbm.build.api.Dependency;
import org.springframework.sbm.engine.context.ProjectContext;
import org.springframework.sbm.project.resource.TestProjectContext;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class RemoveManagedDependenciesTest {

@Test
public void givenProjectWithManagedDependency_removeSpringManagedDependencies_expectHibernateDependencyRemoved(){

LatestRelease latestRelease = new LatestRelease(null);
System.out.println(latestRelease.compare(null, "5.6.11.Final", "5.6.11.Final"));

final String hibernateCoordinates = "org.hibernate:hibernate-core:5.6.11.Final";
final String springBootDataJpaCoordinates = "org.springframework.boot:spring-boot-starter-data-jpa:2.7.4";

final ProjectContext projectContext = TestProjectContext.buildProjectContext()
.withBuildFileHavingDependencies(hibernateCoordinates, springBootDataJpaCoordinates)
.build();

RemoveManagedDependencies removeManagedDependencies = new RemoveManagedDependencies();
removeManagedDependencies.apply(projectContext);

assertThat(projectContext.getBuildFile()
.getDeclaredDependencies()
.stream()
.map(Dependency::getCoordinates)
.anyMatch(hibernateCoordinates::equals)
).isFalse();
}

@Test
public void givenProjectWithLowerVersionedManagedDependency_removeSpringManagedDependencies_expectDependencyRemoved(){
final String hibernateCoordinates = "org.hibernate:hibernate-core:5.3.2.Final";
final String springBootDataJpaCoordinates = "org.springframework.boot:spring-boot-starter-data-jpa:2.7.4";

final ProjectContext projectContext = TestProjectContext.buildProjectContext()
.withBuildFileHavingDependencies(hibernateCoordinates, springBootDataJpaCoordinates)
.build();

RemoveManagedDependencies removeManagedDependencies = new RemoveManagedDependencies();
removeManagedDependencies.apply(projectContext);

assertThat(projectContext.getBuildFile()
.getDeclaredDependencies()
.stream()
.map(Dependency::getCoordinates)
.anyMatch(hibernateCoordinates::equals)
).isFalse();
}

@Test
public void givenProjectWithHigherVersionedManagedDependency_removeSpringManagedDependencies_expectDependencyRemoved(){
final String hibernateCoordinates = "org.hibernate:hibernate-core:5.12.2.Final";
final String springBootDataJpaCoordinates = "org.springframework.boot:spring-boot-starter-data-jpa:2.7.4";

final ProjectContext projectContext = TestProjectContext.buildProjectContext()
.withBuildFileHavingDependencies(hibernateCoordinates, springBootDataJpaCoordinates)
.build();

RemoveManagedDependencies removeManagedDependencies = new RemoveManagedDependencies();
removeManagedDependencies.apply(projectContext);

assertThat(projectContext.getBuildFile()
.getDeclaredDependencies()
.stream()
.map(Dependency::getCoordinates)
.anyMatch(hibernateCoordinates::equals)
).isTrue();
}
}