#99 - Extract ticket references from Git commits.
This commit is contained in:
@@ -63,7 +63,7 @@ public class Repository {
|
||||
return "snapshot";
|
||||
}
|
||||
|
||||
if (version.isMilestoneVersion()) {
|
||||
if (version.isMilestoneVersion() || version.isReleaseCandidateVersion()) {
|
||||
return "milestone";
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,21 @@ import lombok.RequiredArgsConstructor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.release.CliComponent;
|
||||
import org.springframework.data.release.TimedCommand;
|
||||
import org.springframework.data.release.issues.Changelog;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.TicketReference;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.Train;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.utils.ExecutionUtils;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
import org.springframework.shell.core.annotation.CliOption;
|
||||
import org.springframework.shell.support.table.Table;
|
||||
@@ -46,6 +51,7 @@ import org.springframework.util.StringUtils;
|
||||
class GitCommands extends TimedCommand {
|
||||
|
||||
private final @NonNull GitOperations git;
|
||||
private final @NonNull Executor executor;
|
||||
|
||||
@CliCommand("git co-train")
|
||||
public void checkout(@CliOption(key = "", mandatory = true) Train train) throws Exception {
|
||||
@@ -71,6 +77,34 @@ class GitCommands extends TimedCommand {
|
||||
return StringUtils.collectionToDelimitedString(git.getTags(project).asList(), "\n");
|
||||
}
|
||||
|
||||
@CliCommand("git previous")
|
||||
public String previous(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
|
||||
return git.getPreviousIteration(iteration).toString();
|
||||
}
|
||||
|
||||
@CliCommand("git changelog")
|
||||
public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
|
||||
|
||||
TrainIteration previousIteration = git.getPreviousIteration(iteration);
|
||||
|
||||
return ExecutionUtils.runAndReturn(executor, iteration, moduleIteration -> {
|
||||
|
||||
List<TicketReference> ticketRefs = git.getTicketReferencesBetween(moduleIteration.getProject(), previousIteration,
|
||||
iteration);
|
||||
|
||||
return Changelog.of(moduleIteration, toTickets(ticketRefs));
|
||||
|
||||
}).stream() //
|
||||
.map(Changelog::toString) //
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private Tickets toTickets(List<TicketReference> ticketRefs) {
|
||||
|
||||
return new Tickets(
|
||||
ticketRefs.stream().map(it -> new Ticket(it.getId(), it.getMessage(), "", null)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all projects contained in the given {@link Train}.
|
||||
*
|
||||
|
||||
@@ -22,18 +22,12 @@ import lombok.experimental.FieldDefaults;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.eclipse.jgit.api.CheckoutCommand;
|
||||
import org.eclipse.jgit.api.CherryPickResult;
|
||||
@@ -63,15 +57,19 @@ import org.eclipse.jgit.transport.URIish;
|
||||
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.TicketReference;
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
import org.springframework.data.release.model.Gpg;
|
||||
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.ReleaseTrains;
|
||||
import org.springframework.data.release.model.Train;
|
||||
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.Pair;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -311,10 +309,40 @@ public class GitOperations {
|
||||
logger.log(project, "Project update done!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given {@link Project} by fetching all tags.
|
||||
*
|
||||
* @param project must not be {@literal null}.
|
||||
*/
|
||||
public void fetchTags(Project project) {
|
||||
|
||||
Assert.notNull(project, "Project must not be null!");
|
||||
|
||||
logger.log(project, "Updating project tags…");
|
||||
|
||||
GitProject gitProject = new GitProject(project, server);
|
||||
String repositoryName = gitProject.getRepositoryName();
|
||||
|
||||
doWithGit(project, git -> {
|
||||
|
||||
if (workspace.hasProjectDirectory(project)) {
|
||||
|
||||
logger.log(project, "Found existing repository %s. Obtaining tags…", repositoryName);
|
||||
logger.log(project, "git fetch --tags");
|
||||
git.fetch().setTagOpt(TagOpt.FETCH_TAGS).call();
|
||||
|
||||
} else {
|
||||
clone(project);
|
||||
}
|
||||
});
|
||||
|
||||
logger.log(project, "Project tags update done!");
|
||||
}
|
||||
|
||||
public VersionTags getTags(Project project) {
|
||||
|
||||
return doWithGit(project, git -> {
|
||||
return new VersionTags(git.tagList().call().stream()//
|
||||
return new VersionTags(project, git.tagList().call().stream()//
|
||||
.map(ref -> {
|
||||
|
||||
RevCommit commit = getCommit(git.getRepository(), ref);
|
||||
@@ -375,6 +403,120 @@ public class GitOperations {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the previous {@link TrainIteration} from existing tags.
|
||||
*
|
||||
* @param trainIteration must not be {@literal null}.
|
||||
* @return
|
||||
* @throws IllegalStateException if no previous iteration could be found.
|
||||
*/
|
||||
public TrainIteration getPreviousIteration(TrainIteration trainIteration) {
|
||||
|
||||
Assert.notNull(trainIteration, "TrainIteration must not be null!");
|
||||
|
||||
if (trainIteration.getIteration().isMilestone() && trainIteration.getIteration().getIterationValue() == 1) {
|
||||
|
||||
Train trainToUse = getPreviousTrain(trainIteration);
|
||||
return trainToUse.getIteration(Iteration.GA);
|
||||
}
|
||||
|
||||
Optional<TrainIteration> mostRecentBefore = getTags(Projects.BUILD) //
|
||||
.filter((tag, ti) -> ti.getTrain().equals(trainIteration.getTrain())) //
|
||||
.find((tag, iteration) -> iteration.getIteration().compareTo(trainIteration.getIteration()) < 0,
|
||||
Pair::getSecond);
|
||||
|
||||
return mostRecentBefore.orElseThrow(() -> new IllegalStateException(
|
||||
"Cannot determine previous iteration for " + trainIteration.getReleaseTrainNameAndVersion()));
|
||||
}
|
||||
|
||||
public List<TicketReference> getTicketReferencesBetween(Project project, TrainIteration from, TrainIteration to) {
|
||||
|
||||
VersionTags tags = getTags(project);
|
||||
|
||||
List<TicketReference> ticketReferences = doWithGit(project, git -> {
|
||||
|
||||
Repository repo = git.getRepository();
|
||||
|
||||
ModuleIteration toModuleIteration = to.getModule(project);
|
||||
ObjectId fromTag = resolveLowerBoundary(project, from, tags, repo);
|
||||
ObjectId toTag = resolveUpperBoundary(toModuleIteration, tags, repo);
|
||||
|
||||
Iterable<RevCommit> commits = git.log().addRange(fromTag, toTag).call();
|
||||
|
||||
return StreamSupport.stream(commits.spliterator(), false).flatMap(it -> {
|
||||
|
||||
ParsedCommitMessage message = ParsedCommitMessage.parse(it.getFullMessage());
|
||||
|
||||
if (message.getTicketReference() == null) {
|
||||
logger.warn(toModuleIteration, "Commit %s does not refer to a ticket (%s)", it.getName(),
|
||||
it.getShortMessage());
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return Stream.of(message.getTicketReference());
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
// make TicketReference unique
|
||||
Set<String> uniqueIds = new HashSet<>();
|
||||
List<TicketReference> uniqueTicketReferences = new ArrayList<>();
|
||||
|
||||
for (TicketReference reference : ticketReferences) {
|
||||
if (uniqueIds.add(reference.getId())) {
|
||||
uniqueTicketReferences.add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
uniqueTicketReferences.sort(Comparator.<TicketReference> naturalOrder().reversed());
|
||||
|
||||
return uniqueTicketReferences;
|
||||
}
|
||||
|
||||
protected ObjectId resolveLowerBoundary(Project project, TrainIteration iteration, VersionTags tags, Repository repo)
|
||||
throws IOException {
|
||||
|
||||
if (iteration.contains(project)) {
|
||||
|
||||
Optional<Tag> fromTag = tags.filter(iteration.getTrain()).findTag(iteration.getIteration());
|
||||
|
||||
Tag tag = fromTag.get();
|
||||
|
||||
return repo.parseCommit(repo.resolve(tag.getName()));
|
||||
}
|
||||
|
||||
return repo.resolve(getFirstCommit(repo));
|
||||
}
|
||||
|
||||
protected ObjectId resolveUpperBoundary(ModuleIteration iteration, VersionTags tags, Repository repo)
|
||||
throws IOException {
|
||||
|
||||
Optional<Tag> tag = tags.filter(iteration.getTrain()).findTag(iteration.getIteration());
|
||||
String rangeEnd = tag.map(Tag::getName).orElse(Branch.from(iteration).toString());
|
||||
return repo.parseCommit(repo.resolve(rangeEnd));
|
||||
}
|
||||
|
||||
private static String getFirstCommit(Repository repo) throws IOException {
|
||||
|
||||
try (RevWalk revWalk = new RevWalk(repo)) {
|
||||
return revWalk.parseCommit(repo.resolve("master")).getName();
|
||||
}
|
||||
}
|
||||
|
||||
private static Train getPreviousTrain(TrainIteration trainIteration) {
|
||||
|
||||
Train trainToUse = ReleaseTrains.CODD;
|
||||
|
||||
for (Train train : ReleaseTrains.trains()) {
|
||||
if (train.isBefore(trainIteration.getTrain())) {
|
||||
trainToUse = train;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return trainToUse;
|
||||
}
|
||||
|
||||
private Stream<Branch> getRemoteBranches(Project project) {
|
||||
|
||||
return doWithGit(project, git -> {
|
||||
@@ -901,4 +1043,9 @@ public class GitOperations {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class VersionedIterations {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.git;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.release.issues.TicketReference;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
class ParsedCommitMessage {
|
||||
|
||||
private static final Pattern JIRA_TICKET = Pattern.compile("(?>\\[)?([A-Z]+[ ]?-[ ]?\\d+)(?>\\])?");
|
||||
private static final Pattern GITHUB_TICKET = Pattern.compile("((?>#|gh-)\\d+)");
|
||||
|
||||
private static final Pattern GITHUB_CLOSE_SYNTAX = Pattern.compile(
|
||||
"(?>closes|closed|close|fixes|fixed|fix|resolves|resolved|resolve|see)[\\s:]*((?>#|gh-)\\d+)",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
|
||||
|
||||
private static final Pattern GITHUB_PREFIX_SYNTAX = Pattern.compile("^(#\\d+)");
|
||||
|
||||
private static final Pattern A_TICKET = Pattern
|
||||
.compile(String.format("(%s|%s)", JIRA_TICKET.pattern(), GITHUB_TICKET.pattern()));
|
||||
|
||||
private static final Pattern ORIGINAL_PULL_REQUEST = Pattern
|
||||
.compile("Original (?>pull request|PR|pullrequest):(?>\\s+)?" + A_TICKET.pattern(), Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static final Pattern RELATED_TICKET = Pattern.compile(
|
||||
"Related (?>tickets|ticket):(?>\\s+)?((" + A_TICKET.pattern() + "(?>[\\s,]*))+)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final String summary;
|
||||
private final String body;
|
||||
|
||||
private final TicketReference ticketReference;
|
||||
private final TicketReference pullRequestReference;
|
||||
private final List<TicketReference> relatedTickets;
|
||||
|
||||
private ParsedCommitMessage(String summary, String body, TicketReference ticketReference,
|
||||
TicketReference pullRequestReference, List<TicketReference> relatedTickets) {
|
||||
this.summary = summary;
|
||||
this.body = body;
|
||||
this.ticketReference = ticketReference;
|
||||
this.pullRequestReference = pullRequestReference;
|
||||
this.relatedTickets = relatedTickets;
|
||||
}
|
||||
|
||||
public static ParsedCommitMessage parse(String message) {
|
||||
|
||||
int lineBreak = message.indexOf('\n');
|
||||
|
||||
String summary = null;
|
||||
String body = null;
|
||||
TicketReference ticketReference = null;
|
||||
TicketReference pullRequestReference = null;
|
||||
|
||||
if (lineBreak > -1) {
|
||||
summary = message.substring(0, lineBreak).trim();
|
||||
body = message.substring(lineBreak + 1).trim();
|
||||
} else {
|
||||
summary = message.trim();
|
||||
}
|
||||
|
||||
// DATACASS-nnn - syntax
|
||||
Matcher jiraMatcher = JIRA_TICKET.matcher(summary);
|
||||
|
||||
if (jiraMatcher.find()) {
|
||||
|
||||
MatchResult mr = jiraMatcher.toMatchResult();
|
||||
|
||||
// allow […] syntax and start of message syntax
|
||||
if (mr.start(1) < 2) {
|
||||
int summaryStart = findSummaryIndex(summary, mr.end(1));
|
||||
|
||||
ticketReference = new TicketReference(jiraMatcher.group(1).toUpperCase(Locale.ROOT),
|
||||
summaryStart > -1 ? summary.substring(summaryStart) : summary, TicketReference.Style.Jira);
|
||||
}
|
||||
}
|
||||
|
||||
// Closes (gh-nnn|#nnn) syntax
|
||||
Matcher gitHubMatcher = GITHUB_CLOSE_SYNTAX.matcher(message);
|
||||
|
||||
// #nnn syntax
|
||||
Matcher gitHubPrefixMatcher = GITHUB_PREFIX_SYNTAX.matcher(summary);
|
||||
|
||||
if (gitHubPrefixMatcher.find()) {
|
||||
|
||||
MatchResult mr = gitHubPrefixMatcher.toMatchResult();
|
||||
if (mr.start(1) == 0) {
|
||||
int summaryStart = findSummaryIndex(summary, mr.end(1));
|
||||
|
||||
ticketReference = new TicketReference(gitHubPrefixMatcher.group(1).toUpperCase(Locale.ROOT),
|
||||
summaryStart > -1 ? summary.substring(summaryStart) : summary, TicketReference.Style.GitHub);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (gitHubMatcher.find()) {
|
||||
ticketReference = new TicketReference(gitHubMatcher.group(1), summary, TicketReference.Style.GitHub);
|
||||
}
|
||||
}
|
||||
|
||||
List<TicketReference> relatedTickets = new ArrayList<>();
|
||||
if (body != null) {
|
||||
Matcher relatedTicketsMatcher = RELATED_TICKET.matcher(body);
|
||||
|
||||
if (relatedTicketsMatcher.find()) {
|
||||
|
||||
String ticketIds[] = relatedTicketsMatcher.group(1).split(",");
|
||||
|
||||
for (String ticketId : ticketIds) {
|
||||
extractTicket(ticketId).ifPresent(relatedTickets::add);
|
||||
}
|
||||
}
|
||||
|
||||
while (gitHubMatcher.find()) {
|
||||
extractTicket(gitHubMatcher.group(1)).ifPresent(relatedTickets::add);
|
||||
}
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
|
||||
Matcher prMatcher = ORIGINAL_PULL_REQUEST.matcher(body);
|
||||
|
||||
if (prMatcher.find()) {
|
||||
|
||||
Optional<TicketReference> pullRequest = extractTicket(prMatcher.group(1));
|
||||
|
||||
if (pullRequest.isPresent()) {
|
||||
pullRequestReference = pullRequest.get();
|
||||
}
|
||||
|
||||
if (ticketReference == null && pullRequestReference != null) {
|
||||
ticketReference = pullRequestReference;
|
||||
pullRequestReference = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ParsedCommitMessage(summary, body, ticketReference, pullRequestReference, relatedTickets);
|
||||
}
|
||||
|
||||
protected static Optional<TicketReference> extractTicket(String ticketId) {
|
||||
|
||||
if (JIRA_TICKET.matcher(ticketId.trim()).matches()) {
|
||||
return Optional.of(new TicketReference(ticketId.trim(), null, TicketReference.Style.Jira));
|
||||
}
|
||||
|
||||
if (GITHUB_TICKET.matcher(ticketId.trim()).matches()) {
|
||||
return Optional.of(new TicketReference(ticketId.trim(), null, TicketReference.Style.GitHub));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static int findSummaryIndex(String summary, int startAt) {
|
||||
|
||||
int dash = summary.indexOf("- ", startAt);
|
||||
|
||||
if (dash > -1) {
|
||||
return dash + 2;
|
||||
}
|
||||
|
||||
int space = summary.indexOf(" ", startAt);
|
||||
|
||||
if (space > -1) {
|
||||
return space + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,12 +67,16 @@ public class Tag implements Comparable<Tag> {
|
||||
public Optional<ArtifactVersion> toArtifactVersion() {
|
||||
|
||||
try {
|
||||
return Optional.of(ArtifactVersion.of(getVersionSource()));
|
||||
return Optional.of(getRequiredArtifactVersion());
|
||||
} catch (IllegalArgumentException o_O) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public ArtifactVersion getRequiredArtifactVersion() {
|
||||
return ArtifactVersion.of(getVersionSource());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Tag} for the given {@link ArtifactVersion} based on the format of the current one.
|
||||
*
|
||||
|
||||
@@ -17,13 +17,26 @@ package org.springframework.data.release.git;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
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.ReleaseTrains;
|
||||
import org.springframework.data.release.model.Train;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -31,21 +44,26 @@ import org.springframework.util.Assert;
|
||||
* Value object to represent a collection of {@link Tag}s.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
public class VersionTags implements Streamable<Tag> {
|
||||
|
||||
private final Project project;
|
||||
private final List<Tag> tags;
|
||||
|
||||
/**
|
||||
* Creates a new {@link VersionTags} instance for the given {@link List} of {@link Tag}s.
|
||||
* Creates a new {@link VersionTags} instance for the given {@link Project} and {@link List} of {@link Tag}s.
|
||||
*
|
||||
* @param project must not be {@literal null}.
|
||||
* @param source must not be {@literal null}.
|
||||
*/
|
||||
VersionTags(List<Tag> source) {
|
||||
VersionTags(Project project, Collection<Tag> source) {
|
||||
|
||||
Assert.notNull(project, "Project must not be null!");
|
||||
Assert.notNull(source, "Tags must not be null!");
|
||||
|
||||
this.project = project;
|
||||
this.tags = source.stream().//
|
||||
filter(Tag::isVersionTag).//
|
||||
sorted().collect(Collectors.toList());
|
||||
@@ -93,4 +111,140 @@ public class VersionTags implements Streamable<Tag> {
|
||||
public Iterator<Tag> iterator() {
|
||||
return tags.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link VersionTags} that contains only tags from the given {@link Train}.
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
*/
|
||||
public VersionTags filter(Train train) {
|
||||
return filter((tag, ti) -> ti.getTrain().equals(train));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link VersionTags} from a filtered subset of this {@link VersionTags}.
|
||||
*
|
||||
* @param predicate
|
||||
* @return
|
||||
*/
|
||||
public VersionTags filter(BiPredicate<Tag, TrainIteration> predicate) {
|
||||
|
||||
Map<Tag, TrainIteration> iterations = newMap();
|
||||
|
||||
withIterations().forEach((tag, trainIteration) -> {
|
||||
|
||||
if (predicate.test(tag, trainIteration)) {
|
||||
iterations.put(tag, trainIteration);
|
||||
}
|
||||
});
|
||||
|
||||
return new VersionTags(project, iterations.keySet());
|
||||
}
|
||||
|
||||
Map<Tag, TrainIteration> withIterations() {
|
||||
|
||||
Map<Tag, TrainIteration> iterations = newMap();
|
||||
|
||||
for (Tag tag : tags) {
|
||||
|
||||
Optional<ArtifactVersion> artifactVersion = tag.toArtifactVersion();
|
||||
if (!artifactVersion.isPresent()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ArtifactVersion version = artifactVersion.get();
|
||||
Optional<Iteration> iteration = toIteration(version);
|
||||
|
||||
if (!iteration.isPresent()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<TrainIteration> ti = getTrainIteration(version, project, iteration.get());
|
||||
|
||||
ti.ifPresent(it -> iterations.put(tag, it));
|
||||
}
|
||||
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public Optional<TrainIteration> findMostRecentTrainIterationBefore(Iteration iterationToFind) {
|
||||
return find((tag, iteration) -> iteration.getIteration().compareTo(iterationToFind) < 0, Pair::getSecond);
|
||||
}
|
||||
|
||||
public Optional<Tag> findTag(Iteration iterationToFind) {
|
||||
return find((tag, iteration) -> iteration.getIteration().compareTo(iterationToFind) == 0, Pair::getFirst);
|
||||
}
|
||||
|
||||
public <T> Optional<T> find(BiPredicate<Tag, TrainIteration> filter,
|
||||
Function<Pair<Tag, TrainIteration>, T> resultExtractor) {
|
||||
|
||||
for (Map.Entry<Tag, TrainIteration> entry : withIterations().entrySet()) {
|
||||
|
||||
if (filter.test(entry.getKey(), entry.getValue())) {
|
||||
return Optional.of(resultExtractor.apply(Pair.of(entry.getKey(), entry.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static <K, V> TreeMap<K, V> newMap() {
|
||||
return new TreeMap(Comparator.reverseOrder());
|
||||
}
|
||||
|
||||
private static Optional<TrainIteration> getTrainIteration(ArtifactVersion version, Project project,
|
||||
Iteration iteration) {
|
||||
|
||||
// consider major version bump during ReleaseTrain
|
||||
boolean minorIncrementedButTargetsNextTrain = false;
|
||||
|
||||
for (Train train : ReleaseTrains.trains()) {
|
||||
|
||||
TrainIteration ti = new TrainIteration(train, iteration);
|
||||
|
||||
if (!ti.contains(project)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minorIncrementedButTargetsNextTrain) {
|
||||
return Optional.of(ti);
|
||||
}
|
||||
|
||||
ModuleIteration mi = ti.getModule(project);
|
||||
|
||||
ArtifactVersion artifactVersion = ArtifactVersion.of(mi);
|
||||
|
||||
if (artifactVersion.equals(version)) {
|
||||
return Optional.of(ti);
|
||||
}
|
||||
|
||||
if (artifactVersion.getNextMinorVersion().equals(version)) {
|
||||
minorIncrementedButTargetsNextTrain = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<Iteration> toIteration(ArtifactVersion version) {
|
||||
|
||||
if (version.isMilestoneVersion()) {
|
||||
return Optional.of(Iteration.valueOf("M" + version.getLevel()));
|
||||
}
|
||||
|
||||
if (version.isReleaseCandidateVersion()) {
|
||||
return Optional.of(Iteration.valueOf("RC" + version.getLevel()));
|
||||
}
|
||||
|
||||
if (version.isBugFixVersion()) {
|
||||
return Optional.of(Iteration.valueOf("SR" + version.getLevel()));
|
||||
}
|
||||
|
||||
if (version.isReleaseVersion()) {
|
||||
return Optional.of(Iteration.GA);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,11 @@ public class Changelog {
|
||||
|
||||
String summary = ticket.getSummary();
|
||||
|
||||
builder.append("* ").append(ticket.getId()).append(" - ").append(summary.trim());
|
||||
builder.append("* ").append(ticket.getId()).append(" - ").append(summary != null ? summary.trim() : "");
|
||||
|
||||
if (summary == null) {
|
||||
System.out.println();
|
||||
}
|
||||
if (!summary.endsWith(".")) {
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.issues;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
public class TicketReference implements Comparable<TicketReference> {
|
||||
|
||||
String id;
|
||||
String message;
|
||||
Style style;
|
||||
|
||||
public TicketReference(String id, String message, Style style) {
|
||||
this.id = normalize(id);
|
||||
this.message = message;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
private static String normalize(String id) {
|
||||
|
||||
if (id.toLowerCase().startsWith("gh-")) {
|
||||
return "#" + id.substring(3);
|
||||
}
|
||||
|
||||
return id.toUpperCase().replaceAll(" ", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TicketReference o) {
|
||||
|
||||
if (id.startsWith("#") && o.id.startsWith("#")) {
|
||||
return Integer.compare(Integer.parseInt(id.substring(1)), Integer.parseInt(o.id.substring(1)));
|
||||
}
|
||||
|
||||
return id.compareToIgnoreCase(o.id);
|
||||
}
|
||||
|
||||
public enum Style {
|
||||
GitHub, Jira;
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.release.model;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.util.Assert;
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
|
||||
private static final Pattern PATTERN = Pattern
|
||||
@@ -40,12 +39,13 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
.compile("((\\d+)\\.(\\d+)(\\.\\d+)?)(-((RC\\d+)|(M\\d+)|(SNAPSHOT)))?");
|
||||
|
||||
private static final String RELEASE_SUFFIX = "RELEASE";
|
||||
private static final String MILESTONE_SUFFIX = "M\\d|RC\\d";
|
||||
private static final String MILESTONE_SUFFIX = "M\\d";
|
||||
private static final String RC_SUFFIX = "RC\\d";
|
||||
private static final String SNAPSHOT_SUFFIX = "BUILD-SNAPSHOT";
|
||||
private static final String SNAPSHOT_MODIFIER = "SNAPSHOT";
|
||||
|
||||
private static final String VALID_SUFFIX = String.format("%s|%s|%s|-%s|-%s|-%s", RELEASE_SUFFIX, MILESTONE_SUFFIX,
|
||||
SNAPSHOT_SUFFIX, RELEASE_SUFFIX, MILESTONE_SUFFIX, SNAPSHOT_MODIFIER);
|
||||
private static final String VALID_SUFFIX = String.format("%s|%s|%s|%s|-%s|-%s|-%s", RELEASE_SUFFIX, MILESTONE_SUFFIX,
|
||||
RC_SUFFIX, SNAPSHOT_SUFFIX, RELEASE_SUFFIX, MILESTONE_SUFFIX, SNAPSHOT_MODIFIER);
|
||||
|
||||
private final @Getter Version version;
|
||||
private final @With boolean modifierFormat;
|
||||
@@ -175,6 +175,39 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
return suffix.matches(MILESTONE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the version is a RC version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseCandidateVersion() {
|
||||
return suffix.matches(RC_SUFFIX);
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
|
||||
if (isMilestoneVersion()) {
|
||||
Pattern pattern = Pattern.compile("M(\\d+)");
|
||||
Matcher matcher = pattern.matcher(suffix);
|
||||
matcher.find();
|
||||
return Integer.parseInt(matcher.group(1));
|
||||
}
|
||||
|
||||
if (isReleaseCandidateVersion()) {
|
||||
Pattern pattern = Pattern.compile("RC(\\d+)");
|
||||
Matcher matcher = pattern.matcher(suffix);
|
||||
matcher.find();
|
||||
return Integer.parseInt(matcher.group(1));
|
||||
}
|
||||
|
||||
if (isBugFixVersion()) {
|
||||
return version.getBugfix();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Not a M/RC/SR release");
|
||||
|
||||
}
|
||||
|
||||
public boolean isSnapshotVersion() {
|
||||
return suffix.matches(SNAPSHOT_SUFFIX) || suffix.matches(SNAPSHOT_MODIFIER);
|
||||
}
|
||||
@@ -217,9 +250,16 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
return isSnapshotVersion() ? this : new ArtifactVersion(version, modifierFormat, getSnapshotSuffix());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next minor version retaining the modifier and snapshot suffix.
|
||||
*/
|
||||
public ArtifactVersion getNextMinorVersion() {
|
||||
return new ArtifactVersion(version.nextMinor(), modifierFormat, suffix);
|
||||
}
|
||||
|
||||
public String getReleaseTrainSuffix() {
|
||||
|
||||
if (isSnapshotVersion() || isMilestoneVersion()) {
|
||||
if (isSnapshotVersion() || isMilestoneVersion() || isReleaseCandidateVersion()) {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
@@ -250,7 +290,7 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
|
||||
if (modifierFormat) {
|
||||
|
||||
if (isSnapshotVersion() || isMilestoneVersion()) {
|
||||
if (isSnapshotVersion() || isMilestoneVersion() || isReleaseCandidateVersion()) {
|
||||
return String.format("%s-%s", version.toMajorMinorBugfix(), suffix);
|
||||
}
|
||||
|
||||
@@ -276,4 +316,24 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
|
||||
public String getGeneration() {
|
||||
return String.format("%s.%s.x", version.getMajor(), version.getMinor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ArtifactVersion)) {
|
||||
return false;
|
||||
}
|
||||
ArtifactVersion other = (ArtifactVersion) o;
|
||||
return version.equals(other.version) && isReleaseVersion() == other.isReleaseVersion()
|
||||
&& isSnapshotVersion() == other.isSnapshotVersion() && isMilestoneVersion() == other.isMilestoneVersion()
|
||||
&& isReleaseCandidateVersion() == other.isReleaseCandidateVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(version, isReleaseVersion(), isSnapshotVersion(), isMilestoneVersion(),
|
||||
isReleaseCandidateVersion());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,4 +139,8 @@ public class ReleaseTrains {
|
||||
.findFirst() //
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static List<Train> trains() {
|
||||
return TRAINS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,10 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode(of = "name")
|
||||
public class Train implements Streamable<Module> {
|
||||
|
||||
private final String name;;
|
||||
private final String name;
|
||||
private final Modules modules;
|
||||
private @Nullable Version calver;
|
||||
private @With Iterations iterations;
|
||||
@@ -136,7 +137,7 @@ public class Train implements Streamable<Module> {
|
||||
|
||||
Module module = getModule(project);
|
||||
|
||||
return new ModuleIteration(module, getIteration(iteration));
|
||||
return new ModuleIteration(module, doGetTrainIteration(iteration));
|
||||
}
|
||||
|
||||
public Iterable<ModuleIteration> getModuleIterations(Iteration iteration) {
|
||||
@@ -154,7 +155,7 @@ public class Train implements Streamable<Module> {
|
||||
}
|
||||
|
||||
public TrainIteration getIteration(String name) {
|
||||
return new TrainIteration(this, iterations.getIterationByName(name));
|
||||
return doGetTrainIteration(iterations.getIterationByName(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +185,31 @@ public class Train implements Streamable<Module> {
|
||||
return new Train(name, Modules.of(modules), calver, iterations, alwaysUseBranch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this train comes before {@link Train other}.
|
||||
*
|
||||
* @param other
|
||||
* @return
|
||||
*/
|
||||
public boolean isBefore(Train other) {
|
||||
|
||||
Assert.notNull(other, "Train must not be null");
|
||||
|
||||
if (usesCalver() && other.usesCalver()) {
|
||||
return getCalver().compareTo(other.getCalver()) < 0;
|
||||
}
|
||||
|
||||
if (usesCalver()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other.usesCalver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return getName().compareToIgnoreCase(other.getName()) < 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
@@ -216,6 +242,10 @@ public class Train implements Streamable<Module> {
|
||||
Assert.isTrue(iterations.contains(iteration),
|
||||
String.format("Iteration %s is not a valid one for the configured iterations %s!", iteration, iterations));
|
||||
|
||||
return doGetTrainIteration(iteration);
|
||||
}
|
||||
|
||||
protected TrainIteration doGetTrainIteration(Iteration iteration) {
|
||||
return new TrainIteration(this, iteration);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,17 +23,24 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
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.model.Iteration;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.TestReleaseTrains;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Disabled
|
||||
class GitOperationsIntegrationTests extends AbstractIntegrationTests {
|
||||
@@ -75,4 +82,49 @@ class GitOperationsIntegrationTests extends AbstractIntegrationTests {
|
||||
void obtainsVersionTagsForRepoThatAlsoHasOtherTags() {
|
||||
gitOperations.getTags(MONGO_DB);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeterminePreviousIterationFromGA() throws Exception {
|
||||
|
||||
TrainIteration hopperRc1 = gitOperations.getPreviousIteration(ReleaseTrains.HOPPER.getIteration(Iteration.GA));
|
||||
|
||||
assertThat(hopperRc1.getTrain()).isEqualTo(ReleaseTrains.HOPPER);
|
||||
assertThat(hopperRc1.getIteration()).isEqualTo(Iteration.RC1);
|
||||
|
||||
TrainIteration hopperM1 = gitOperations.getPreviousIteration(ReleaseTrains.HOPPER.getIteration(Iteration.RC1));
|
||||
|
||||
assertThat(hopperM1.getTrain()).isEqualTo(ReleaseTrains.HOPPER);
|
||||
assertThat(hopperM1.getIteration()).isEqualTo(Iteration.M1);
|
||||
|
||||
TrainIteration hopperSR9 = gitOperations.getPreviousIteration(ReleaseTrains.HOPPER.getIteration(Iteration.SR10));
|
||||
|
||||
assertThat(hopperSR9.getTrain()).isEqualTo(ReleaseTrains.HOPPER);
|
||||
assertThat(hopperSR9.getIteration()).isEqualTo(Iteration.SR9);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verify() throws Exception {
|
||||
|
||||
// gitOperations.update(ReleaseTrains.HOPPER);
|
||||
|
||||
try (Git git = new Git(new FileRepository("/Users/mpaluch/git/data/spring-data-elasticsearch/.git"))) {
|
||||
|
||||
ObjectId resolve = git.getRepository().parseCommit(git.getRepository().resolve("3.0.0.RELEASE"));
|
||||
Iterable<RevCommit> master = git.log().addRange(resolve, git.getRepository().resolve("master")).call();
|
||||
|
||||
for (RevCommit revCommit : master) {
|
||||
|
||||
ParsedCommitMessage message = ParsedCommitMessage.parse(revCommit.getFullMessage());
|
||||
|
||||
if (message.getTicketReference() == null || message.getSummary() == null) {
|
||||
System.out.println(revCommit.getFullMessage());
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception o_O) {
|
||||
throw new RuntimeException(o_O);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.git;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ParsedCommitMessage}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ParsedCommitMessageUnitTests {
|
||||
|
||||
@Test
|
||||
void shouldParsePlainCommit() {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage.parse("hello world");
|
||||
assertThat(commit.getSummary()).isEqualTo("hello world");
|
||||
assertThat(commit.getBody()).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "DATAFOO-123 - Hello World.", "DATAFOO-123 Hello World.", "[DATAFOO-123] - Hello World.",
|
||||
"DATAFOO - 123 - Hello World." })
|
||||
void shouldParseOneLinerCommitWithJiraTicket(String commitMessage) {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage.parse(commitMessage);
|
||||
|
||||
assertThat(commit.getSummary()).endsWith("Hello World.");
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("DATAFOO-123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void considersMultipleTicketsAsRelatedTickets() {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage
|
||||
.parse("Polishing\n\nAdd tests for write and delete.\n\nCloses gh-503\nCloses gh-511");
|
||||
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("#503");
|
||||
assertThat(commit.getRelatedTickets()).hasSize(1);
|
||||
assertThat(commit.getRelatedTickets().get(0).getId()).isEqualTo("#511");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved" })
|
||||
void considersResolvesSyntax(String prefix) {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage.parse("Polishing\n\n" + prefix + " gh-586.");
|
||||
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("#586");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "Incorporate review feedback\n\nSee gh-574." })
|
||||
void shouldParseCommitWithSeeTicket(String commitMessage) {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage.parse(commitMessage);
|
||||
|
||||
assertThat(commit.getSummary()).isEqualTo("Incorporate review feedback");
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("#574");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseCommitWithRelatedTickets() {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage
|
||||
.parse("DATAFOO-123 - Hello World.\n Related tickets: DATACMNS-1438, DATACMNS-1461, DATACMNS-1609.");
|
||||
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("DATAFOO-123");
|
||||
assertThat(commit.getRelatedTickets()).hasSize(3);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "DATAFOO-456 - Hello World.\n Original pull request: #415.",
|
||||
"DATAFOO-456 - Hello World.\n Original pr: #415." })
|
||||
void shouldParseCommitWithPullRequest(String commitMessage) {
|
||||
|
||||
ParsedCommitMessage commit = ParsedCommitMessage.parse(commitMessage);
|
||||
|
||||
assertThat(commit.getTicketReference().getId()).isEqualTo("DATAFOO-456");
|
||||
assertThat(commit.getPullRequestReference()).isNotNull();
|
||||
assertThat(commit.getPullRequestReference().getId()).isEqualTo("#415");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.git;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link VersionTags}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class VersionTagsUnitTests {
|
||||
|
||||
@Test
|
||||
void shouldDetermineTagForIteration() {
|
||||
|
||||
VersionTags tags = new VersionTags(Projects.NEO4J,
|
||||
Stream.of("5.3.0.RELEASE", "5.4.0-M1", "6.0.0-M2").map(Tag::of).collect(Collectors.toList()));
|
||||
|
||||
assertThat(tags.filter(ReleaseTrains.NEUMANN).findTag(Iteration.GA))
|
||||
.hasValueSatisfying(actual -> assertThat(actual.getName()).isEqualTo("5.3.0.RELEASE"));
|
||||
assertThat(tags.filter(ReleaseTrains.OCKHAM).findTag(Iteration.M2))
|
||||
.hasValueSatisfying(actual -> assertThat(actual.getName()).isEqualTo("6.0.0-M2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDetermineTagForMajorVersionBumpDuringDevelopment() {
|
||||
|
||||
VersionTags tags = new VersionTags(Projects.NEO4J,
|
||||
Stream.of("5.3.0.RELEASE", "5.4.0-M1", "6.0.0-M2").map(Tag::of).collect(Collectors.toList()));
|
||||
|
||||
assertThat(tags.filter(ReleaseTrains.OCKHAM).findTag(Iteration.M1))
|
||||
.hasValueSatisfying(actual -> assertThat(actual.getName()).isEqualTo("5.4.0-M1"));
|
||||
}
|
||||
}
|
||||
@@ -47,4 +47,23 @@ class TrainsUnitTest {
|
||||
|
||||
assertThat(ReleaseTrains.PASCAL.getModule(Projects.BOM).getVersion().toMajorMinorBugfix()).isEqualTo("2021.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void beforeShouldConsiderNonCalver() {
|
||||
assertThat(ReleaseTrains.HOPPER.isBefore(ReleaseTrains.GOSLING)).isFalse();
|
||||
assertThat(ReleaseTrains.HOPPER.isBefore(ReleaseTrains.INGALLS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void beforeShouldConsiderCalver() {
|
||||
assertThat(ReleaseTrains.PASCAL.isBefore(ReleaseTrains.OCKHAM)).isFalse();
|
||||
assertThat(ReleaseTrains.OCKHAM.isBefore(ReleaseTrains.PASCAL)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void beforeShouldConsiderMixedCalver() {
|
||||
assertThat(ReleaseTrains.PASCAL.isBefore(ReleaseTrains.INGALLS)).isFalse();
|
||||
assertThat(ReleaseTrains.INGALLS.isBefore(ReleaseTrains.PASCAL)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user