#159 - Dependency upgrade automation.
Add commands to check for dependency upgrades and to perform an upgrade for Spring Data Build.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ application-local.properties
|
|||||||
spring-shell.log
|
spring-shell.log
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
|
dependency-upgrade-*.properties
|
||||||
|
|||||||
@@ -83,3 +83,16 @@ $ tracker archive $trainIteration.previous
|
|||||||
$ github update labels $project
|
$ github update labels $project
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Dependency Upgrade
|
||||||
|
|
||||||
|
`ProjectDependencies` contains a per-project configuration of dependencies.
|
||||||
|
|
||||||
|
Workflow:
|
||||||
|
|
||||||
|
* Check for dependency upgrades `$ dependency check $trainIteration`
|
||||||
|
|
||||||
|
Reports upgradable dependencies for Build and Modules and creates `dependency-upgrade-build.properties` file.
|
||||||
|
Edit `dependency-upgrade-build.properties` to specify the dependency version to upgrade. Removing a line will omit that dependency upgrade.
|
||||||
|
|
||||||
|
* Apply dependency upgrade with `$ dependency upgrade $trainIteration`. Applies dependency upgrades currently only to Spring Data Build.
|
||||||
|
* Report store-specific dependencies to Spring Boot's current upgrade ticket ([sample](https://github.com/spring-projects/spring-boot/issues/24036)) `$ dependency report $trainIteration`
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ public interface Pom {
|
|||||||
@XBWrite("/project/properties/{0}")
|
@XBWrite("/project/properties/{0}")
|
||||||
void setProperty(String property, @XBValue ArtifactVersion value);
|
void setProperty(String property, @XBValue ArtifactVersion value);
|
||||||
|
|
||||||
|
@XBWrite("/project/properties/{0}")
|
||||||
|
void setProperty(String property, @XBValue String value);
|
||||||
|
|
||||||
@XBWrite("/project/repositories/repository[id=\"{0}\"]/id")
|
@XBWrite("/project/repositories/repository[id=\"{0}\"]/id")
|
||||||
void setRepositoryId(String oldId, @XBValue String newId);
|
void setRepositoryId(String oldId, @XBValue String newId);
|
||||||
|
|
||||||
@@ -63,6 +66,9 @@ public interface Pom {
|
|||||||
@XBWrite("/project/dependencies/dependency[artifactId=\"{0}\"]/version")
|
@XBWrite("/project/dependencies/dependency[artifactId=\"{0}\"]/version")
|
||||||
Pom setDependencyVersion(String artifactId, @XBValue ArtifactVersion version);
|
Pom setDependencyVersion(String artifactId, @XBValue ArtifactVersion version);
|
||||||
|
|
||||||
|
@XBRead("/project/dependencies/dependency[artifactId=\"{0}\"]/version")
|
||||||
|
String getDependencyVersion(String artifactId);
|
||||||
|
|
||||||
@XBWrite("/project/dependencyManagement/dependencies/dependency[artifactId=\"{0}\"]/version")
|
@XBWrite("/project/dependencyManagement/dependencies/dependency[artifactId=\"{0}\"]/version")
|
||||||
Pom setDependencyManagementVersion(String artifactId, @XBValue ArtifactVersion version);
|
Pom setDependencyManagementVersion(String artifactId, @XBValue ArtifactVersion version);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class Dependencies {
|
||||||
|
|
||||||
|
static final List<Dependency> dependencies = new ArrayList<>();
|
||||||
|
|
||||||
|
public static final Dependency APT = Dependency.of("APT", "com.mysema.maven:apt-maven-plugin");
|
||||||
|
|
||||||
|
public static final Dependency ASPECTJ = Dependency.of("AspectJ", "org.aspectj:aspectjrt");
|
||||||
|
|
||||||
|
public static final Dependency ASSERTJ = Dependency.of("AssertJ", "org.assertj:assertj-core");
|
||||||
|
|
||||||
|
public static final Dependency JACKSON = Dependency.of("Jackson", "com.fasterxml.jackson:jackson-bom");
|
||||||
|
|
||||||
|
public static final Dependency JACOCO = Dependency.of("Jacoco", "org.jacoco:jacoco");
|
||||||
|
|
||||||
|
public static final Dependency JODA_TIME = Dependency.of("Joda Time", "joda-time:joda-time");
|
||||||
|
|
||||||
|
public static final Dependency JUNIT5 = Dependency.of("JUnit", "org.junit:junit-bom");
|
||||||
|
|
||||||
|
public static final Dependency JUNIT4 = Dependency.of("JUnit", "junit:junit");
|
||||||
|
|
||||||
|
public static final Dependency KOTLIN = Dependency.of("Kotlin", "org.jetbrains.kotlin:kotlin-bom");
|
||||||
|
|
||||||
|
public static final Dependency KOTLIN_COROUTINES = Dependency.of("Kotlin Coroutines",
|
||||||
|
"org.jetbrains.kotlinx:kotlinx-coroutines-bom");
|
||||||
|
|
||||||
|
public static final Dependency MOCKITO = Dependency.of("Mockito", "org.mockito:mockito-core");
|
||||||
|
|
||||||
|
public static final Dependency MOCKK = Dependency.of("Mockk", "io.mockk:mockk");
|
||||||
|
|
||||||
|
public static final Dependency QUERYDSL = Dependency.of("Querydsl", "com.querydsl:querydsl-jpa");
|
||||||
|
|
||||||
|
public static final Dependency PROJECT_REACTOR = Dependency.of("Project Reactor", "io.projectreactor:reactor-bom");
|
||||||
|
|
||||||
|
public static final Dependency RXJAVA1 = Dependency.of("RxJava", "io.reactivex:rxjava");
|
||||||
|
|
||||||
|
public static final Dependency RXJAVA2 = Dependency.of("RxJava", "io.reactivex.rxjava2:rxjava");
|
||||||
|
|
||||||
|
public static final Dependency RXJAVA3 = Dependency.of("RxJava", "io.reactivex.rxjava3:rxjava");
|
||||||
|
|
||||||
|
public static final Dependency RXJAVA_RS = Dependency.of("RxJava Reactive Streams",
|
||||||
|
"io.reactivex:rxjava-reactive-streams");
|
||||||
|
|
||||||
|
public static final Dependency SPRING_FRAMEWORK = Dependency.of("Spring Framework",
|
||||||
|
"org.springframework:spring-core");
|
||||||
|
|
||||||
|
public static final Dependency SPRING_HATEOAS = Dependency.of("Spring Hateoas",
|
||||||
|
"org.springframework.hateoas:spring-hateoas");
|
||||||
|
|
||||||
|
public static final Dependency SPRING_PLUGIN = Dependency.of("Spring Plugin",
|
||||||
|
"org.springframework.plugin:spring-plugin");
|
||||||
|
|
||||||
|
public static final Dependency TESTCONTAINERS = Dependency.of("Testcontainers", "org.testcontainers:testcontainers");
|
||||||
|
|
||||||
|
public static final Dependency THREE_TEN_BP = Dependency.of("ThreeTenBp", "org.threeten:threetenbp");
|
||||||
|
|
||||||
|
public static final Dependency OPEN_WEB_BEANS = Dependency.of("OpenWebBeans", "org.apache.openwebbeans:openwebbeans");
|
||||||
|
|
||||||
|
public static final Dependency VAVR = Dependency.of("Vavr", "io.vavr:vavr").excludeVersionStartingWith("1.0.0-alpha");
|
||||||
|
|
||||||
|
public static final Dependency XML_BEAM = Dependency.of("XMLBeam", "org.xmlbeam:xmlprojector");
|
||||||
|
|
||||||
|
public static final Dependency MONGODB_CORE = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-core");
|
||||||
|
|
||||||
|
public static final Dependency MONGODB_LEGACY = Dependency.of("MongoDB", "org.mongodb:mongo-java-driver");
|
||||||
|
|
||||||
|
public static final Dependency MONGODB_SYNC = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-sync");
|
||||||
|
|
||||||
|
public static final Dependency MONGODB_ASYNC = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-async");
|
||||||
|
|
||||||
|
public static final Dependency MONGODB_RS = Dependency.of("MongoDB Reactive Streams",
|
||||||
|
"org.mongodb:mongodb-driver-reactivestreams");
|
||||||
|
|
||||||
|
public static final Dependency LETTUCE = Dependency.of("Lettuce", "io.lettuce:lettuce-core");
|
||||||
|
|
||||||
|
public static final Dependency JEDIS = Dependency.of("Jedis", "redis.clients:jedis");
|
||||||
|
|
||||||
|
public static final Dependency CASSANDRA_DRIVER3 = Dependency.of("Cassandra Driver",
|
||||||
|
"com.datastax.cassandra:cassandra-driver-core");
|
||||||
|
|
||||||
|
public static final Dependency CASSANDRA_DRIVER4 = Dependency.of("Cassandra Driver",
|
||||||
|
"com.datastax.oss:java-driver-core");
|
||||||
|
|
||||||
|
public static final Dependency NEO4J_OGM = Dependency.of("Neo4j OGM", "org.neo4j:neo4j-ogm-api");
|
||||||
|
|
||||||
|
public static final Dependency NEO4J_DRIVER = Dependency.of("Neo4j Driver", "org.neo4j.driver:neo4j-java-driver");
|
||||||
|
|
||||||
|
public static final Dependency COUCHBASE = Dependency.of("Couchbase Client", "com.couchbase.client:java-client");
|
||||||
|
|
||||||
|
public static final Dependency ELASTICSEARCH = Dependency.of("Elasticsearch",
|
||||||
|
"org.elasticsearch.client:elasticsearch-rest-high-level-client");
|
||||||
|
|
||||||
|
public static final Dependency SPRING_LDAP = Dependency.of("Spring LDAP",
|
||||||
|
"org.springframework.ldap:spring-ldap-core");
|
||||||
|
|
||||||
|
static {
|
||||||
|
|
||||||
|
ReflectionUtils.doWithFields(Dependencies.class, field -> {
|
||||||
|
|
||||||
|
// ignore dependencies constant
|
||||||
|
if (field.getName().toLowerCase().equals(field.getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies.add((Dependency) ReflectionUtils.getField(field, null));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dependency getRequiredByName(String name) {
|
||||||
|
|
||||||
|
return dependencies.stream().filter(it -> it.getName().equals(name)).findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format("No such dependency: %s", name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dependency getRequiredByArtifactId(String artifactId) {
|
||||||
|
|
||||||
|
return dependencies.stream().filter(it -> it.getArtifactId().equals(artifactId)).findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String.format("No such dependency: %s", artifactId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@Value
|
||||||
|
public class Dependency implements Comparable<Dependency> {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String groupId, artifactId;
|
||||||
|
Predicate<String> exclusions;
|
||||||
|
|
||||||
|
public static Dependency of(String name, String ga) {
|
||||||
|
|
||||||
|
Assert.hasText(name, "Name must not be empty");
|
||||||
|
Assert.hasText(ga, "GroupId/ArtifactId must not be empty");
|
||||||
|
Assert.isTrue(ga.indexOf(':') != -1, "GroupId/ArtifactId must be in the format of org.group:artifact-id");
|
||||||
|
|
||||||
|
String[] parts = ga.split(":");
|
||||||
|
|
||||||
|
return new Dependency(name, parts[0], parts[1], it -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dependency excludeVersionStartingWith(String identifier) {
|
||||||
|
return new Dependency(name, groupId, artifactId, it -> it.startsWith(identifier) || exclusions.test(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldInclude(String identifier) {
|
||||||
|
return !exclusions.test(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Dependency o) {
|
||||||
|
return name.compareTo(o.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.data.release.CliComponent;
|
||||||
|
import org.springframework.data.release.TimedCommand;
|
||||||
|
import org.springframework.data.release.git.GitOperations;
|
||||||
|
import org.springframework.data.release.model.Project;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
import org.springframework.data.release.model.TrainIteration;
|
||||||
|
import org.springframework.data.release.utils.Logger;
|
||||||
|
import org.springframework.shell.core.annotation.CliCommand;
|
||||||
|
import org.springframework.shell.core.annotation.CliOption;
|
||||||
|
import org.springframework.shell.support.table.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shell commands for dependency management.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@CliComponent
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||||
|
public class DependencyCommands extends TimedCommand {
|
||||||
|
|
||||||
|
public static final String BUILD_PROPERTIES = "dependency-upgrade-build.properties";
|
||||||
|
DependencyOperations operations;
|
||||||
|
GitOperations git;
|
||||||
|
Logger logger;
|
||||||
|
|
||||||
|
@CliCommand(value = "dependency check")
|
||||||
|
public void check(@CliOption(key = "", mandatory = true) TrainIteration iteration,
|
||||||
|
@CliOption(key = "all", mandatory = false) Boolean reportAll) throws IOException {
|
||||||
|
|
||||||
|
git.checkout(iteration.getTrain());
|
||||||
|
|
||||||
|
checkBuildDependencies(iteration, reportAll != null ? reportAll : false);
|
||||||
|
checkModuleDependencies(iteration, reportAll != null ? reportAll : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a dependency report for all store modules to be used typically in Spring Boot upgrade tickets.
|
||||||
|
*
|
||||||
|
* @param iteration
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@CliCommand(value = "dependency report")
|
||||||
|
public String report(@CliOption(key = "", mandatory = true) TrainIteration iteration) {
|
||||||
|
|
||||||
|
git.checkout(iteration.getTrain());
|
||||||
|
|
||||||
|
List<Project> projects = Projects.all().stream()
|
||||||
|
.filter(it -> it != Projects.BOM && it != Projects.BUILD && it != Projects.COMMONS)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> dependencies = new TreeMap<>();
|
||||||
|
|
||||||
|
for (Project project : projects) {
|
||||||
|
dependencies.putAll(operations.getCurrentDependencies(project));
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder report = new StringBuilder();
|
||||||
|
|
||||||
|
report.append(System.lineSeparator()).append("Project Dependencies Spring Data ")
|
||||||
|
.append(iteration.getReleaseTrainNameAndVersion()).append(System.lineSeparator())
|
||||||
|
.append(System.lineSeparator());
|
||||||
|
|
||||||
|
dependencies.forEach((dependency, dependencyVersion) -> {
|
||||||
|
|
||||||
|
report.append(String.format("* %s (%s:%s): %s", dependency.getName(), dependency.getGroupId(),
|
||||||
|
dependency.getArtifactId(), dependencyVersion.getIdentifier())).append(System.lineSeparator());
|
||||||
|
});
|
||||||
|
|
||||||
|
return report.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CliCommand(value = "dependency upgrade")
|
||||||
|
public void check(@CliOption(key = "", mandatory = true) TrainIteration iteration)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
git.checkout(iteration.getTrain());
|
||||||
|
logger.log(iteration, "Applying dependency upgrades to Spring Data Build");
|
||||||
|
|
||||||
|
Properties properties = loadDependencyUpgrades(iteration);
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> dependencyUpgrades = DependencyUpgradeProposals.fromProperties(iteration,
|
||||||
|
properties);
|
||||||
|
|
||||||
|
operations.createUpgradeTickets(iteration, Projects.BUILD, dependencyUpgrades);
|
||||||
|
operations.upgradeDependencies(iteration, Projects.BUILD, dependencyUpgrades);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Properties loadDependencyUpgrades(@CliOption(key = "", mandatory = true) TrainIteration iteration)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
if (!Files.exists(Paths.get(BUILD_PROPERTIES))) {
|
||||||
|
logger.log(iteration, "Cannot upgrade dependencies: " + BUILD_PROPERTIES + " does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try (FileInputStream fis = new FileInputStream(BUILD_PROPERTIES)) {
|
||||||
|
properties.load(fis);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkModuleDependencies(TrainIteration iteration, boolean reportAll) throws IOException {
|
||||||
|
|
||||||
|
String propertiesFile = "dependency-upgrade-modules.properties";
|
||||||
|
|
||||||
|
List<Project> projects = Projects.all().stream().filter(it -> it != Projects.BOM && it != Projects.BUILD)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposals proposals = DependencyUpgradeProposals.empty();
|
||||||
|
|
||||||
|
for (Project project : projects) {
|
||||||
|
proposals = proposals.mergeWith(operations.getDependencyUpgradeProposals(project, iteration.getIteration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.write(Paths.get(propertiesFile), proposals.asProperties(iteration).getBytes());
|
||||||
|
|
||||||
|
Table summary = proposals.toTable(reportAll);
|
||||||
|
|
||||||
|
logger.log(iteration, "Upgrade summary:" + System.lineSeparator() + System.lineSeparator() + summary);
|
||||||
|
logger.log(iteration, "Upgrade proposals written to " + propertiesFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBuildDependencies(TrainIteration iteration, boolean reportAll) throws IOException {
|
||||||
|
|
||||||
|
String propertiesFile = BUILD_PROPERTIES;
|
||||||
|
|
||||||
|
DependencyUpgradeProposals proposals = operations.getDependencyUpgradeProposals(Projects.BUILD,
|
||||||
|
iteration.getIteration());
|
||||||
|
|
||||||
|
Files.write(Paths.get(propertiesFile), proposals.asProperties(iteration).getBytes());
|
||||||
|
|
||||||
|
Table summary = proposals.toTable(reportAll);
|
||||||
|
|
||||||
|
logger.log(Projects.BUILD, "Upgrade summary:" + System.lineSeparator() + System.lineSeparator() + summary);
|
||||||
|
logger.log(iteration, "Upgrade proposals written to " + propertiesFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.data.release.CliComponent;
|
||||||
|
import org.springframework.data.release.build.Pom;
|
||||||
|
import org.springframework.data.release.git.GitOperations;
|
||||||
|
import org.springframework.data.release.io.Workspace;
|
||||||
|
import org.springframework.data.release.issues.IssueTracker;
|
||||||
|
import org.springframework.data.release.issues.Ticket;
|
||||||
|
import org.springframework.data.release.issues.Tickets;
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
import org.springframework.data.release.model.ModuleIteration;
|
||||||
|
import org.springframework.data.release.model.Project;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
import org.springframework.data.release.model.TrainIteration;
|
||||||
|
import org.springframework.data.release.utils.ExecutionUtils;
|
||||||
|
import org.springframework.data.release.utils.Logger;
|
||||||
|
import org.springframework.data.util.Streamable;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.plugin.core.PluginRegistry;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.client.RestOperations;
|
||||||
|
|
||||||
|
import org.xmlbeam.ProjectionFactory;
|
||||||
|
import org.xmlbeam.annotation.XBRead;
|
||||||
|
import org.xmlbeam.io.XBFileIO;
|
||||||
|
import org.xmlbeam.io.XBStreamInput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operations for dependency management.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@CliComponent
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DependencyOperations {
|
||||||
|
|
||||||
|
public static final Pattern REPO_MAVEN_ORG_DIR_LISTING = Pattern
|
||||||
|
.compile("<a (?>[^>]+)>([^\\/]+)\\/<\\/a>(?>\\s*)(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2})(?>\\s*)(?>-)");
|
||||||
|
|
||||||
|
public static final DateTimeFormatter DIR_LISTING_TIME_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
|
||||||
|
|
||||||
|
private final ProjectionFactory projectionFactory;
|
||||||
|
private final GitOperations gitOperations;
|
||||||
|
private final Workspace workspace;
|
||||||
|
private final PluginRegistry<IssueTracker, Project> tracker;
|
||||||
|
private final ExecutorService executor;
|
||||||
|
private final RestOperations restOperations;
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain dependency upgrade proposals for {@link Project} and {@link Iteration}. Considers dependency upgrade rules
|
||||||
|
* according to minor/bugfix release increments. Also, SemVer with modifier are only allowed in milestone versions.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* @param iteration
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DependencyUpgradeProposals getDependencyUpgradeProposals(Project project, Iteration iteration) {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> currentDependencies = getCurrentDependencies(project);
|
||||||
|
Map<Dependency, DependencyUpgradeProposal> proposals = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||||
|
|
||||||
|
ExecutionUtils.run(executor, Streamable.of(currentDependencies.keySet()), dependency -> {
|
||||||
|
|
||||||
|
DependencyVersion currentVersion = currentDependencies.get(dependency);
|
||||||
|
List<DependencyVersion> versions = getAvailableVersions(dependency);
|
||||||
|
DependencyUpgradeProposal proposal = getDependencyUpgradeProposal(iteration, currentVersion, versions);
|
||||||
|
|
||||||
|
proposals.put(dependency, proposal);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new DependencyUpgradeProposals(proposals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures there's a upgrade ticket for each dependency to upgrade.
|
||||||
|
*
|
||||||
|
* @param iteration
|
||||||
|
* @param project
|
||||||
|
* @param dependencyVersions
|
||||||
|
*/
|
||||||
|
public void createUpgradeTickets(TrainIteration iteration, Project project,
|
||||||
|
Map<Dependency, DependencyVersion> dependencyVersions) {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> upgrades = getDependencyUpgradesToApply(project, dependencyVersions);
|
||||||
|
|
||||||
|
IssueTracker tracker = this.tracker.getRequiredPluginFor(project);
|
||||||
|
Tickets tickets = tracker.getTicketsFor(iteration);
|
||||||
|
|
||||||
|
upgrades.forEach((dependency, dependencyVersion) -> {
|
||||||
|
|
||||||
|
String upgradeTicketSummary = getUpgradeTicketSummary(dependency, dependencyVersion);
|
||||||
|
Optional<Ticket> upgradeTicket = getDependencyUpgradeTicket(tickets, upgradeTicketSummary);
|
||||||
|
|
||||||
|
if (upgradeTicket.isPresent()) {
|
||||||
|
logger.log(project, "Found upgrade ticket %s", upgradeTicket.get());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ModuleIteration module = iteration.getModule(project);
|
||||||
|
|
||||||
|
logger.log(module, "Creating upgrade ticket for %s", upgradeTicketSummary);
|
||||||
|
tracker.createTicket(module, upgradeTicketSummary);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies dependencies to upgrade, applies the upgrade, creates a commit, pushes the repository and resolves the
|
||||||
|
* upgrade ticket.
|
||||||
|
*
|
||||||
|
* @param iteration
|
||||||
|
* @param project
|
||||||
|
* @param dependencyVersions
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
public void upgradeDependencies(TrainIteration iteration, Project project,
|
||||||
|
Map<Dependency, DependencyVersion> dependencyVersions) throws InterruptedException {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> upgrades = getDependencyUpgradesToApply(project, dependencyVersions);
|
||||||
|
ProjectDependencies dependencies = ProjectDependencies.get(project);
|
||||||
|
ModuleIteration module = iteration.getModule(project);
|
||||||
|
|
||||||
|
if (upgrades.isEmpty()) {
|
||||||
|
logger.log(module, "No dependency upgrades to apply");
|
||||||
|
}
|
||||||
|
|
||||||
|
IssueTracker tracker = this.tracker.getRequiredPluginFor(project);
|
||||||
|
Tickets tickets = tracker.getTicketsFor(iteration);
|
||||||
|
List<Ticket> ticketsToClose = new ArrayList<>();
|
||||||
|
|
||||||
|
upgrades.forEach((dependency, dependencyVersion) -> {
|
||||||
|
|
||||||
|
String upgradeTicketSummary = getUpgradeTicketSummary(dependency, dependencyVersion);
|
||||||
|
Ticket upgradeTicket = getDependencyUpgradeTicket(tickets, upgradeTicketSummary).get();
|
||||||
|
String versionProperty = dependencies.getVersionPropertyFor(dependency);
|
||||||
|
|
||||||
|
File pom = getPomFile(project);
|
||||||
|
update(pom, Pom.class, it -> {
|
||||||
|
it.setProperty(versionProperty, dependencyVersion.getIdentifier());
|
||||||
|
});
|
||||||
|
|
||||||
|
gitOperations.commit(module, upgradeTicket, upgradeTicketSummary, Optional.empty(), pom);
|
||||||
|
|
||||||
|
ticketsToClose.add(upgradeTicket);
|
||||||
|
});
|
||||||
|
|
||||||
|
gitOperations.push(module);
|
||||||
|
|
||||||
|
// Allow GitHub to catch up with ticket notifications.
|
||||||
|
Thread.sleep(1500);
|
||||||
|
|
||||||
|
for (Ticket ticket : ticketsToClose) {
|
||||||
|
tracker.closeTicket(module, ticket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Dependency, DependencyVersion> getDependencyUpgradesToApply(Project project,
|
||||||
|
Map<Dependency, DependencyVersion> dependencyVersions) {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> currentDependencies = getCurrentDependencies(project);
|
||||||
|
Map<Dependency, DependencyVersion> upgrades = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
currentDependencies.forEach((dependency, dependencyVersion) -> {
|
||||||
|
|
||||||
|
DependencyVersion upgradeVersion = dependencyVersions.get(dependency);
|
||||||
|
|
||||||
|
if (upgradeVersion == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upgradeVersion.equals(dependencyVersion)) {
|
||||||
|
logger.log(project, "Skipping upgrade of %s (%s)", dependency.getName(), dependencyVersion.getIdentifier());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upgrades.put(dependency, upgradeVersion);
|
||||||
|
});
|
||||||
|
|
||||||
|
return upgrades;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Ticket> getDependencyUpgradeTicket(Tickets tickets, String upgradeTicketSummary) {
|
||||||
|
|
||||||
|
List<Ticket> upgradeTickets = tickets.filter(it -> it.getSummary().equals(upgradeTicketSummary)).toList();
|
||||||
|
|
||||||
|
if (upgradeTickets.size() > 1) {
|
||||||
|
throw new IllegalStateException("Multiple upgrade tickets found: " + upgradeTickets);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.ofNullable(upgradeTickets.isEmpty() ? null : upgradeTickets.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static DependencyUpgradeProposal getDependencyUpgradeProposal(Iteration iteration,
|
||||||
|
DependencyVersion currentVersion, List<DependencyVersion> allVersions) {
|
||||||
|
|
||||||
|
DependencyVersion latestMinor = findLatestMinor(iteration, currentVersion, allVersions);
|
||||||
|
DependencyVersion latest = findLatest(iteration, allVersions);
|
||||||
|
List<DependencyVersion> newerVersions = allVersions.stream() //
|
||||||
|
.sorted() //
|
||||||
|
.filter(it -> it.compareTo(currentVersion) > 0) //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return DependencyUpgradeProposal.of(iteration, currentVersion, latestMinor, latest, newerVersions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DependencyVersion findLatest(Iteration iteration, List<DependencyVersion> availableVersions) {
|
||||||
|
|
||||||
|
return availableVersions.stream().filter(it -> {
|
||||||
|
|
||||||
|
if (!iteration.isMilestone() && StringUtils.hasText(it.getModifier())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}).max(DependencyVersion::compareTo).orElseThrow(
|
||||||
|
() -> new IllegalArgumentException("Cannot determine new latest version from " + availableVersions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DependencyVersion findLatestMinor(Iteration iteration, DependencyVersion currentVersion,
|
||||||
|
List<DependencyVersion> availableVersions) {
|
||||||
|
|
||||||
|
return availableVersions.stream().filter(it -> {
|
||||||
|
|
||||||
|
if (!iteration.isMilestone() && StringUtils.hasText(it.getModifier())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.getVersion() == null || currentVersion.getVersion() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.getTrainName() != null && currentVersion.getTrainName() != null) {
|
||||||
|
return it.getTrainName().equals(currentVersion.getTrainName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.getVersion().getMajor() == currentVersion.getVersion().getMajor()
|
||||||
|
&& it.getVersion().getMinor() == currentVersion.getVersion().getMinor()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}) //
|
||||||
|
.max(DependencyVersion::compareTo) //
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new IllegalArgumentException("Cannot determine new minor version from " + availableVersions));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> getCurrentDependencies(Project project) {
|
||||||
|
|
||||||
|
if (!ProjectDependencies.containsProject(project)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
File pom = getPomFile(project);
|
||||||
|
ProjectDependencies dependencies = ProjectDependencies.get(project);
|
||||||
|
|
||||||
|
return doWithPom(pom, Pom.class, it -> {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> versions = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (ProjectDependencies.ProjectDependency projectDependency : dependencies) {
|
||||||
|
|
||||||
|
Dependency dependency = projectDependency.getDependency();
|
||||||
|
|
||||||
|
if (!((project == Projects.MONGO_DB && projectDependency.getProperty().equals("mongo.reactivestreams"))
|
||||||
|
|| project == Projects.NEO4J)) {
|
||||||
|
|
||||||
|
if (it.getDependencyVersion(dependency.getArtifactId()) == null
|
||||||
|
&& it.getManagedDependency(dependency.getArtifactId()) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String value = it.getProperty(projectDependency.getProperty());
|
||||||
|
|
||||||
|
if (value != null && !value.contains("${")) {
|
||||||
|
versions.put(dependency, DependencyVersion.of(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getPomFile(Project project) {
|
||||||
|
return workspace.getFile(project == Projects.BUILD ? "parent/pom.xml" : "pom.xml", project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
List<DependencyVersion> getAvailableVersions(Dependency dependency) {
|
||||||
|
|
||||||
|
String baseUrl = String.format("https://repo1.maven.org/maven2/%s/%s/", dependency.getGroupId().replace('.', '/'),
|
||||||
|
dependency.getArtifactId());
|
||||||
|
|
||||||
|
ResponseEntity<byte[]> mavenMetadata = restOperations.getForEntity(baseUrl + "/maven-metadata.xml", byte[].class);
|
||||||
|
ResponseEntity<String> directoryListing = restOperations.getForEntity(baseUrl, String.class);
|
||||||
|
|
||||||
|
Map<String, LocalDateTime> creationDates = parseCreationDates(directoryListing.getBody());
|
||||||
|
|
||||||
|
XBStreamInput io = projectionFactory.io().stream(new ByteArrayInputStream(mavenMetadata.getBody()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
MavenMetadata metadata = io.read(MavenMetadata.class);
|
||||||
|
|
||||||
|
return metadata.getVersions().stream().filter(dependency::shouldInclude).map(DependencyVersion::of).map(it -> {
|
||||||
|
|
||||||
|
if (creationDates.containsKey(it.getIdentifier())) {
|
||||||
|
return it.withCreatedAt(creationDates.get(it.getIdentifier()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return it;
|
||||||
|
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
} catch (Exception o_O) {
|
||||||
|
throw new RuntimeException(o_O);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, LocalDateTime> parseCreationDates(String body) {
|
||||||
|
|
||||||
|
Map<String, LocalDateTime> creationDates = new LinkedHashMap<>();
|
||||||
|
Matcher matcher = REPO_MAVEN_ORG_DIR_LISTING.matcher(body);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
|
||||||
|
String version = matcher.group(1);
|
||||||
|
LocalDateTime creationDate = LocalDateTime.from(DIR_LISTING_TIME_FORMAT.parse(matcher.group(2)));
|
||||||
|
creationDates.put(version, creationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return creationDates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Pom, R> R doWithPom(File file, Class<T> type, Function<T, R> callback) {
|
||||||
|
|
||||||
|
XBFileIO io = projectionFactory.io().file(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
T pom = (T) io.read(type);
|
||||||
|
return callback.apply(pom);
|
||||||
|
|
||||||
|
} catch (Exception o_O) {
|
||||||
|
throw new RuntimeException(o_O);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Pom> void update(File file, Class<T> type, Consumer<T> callback) {
|
||||||
|
|
||||||
|
XBFileIO io = projectionFactory.io().file(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
T pom = (T) io.read(type);
|
||||||
|
callback.accept(pom);
|
||||||
|
io.write(pom);
|
||||||
|
|
||||||
|
} catch (Exception o_O) {
|
||||||
|
throw new RuntimeException(o_O);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getUpgradeTicketSummary(Dependency dependency, DependencyVersion dependencyVersion) {
|
||||||
|
return String.format("Upgrade to %s %s", dependency.getName(), dependencyVersion.getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MavenMetadata {
|
||||||
|
|
||||||
|
@XBRead("/metadata/versioning/versions/version/text()")
|
||||||
|
List<String> getVersions();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@Value
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class DependencyUpgradeProposal {
|
||||||
|
|
||||||
|
DependencyVersion current, latest, latestMinor, proposal;
|
||||||
|
List<DependencyVersion> newerVersions;
|
||||||
|
|
||||||
|
public static DependencyUpgradeProposal of(Iteration iteration, DependencyVersion currentVersion,
|
||||||
|
DependencyVersion latestMinor, DependencyVersion latest, List<DependencyVersion> newerVersions) {
|
||||||
|
|
||||||
|
if (iteration.isServiceIteration()) {
|
||||||
|
return new DependencyUpgradeProposal(currentVersion, latest, latestMinor, latestMinor, newerVersions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DependencyUpgradeProposal(currentVersion, latest, latestMinor, latest, newerVersions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewVersions(boolean includeAll, boolean includeDate) {
|
||||||
|
|
||||||
|
if (includeAll) {
|
||||||
|
return getNewerVersions().stream().map(dependencyVersion -> {
|
||||||
|
|
||||||
|
if (includeDate && dependencyVersion.getCreatedAt() != null) {
|
||||||
|
return String.format("%s (%s)", dependencyVersion.getIdentifier(),
|
||||||
|
dependencyVersion.getCreatedAt().toLocalDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencyVersion.getIdentifier();
|
||||||
|
}).collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestMinor.toString().equals(latest.toString())) {
|
||||||
|
return latest.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("%s, %s", latestMinor, latest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUpgradeAvailable() {
|
||||||
|
return !getCurrent().getIdentifier().equals(getProposal().getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getProposal().getIdentifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.TrainIteration;
|
||||||
|
import org.springframework.shell.support.table.Table;
|
||||||
|
import org.springframework.shell.support.table.TableHeader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object capturing upgrade proposals for each {@link Dependency}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class DependencyUpgradeProposals {
|
||||||
|
|
||||||
|
private final Map<Dependency, DependencyUpgradeProposal> proposals;
|
||||||
|
|
||||||
|
public DependencyUpgradeProposals(Map<Dependency, DependencyUpgradeProposal> proposals) {
|
||||||
|
this.proposals = proposals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty {@link DependencyUpgradeProposal} object.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DependencyUpgradeProposals empty() {
|
||||||
|
return new DependencyUpgradeProposals(Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link DependencyUpgradeProposal} by merging this and {@code other}.
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DependencyUpgradeProposals mergeWith(DependencyUpgradeProposals other) {
|
||||||
|
|
||||||
|
Map<Dependency, DependencyUpgradeProposal> proposals = new TreeMap<>(this.proposals);
|
||||||
|
proposals.putAll(other.proposals);
|
||||||
|
|
||||||
|
return new DependencyUpgradeProposals(proposals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tabular summary including {@link Ansi} escapes.
|
||||||
|
*
|
||||||
|
* @param includeAll
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Table toTable(boolean includeAll) {
|
||||||
|
|
||||||
|
Table table = new Table();
|
||||||
|
table.addHeader(1, new TableHeader("Dependency"));
|
||||||
|
table.addHeader(2, new TableHeader("Current"));
|
||||||
|
table.addHeader(3, new TableHeader("Available"));
|
||||||
|
table.addHeader(4, new TableHeader("Proposed"));
|
||||||
|
|
||||||
|
proposals.forEach((dependency, proposal) -> {
|
||||||
|
|
||||||
|
boolean updateAvailable = proposal.isUpgradeAvailable();
|
||||||
|
|
||||||
|
String s = updateAvailable
|
||||||
|
? Ansi.ansi().fg(Ansi.Color.MAGENTA).a(proposal.getProposal()).fg(Ansi.Color.GREEN).toString()
|
||||||
|
: proposal.getProposal().toString();
|
||||||
|
|
||||||
|
if (includeAll || updateAvailable) {
|
||||||
|
table.addRow(dependency.getName(), proposal.getCurrent().toString(), proposal.getNewVersions(includeAll, false),
|
||||||
|
s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the upgrade proposal as {@link java.util.Properties} representation.
|
||||||
|
*
|
||||||
|
* @param iteration
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String asProperties(TrainIteration iteration) {
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.append("dependency.train=").append(iteration.getTrain().getName()).append(System.lineSeparator());
|
||||||
|
builder.append("dependency.iteration=").append(iteration.getIteration().getName()).append(System.lineSeparator());
|
||||||
|
|
||||||
|
proposals.forEach((dependency, proposal) -> {
|
||||||
|
|
||||||
|
boolean updateAvailable = proposal.isUpgradeAvailable();
|
||||||
|
|
||||||
|
if (updateAvailable) {
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
builder.append(String.format("# %s - Available versions: ", dependency.getName()))
|
||||||
|
.append(proposal.getNewVersions(true, true)).append(System.lineSeparator());
|
||||||
|
|
||||||
|
builder.append(
|
||||||
|
String.format("dependency[%s\\:%s]=%s", dependency.getGroupId(), dependency.getArtifactId(), proposal));
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a dependency upgrade map by parsing {@link Properties}.
|
||||||
|
*
|
||||||
|
* @param iteration
|
||||||
|
* @param properties
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Map<Dependency, DependencyVersion> fromProperties(TrainIteration iteration, Properties properties) {
|
||||||
|
|
||||||
|
Pattern keyPattern = Pattern.compile("dependency\\[([a-zA-Z0-9\\-\\.]+):([a-zA-Z0-9\\-\\.]+)\\]");
|
||||||
|
|
||||||
|
String verificationTrain = properties.getProperty("dependency.train", "");
|
||||||
|
String verificationIteration = properties.getProperty("dependency.iteration", "");
|
||||||
|
|
||||||
|
if (!verificationTrain.equals(iteration.getTrain().getName())
|
||||||
|
|| !verificationIteration.equals(iteration.getIteration().getName())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("Verification failed: Dependency upgrade descriptor reports %s %s", verificationTrain,
|
||||||
|
verificationIteration));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> result = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
properties.forEach((k, v) -> {
|
||||||
|
|
||||||
|
if ("dependency.train".equals(k) || "dependency.iteration".equals(k)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher matcher = keyPattern.matcher(k.toString());
|
||||||
|
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
throw new IllegalArgumentException(String.format("Unexpected key: %s", k));
|
||||||
|
}
|
||||||
|
|
||||||
|
String artifactId = matcher.group(2);
|
||||||
|
Dependency dependency = Dependencies.getRequiredByArtifactId(artifactId);
|
||||||
|
|
||||||
|
result.put(dependency, DependencyVersion.of(v.toString()));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.With;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@Value
|
||||||
|
class DependencyVersion implements Comparable<DependencyVersion> {
|
||||||
|
|
||||||
|
private static Pattern VERSION = Pattern.compile("((?>(?>\\d+)[\\.]?)+)(-[a-zA-Z]+)?(\\d+)?");
|
||||||
|
private static Pattern NAME_VERSION = Pattern.compile("([A-Za-z]+)-(RELEASE|SR(\\d+))");
|
||||||
|
|
||||||
|
private static Comparator<DependencyVersion> VERSION_COMPARATOR = Comparator.comparing(DependencyVersion::getVersion)
|
||||||
|
.thenComparing((o1, o2) -> {
|
||||||
|
|
||||||
|
// no modifier means release so it's higher order
|
||||||
|
if (o1.getModifier().isEmpty() && !o2.getModifier().isEmpty()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!o1.getModifier().isEmpty() && o2.getModifier().isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}).thenComparing(DependencyVersion::getModifier).thenComparing(DependencyVersion::getCounter)
|
||||||
|
.thenComparing(DependencyVersion::getIdentifier);
|
||||||
|
|
||||||
|
private static Comparator<DependencyVersion> TRAIN_VERSION_COMPARATOR = Comparator
|
||||||
|
.comparing(DependencyVersion::getTrainName).thenComparing(DependencyVersion::getVersion);
|
||||||
|
|
||||||
|
String identifier;
|
||||||
|
String trainName;
|
||||||
|
Version version;
|
||||||
|
String modifier;
|
||||||
|
int counter;
|
||||||
|
@With LocalDateTime createdAt;
|
||||||
|
|
||||||
|
public static DependencyVersion of(String identifier) {
|
||||||
|
|
||||||
|
Matcher bomMatcher = NAME_VERSION.matcher(identifier);
|
||||||
|
|
||||||
|
if (bomMatcher.find()) {
|
||||||
|
|
||||||
|
Version version = Version.of(0);
|
||||||
|
if (identifier.contains("-SR")) {
|
||||||
|
version = Version.of(Integer.parseInt(bomMatcher.group(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DependencyVersion(identifier, bomMatcher.group(1), version, "", 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher versionMatcher = VERSION.matcher(identifier);
|
||||||
|
|
||||||
|
if (versionMatcher.find()) {
|
||||||
|
Version version = null;
|
||||||
|
String modifier;
|
||||||
|
String counter;
|
||||||
|
try {
|
||||||
|
version = Version.parse(versionMatcher.group(1));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier = versionMatcher.group(2);
|
||||||
|
counter = versionMatcher.group(3);
|
||||||
|
|
||||||
|
return new DependencyVersion(identifier, null, version, modifier == null ? "" : modifier.toUpperCase(Locale.ROOT),
|
||||||
|
counter != null ? Integer.parseInt(counter) : 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(String.format("Cannot parse version identifier%s", identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DependencyVersion o) {
|
||||||
|
|
||||||
|
if (trainName != null && o.trainName != null) {
|
||||||
|
return TRAIN_VERSION_COMPARATOR.compare(this, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trainName != null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.trainName != null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version != null && o.version != null) {
|
||||||
|
return VERSION_COMPARATOR.compare(this, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier.compareTo(o.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Project;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
import org.springframework.data.util.Streamable;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration of dependencies for a specific project.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class ProjectDependencies implements Streamable<ProjectDependencies.ProjectDependency> {
|
||||||
|
|
||||||
|
private static final MultiValueMap<Project, ProjectDependency> config = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("apt", Dependencies.APT));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("aspectj", Dependencies.ASPECTJ));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("assertj", Dependencies.ASSERTJ));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("jackson", Dependencies.JACKSON));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("jacoco", Dependencies.JACOCO));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("jodatime", Dependencies.JODA_TIME));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("junit5", Dependencies.JUNIT5));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("junit", Dependencies.JUNIT4));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("kotlin", Dependencies.KOTLIN));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("kotlin-coroutines", Dependencies.KOTLIN_COROUTINES));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("mockito", Dependencies.MOCKITO));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("querydsl", Dependencies.QUERYDSL));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("reactor", Dependencies.PROJECT_REACTOR));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava", Dependencies.RXJAVA1));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava2", Dependencies.RXJAVA2));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava3", Dependencies.RXJAVA3));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava-reactive-streams", Dependencies.RXJAVA_RS));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("spring", Dependencies.SPRING_FRAMEWORK));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("spring-hateoas", Dependencies.SPRING_HATEOAS));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("spring-plugin", Dependencies.SPRING_PLUGIN));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("testcontainers", Dependencies.TESTCONTAINERS));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("threetenbp", Dependencies.THREE_TEN_BP));
|
||||||
|
config.add(Projects.BUILD, ProjectDependency.ofProperty("webbeans", Dependencies.OPEN_WEB_BEANS));
|
||||||
|
|
||||||
|
config.add(Projects.COMMONS, ProjectDependency.ofProperty("vavr", Dependencies.VAVR));
|
||||||
|
config.add(Projects.COMMONS, ProjectDependency.ofProperty("xmlbeam", Dependencies.XML_BEAM));
|
||||||
|
|
||||||
|
config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo.reactivestreams", Dependencies.MONGODB_RS));
|
||||||
|
config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_LEGACY));
|
||||||
|
config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_CORE));
|
||||||
|
config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_SYNC));
|
||||||
|
config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_ASYNC));
|
||||||
|
|
||||||
|
config.add(Projects.REDIS, ProjectDependency.ofProperty("lettuce", Dependencies.LETTUCE));
|
||||||
|
config.add(Projects.REDIS, ProjectDependency.ofProperty("jedis", Dependencies.JEDIS));
|
||||||
|
|
||||||
|
config.add(Projects.CASSANDRA,
|
||||||
|
ProjectDependency.ofProperty("cassandra-driver.version", Dependencies.CASSANDRA_DRIVER3));
|
||||||
|
config.add(Projects.CASSANDRA,
|
||||||
|
ProjectDependency.ofProperty("cassandra-driver.version", Dependencies.CASSANDRA_DRIVER4));
|
||||||
|
|
||||||
|
config.add(Projects.NEO4J, ProjectDependency.ofProperty("neo4j.ogm.version", Dependencies.NEO4J_OGM));
|
||||||
|
config.add(Projects.NEO4J, ProjectDependency.ofProperty("neo4j-java-driver.version", Dependencies.NEO4J_DRIVER));
|
||||||
|
|
||||||
|
config.add(Projects.COUCHBASE, ProjectDependency.ofProperty("couchbase", Dependencies.COUCHBASE));
|
||||||
|
|
||||||
|
config.add(Projects.ELASTICSEARCH, ProjectDependency.ofProperty("elasticsearch", Dependencies.ELASTICSEARCH));
|
||||||
|
|
||||||
|
config.add(Projects.LDAP, ProjectDependency.ofProperty("spring-ldap", Dependencies.SPRING_LDAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<ProjectDependency> dependencies;
|
||||||
|
|
||||||
|
private ProjectDependencies(List<ProjectDependency> dependencies) {
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve upgradable dependencies for a {@link Project}.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* @return
|
||||||
|
* @throws IllegalArgumentException if the project has no upgradable dependencies.
|
||||||
|
*/
|
||||||
|
public static ProjectDependencies get(Project project) {
|
||||||
|
|
||||||
|
if (!containsProject(project)) {
|
||||||
|
throw new IllegalArgumentException(String.format("No dependency configuration for %s!", project));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ProjectDependencies(config.get(project));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the {@link Project} has upgradable dependencies.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean containsProject(Project project) {
|
||||||
|
return config.containsKey(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionPropertyFor(Dependency dependency) {
|
||||||
|
|
||||||
|
for (ProjectDependency projectDependency : dependencies) {
|
||||||
|
|
||||||
|
if (projectDependency.getDependency().equals(dependency)) {
|
||||||
|
return projectDependency.getProperty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Dependency " + dependency + " is not a dependency of this project!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ProjectDependency> iterator() {
|
||||||
|
return dependencies.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public static class ProjectDependency {
|
||||||
|
|
||||||
|
String property;
|
||||||
|
|
||||||
|
Dependency dependency;
|
||||||
|
|
||||||
|
public static ProjectDependency ofProperty(String pomProperty, Dependency dependency) {
|
||||||
|
return new ProjectDependency(pomProperty, dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -233,28 +233,29 @@ public class GitOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void push(TrainIteration iteration) {
|
public void push(TrainIteration iteration) {
|
||||||
|
ExecutionUtils.run(executor, iteration, this::push);
|
||||||
|
}
|
||||||
|
|
||||||
ExecutionUtils.run(executor, iteration, module -> {
|
public void push(ModuleIteration module) {
|
||||||
|
|
||||||
Branch branch = Branch.from(module);
|
Branch branch = Branch.from(module);
|
||||||
logger.log(module, "git push origin %s", branch);
|
logger.log(module, "git push origin %s", branch);
|
||||||
|
|
||||||
if (!branchExists(module.getProject(), branch)) {
|
if (!branchExists(module.getProject(), branch)) {
|
||||||
|
|
||||||
logger.log(module, "No branch %s in %s, skip push", branch, module.getProject().getName());
|
logger.log(module, "No branch %s in %s, skip push", branch, module.getProject().getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doWithGit(module.getProject(), git -> {
|
doWithGit(module.getProject(), git -> {
|
||||||
|
|
||||||
Ref ref = git.getRepository().findRef(branch.toString());
|
Ref ref = git.getRepository().findRef(branch.toString());
|
||||||
|
|
||||||
git.push()//
|
git.push()//
|
||||||
.setRemote("origin")//
|
.setRemote("origin")//
|
||||||
.setRefSpecs(new RefSpec(ref.getName()))//
|
.setRefSpecs(new RefSpec(ref.getName()))//
|
||||||
.setCredentialsProvider(gitProperties.getCredentials())//
|
.setCredentialsProvider(gitProperties.getCredentials())//
|
||||||
.call();
|
.call();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,6 +622,26 @@ public class GitOperations {
|
|||||||
() -> String.format("No issue tracker found for project %s!", project));
|
() -> String.format("No issue tracker found for project %s!", project));
|
||||||
Ticket ticket = tracker.getReleaseTicketFor(module);
|
Ticket ticket = tracker.getReleaseTicketFor(module);
|
||||||
|
|
||||||
|
commit(module, ticket, summary, details, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit
|
||||||
|
* message. If no files are given, all pending changes are committed.
|
||||||
|
*
|
||||||
|
* @param module must not be {@literal null}.
|
||||||
|
* @param summary must not be {@literal null} or empty.
|
||||||
|
* @param details can be {@literal null} or empty.
|
||||||
|
* @param files can be empty.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void commit(ModuleIteration module, Ticket ticket, String summary, Optional<String> details, File... files) {
|
||||||
|
|
||||||
|
Assert.notNull(module, "Module iteration must not be null!");
|
||||||
|
Assert.hasText(summary, "Summary must not be null or empty!");
|
||||||
|
|
||||||
|
Project project = module.getProject();
|
||||||
|
|
||||||
Commit commit = new Commit(ticket, summary, details);
|
Commit commit = new Commit(ticket, summary, details);
|
||||||
String author = gitProperties.getAuthor();
|
String author = gitProperties.getAuthor();
|
||||||
String email = gitProperties.getEmail();
|
String email = gitProperties.getEmail();
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ public interface IssueTracker extends Plugin<Project> {
|
|||||||
*/
|
*/
|
||||||
default Changelog getChangelogFor(ModuleIteration module, List<TicketReference> ticketReferences) {
|
default Changelog getChangelogFor(ModuleIteration module, List<TicketReference> ticketReferences) {
|
||||||
|
|
||||||
Tickets tickets = resolve(module, ticketReferences);
|
Tickets tickets = findTickets(module, ticketReferences);
|
||||||
return Changelog.of(module, tickets);
|
return Changelog.of(module, tickets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +155,14 @@ public interface IssueTracker extends Plugin<Project> {
|
|||||||
*/
|
*/
|
||||||
void closeIteration(ModuleIteration module);
|
void closeIteration(ModuleIteration module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a {@link Ticket}.
|
||||||
|
*
|
||||||
|
* @param module must not be {@literal null}.
|
||||||
|
* @param ticket must not be {@literal null}.
|
||||||
|
*/
|
||||||
|
void closeTicket(ModuleIteration module, Ticket ticket);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a {@link List} of {@link TicketReference}s to {@link Tickets} for a given {@link ModuleIteration}. The
|
* Resolve a {@link List} of {@link TicketReference}s to {@link Tickets} for a given {@link ModuleIteration}. The
|
||||||
* implementation ensures to resolve only references that match the issue tracker scheme this issue tracker is
|
* implementation ensures to resolve only references that match the issue tracker scheme this issue tracker is
|
||||||
@@ -164,5 +172,5 @@ public interface IssueTracker extends Plugin<Project> {
|
|||||||
* @param ticketReferences must not be {@literal null}.
|
* @param ticketReferences must not be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Tickets resolve(ModuleIteration moduleIteration, List<TicketReference> ticketReferences);
|
Tickets findTickets(ModuleIteration moduleIteration, List<TicketReference> ticketReferences);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,18 +320,23 @@ class GitHub extends GitHubSupport implements IssueTracker {
|
|||||||
Assert.notNull(module, "ModuleIteration must not be null.");
|
Assert.notNull(module, "ModuleIteration must not be null.");
|
||||||
|
|
||||||
Ticket releaseTicketFor = getReleaseTicketFor(module);
|
Ticket releaseTicketFor = getReleaseTicketFor(module);
|
||||||
|
GitHubIssue response = close(module, releaseTicketFor);
|
||||||
|
|
||||||
|
return toTicket(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GitHubIssue close(ModuleIteration module, Ticket ticket) {
|
||||||
|
|
||||||
String repositoryName = GitProject.of(module.getProject()).getRepositoryName();
|
String repositoryName = GitProject.of(module.getProject()).getRepositoryName();
|
||||||
|
|
||||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||||
parameters.put("repoName", repositoryName);
|
parameters.put("repoName", repositoryName);
|
||||||
parameters.put("id", stripHash(releaseTicketFor));
|
parameters.put("id", stripHash(ticket));
|
||||||
|
|
||||||
GitHubIssue edit = GitHubIssue.assignedTo(properties.getUsername()).close();
|
GitHubIssue edit = GitHubIssue.assignedTo(properties.getUsername()).close();
|
||||||
|
|
||||||
GitHubIssue response = operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH,
|
return operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH,
|
||||||
new HttpEntity<>(edit, new HttpHeaders()), ISSUE_TYPE, parameters).getBody();
|
new HttpEntity<>(edit, new HttpHeaders()), ISSUE_TYPE, parameters).getBody();
|
||||||
|
|
||||||
return toTicket(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String stripHash(Ticket ticket) {
|
private String stripHash(Ticket ticket) {
|
||||||
@@ -424,7 +429,12 @@ class GitHub extends GitHubSupport implements IssueTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tickets resolve(ModuleIteration moduleIteration, List<TicketReference> ticketReferences) {
|
public void closeTicket(ModuleIteration module, Ticket ticket) {
|
||||||
|
close(module, ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tickets findTickets(ModuleIteration moduleIteration, List<TicketReference> ticketReferences) {
|
||||||
|
|
||||||
logger.log(moduleIteration, "Looking up GitHub issues from milestone …");
|
logger.log(moduleIteration, "Looking up GitHub issues from milestone …");
|
||||||
|
|
||||||
|
|||||||
@@ -543,6 +543,11 @@ class Jira implements JiraConnector {
|
|||||||
// - if no next version exists, create
|
// - if no next version exists, create
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeTicket(ModuleIteration module, Ticket ticket) {
|
||||||
|
resolve(ticket);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.release.jira.JiraConnector#getChangelogFor(org.springframework.data.release.model.Module, org.springframework.data.release.model.Iteration)
|
* @see org.springframework.data.release.jira.JiraConnector#getChangelogFor(org.springframework.data.release.model.Module, org.springframework.data.release.model.Iteration)
|
||||||
@@ -575,7 +580,7 @@ class Jira implements JiraConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tickets resolve(ModuleIteration moduleIteration, List<TicketReference> ticketReferences) {
|
public Tickets findTickets(ModuleIteration moduleIteration, List<TicketReference> ticketReferences) {
|
||||||
|
|
||||||
List<String> ids = ticketReferences.stream()
|
List<String> ids = ticketReferences.stream()
|
||||||
.filter(it -> it.getId().startsWith(moduleIteration.getProjectKey().getKey())).map(TicketReference::getId)
|
.filter(it -> it.getId().startsWith(moduleIteration.getProjectKey().getKey())).map(TicketReference::getId)
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class ReleaseOperations {
|
|||||||
List<Ticket> tickets = new ArrayList<>();
|
List<Ticket> tickets = new ArrayList<>();
|
||||||
|
|
||||||
for (IssueTracker tracker : trackers) {
|
for (IssueTracker tracker : trackers) {
|
||||||
tickets.addAll(tracker.resolve(module, ticketReferences).getTickets());
|
tickets.addAll(tracker.findTickets(module, ticketReferences).getTickets());
|
||||||
}
|
}
|
||||||
|
|
||||||
changelog = Changelog.of(module, new Tickets(tickets));
|
changelog = Changelog.of(module, new Tickets(tickets));
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
import static org.junit.Assume.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.release.AbstractIntegrationTests;
|
||||||
|
import org.springframework.data.release.git.GitOperations;
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link DependencyOperations}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@Disabled
|
||||||
|
class DependencyOperationsIntegrationTests extends AbstractIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired GitOperations git;
|
||||||
|
@Autowired DependencyOperations operations;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
try {
|
||||||
|
URL url = new URL("https://repo1.maven.org");
|
||||||
|
URLConnection urlConnection = url.openConnection();
|
||||||
|
urlConnection.connect();
|
||||||
|
urlConnection.getInputStream().close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
assumeTrue("Test requires connectivity to Maven: " + e.toString(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldDiscoverDependencyVersions() {
|
||||||
|
assertThat(operations.getAvailableVersions(Dependencies.PROJECT_REACTOR)).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReportExistingDependencyVersions() {
|
||||||
|
assertThat(operations.getCurrentDependencies(Projects.BUILD)).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReportExistingOptionalDependencies() {
|
||||||
|
|
||||||
|
// git.checkout(ReleaseTrains.MOORE);
|
||||||
|
|
||||||
|
assertThat(operations.getCurrentDependencies(Projects.CASSANDRA)).hasSize(1);
|
||||||
|
assertThat(operations.getCurrentDependencies(Projects.MONGO_DB)).hasSize(1);
|
||||||
|
assertThat(operations.getCurrentDependencies(Projects.NEO4J)).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUpgradeProposals() {
|
||||||
|
System.out.println(operations.getDependencyUpgradeProposals(Projects.BUILD, Iteration.M1));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link DependencyOperations}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
class DependencyOperationsUnitTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRetainCurrentVersion() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.0-M1") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.SR1,
|
||||||
|
DependencyVersion.of("5.7.0"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getCurrent()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
assertThat(proposal.getLatestMinor()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
assertThat(proposal.getProposal()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
assertThat(proposal.getLatest()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSelectNextMinorVersion() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.1", "5.8.0") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.SR1,
|
||||||
|
DependencyVersion.of("5.7.0"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getCurrent()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
assertThat(proposal.getLatestMinor()).isEqualTo(DependencyVersion.of("5.7.1"));
|
||||||
|
assertThat(proposal.getProposal()).isEqualTo(DependencyVersion.of("5.7.1"));
|
||||||
|
assertThat(proposal.getLatest()).isEqualTo(DependencyVersion.of("5.8.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSelectLatestVersion() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.1", "5.8.0") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.M1,
|
||||||
|
DependencyVersion.of("5.7.0"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getCurrent()).isEqualTo(DependencyVersion.of("5.7.0"));
|
||||||
|
assertThat(proposal.getLatestMinor()).isEqualTo(DependencyVersion.of("5.7.1"));
|
||||||
|
assertThat(proposal.getProposal()).isEqualTo(DependencyVersion.of("5.8.0"));
|
||||||
|
assertThat(proposal.getLatest()).isEqualTo(DependencyVersion.of("5.8.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReportNewerVersions() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.1", "5.7.2-M2", "5.7.2", "5.8.0") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.SR1,
|
||||||
|
DependencyVersion.of("5.7.1"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getNewerVersions()).extracting(DependencyVersion::getIdentifier).containsExactly("5.7.2-M2",
|
||||||
|
"5.7.2", "5.8.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReportMilestoneVersionForMilestoneIteration() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.1", "5.7.2-M2") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.M1,
|
||||||
|
DependencyVersion.of("5.7.1"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getLatest()).extracting(DependencyVersion::getIdentifier).isEqualTo("5.7.2-M2");
|
||||||
|
assertThat(proposal.getProposal()).extracting(DependencyVersion::getIdentifier).isEqualTo("5.7.2-M2");
|
||||||
|
assertThat(proposal.getNewerVersions()).extracting(DependencyVersion::getIdentifier).containsExactly("5.7.2-M2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSkipMilestoneVersionForNonMilestoneIteration() {
|
||||||
|
|
||||||
|
List<DependencyVersion> availableVersions = Stream.of("5.7.0", "5.7.1", "5.7.2-M2") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DependencyUpgradeProposal proposal = DependencyOperations.getDependencyUpgradeProposal(Iteration.RC1,
|
||||||
|
DependencyVersion.of("5.7.1"), availableVersions);
|
||||||
|
|
||||||
|
assertThat(proposal.getLatest()).extracting(DependencyVersion::getIdentifier).isEqualTo("5.7.1");
|
||||||
|
assertThat(proposal.getProposal()).extracting(DependencyVersion::getIdentifier).isEqualTo("5.7.1");
|
||||||
|
assertThat(proposal.getNewerVersions()).extracting(DependencyVersion::getIdentifier).containsExactly("5.7.2-M2");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
import org.springframework.data.release.model.ReleaseTrains;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link DependencyUpgradeProposals}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
class DependencyUpgradeProposalsUnitTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldParseDependencies() {
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.put("dependency.train", "Pascal");
|
||||||
|
properties.put("dependency.iteration", "M1");
|
||||||
|
properties.put("dependency[org.assertj:assertj-core]", "3.18.1");
|
||||||
|
|
||||||
|
Map<Dependency, DependencyVersion> dependencies = DependencyUpgradeProposals
|
||||||
|
.fromProperties(ReleaseTrains.PASCAL.getIteration(Iteration.M1), properties);
|
||||||
|
|
||||||
|
assertThat(dependencies).hasSize(1).containsEntry(Dependencies.ASSERTJ, DependencyVersion.of("3.18.1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.springframework.data.release.dependency;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link DependencyVersion}.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
class DependencyVersionUnitTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConsiderSemVerSortOrder() {
|
||||||
|
|
||||||
|
List<String> sorted = Stream.of("1.0.0", "1.0.0-m1", "1.0.0-rc1", "1.0.0-m2") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.map(DependencyVersion::getIdentifier) //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
assertThat(sorted).containsExactly("1.0.0-m1", "1.0.0-m2", "1.0.0-rc1", "1.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldConsiderReleaseTrainSortOrder() {
|
||||||
|
|
||||||
|
List<String> sorted = Stream.of("Bismuth-SR1", "Aluminium-SR1", "Aluminium-RELEASE", "Aluminium-SR2") //
|
||||||
|
.map(DependencyVersion::of) //
|
||||||
|
.sorted() //
|
||||||
|
.map(DependencyVersion::getIdentifier) //
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
assertThat(sorted).containsExactly("Aluminium-RELEASE", "Aluminium-SR1", "Aluminium-SR2", "Bismuth-SR1");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,7 +56,7 @@ class ReleaseOperationsIntegrationTests extends AbstractIntegrationTests {
|
|||||||
List<TicketReference> ticketReferences = gitOperations.getTicketReferencesBetween(Projects.MONGO_DB, from, to);
|
List<TicketReference> ticketReferences = gitOperations.getTicketReferencesBetween(Projects.MONGO_DB, from, to);
|
||||||
IssueTracker tracker = trackers.getRequiredPluginFor(Projects.MONGO_DB);
|
IssueTracker tracker = trackers.getRequiredPluginFor(Projects.MONGO_DB);
|
||||||
|
|
||||||
Tickets tickets = tracker.resolve(to.getModule(Projects.MONGO_DB), ticketReferences);
|
Tickets tickets = tracker.findTickets(to.getModule(Projects.MONGO_DB), ticketReferences);
|
||||||
|
|
||||||
assertThat(tickets).hasSize(15);
|
assertThat(tickets).hasSize(15);
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ class ReleaseOperationsIntegrationTests extends AbstractIntegrationTests {
|
|||||||
List<TicketReference> ticketReferences = gitOperations.getTicketReferencesBetween(Projects.R2DBC, from, to);
|
List<TicketReference> ticketReferences = gitOperations.getTicketReferencesBetween(Projects.R2DBC, from, to);
|
||||||
IssueTracker tracker = trackers.getRequiredPluginFor(Projects.R2DBC);
|
IssueTracker tracker = trackers.getRequiredPluginFor(Projects.R2DBC);
|
||||||
|
|
||||||
Tickets tickets = tracker.resolve(to.getModule(Projects.R2DBC), ticketReferences);
|
Tickets tickets = tracker.findTickets(to.getModule(Projects.R2DBC), ticketReferences);
|
||||||
|
|
||||||
assertThat(tickets).hasSize(22);
|
assertThat(tickets).hasSize(22);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user