#2 - Provide command to list all issue branches alongside their state in the issue tracker
This commit is contained in:
committed by
Oliver Gierke
parent
8abad1385f
commit
67e5b36bc6
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,4 +5,6 @@ target/
|
|||||||
.factorypath
|
.factorypath
|
||||||
.springBeans
|
.springBeans
|
||||||
application-local.properties
|
application-local.properties
|
||||||
spring-shell.log
|
spring-shell.log
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
@@ -31,7 +31,7 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
class Branch {
|
public class Branch implements Comparable<Branch> {
|
||||||
|
|
||||||
public static final Branch MASTER = new Branch("master");
|
public static final Branch MASTER = new Branch("master");
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ class Branch {
|
|||||||
return MASTER.equals(this);
|
return MASTER.equals(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see java.lang.Object#toString()
|
* @see java.lang.Object#toString()
|
||||||
*/
|
*/
|
||||||
@@ -81,4 +81,9 @@ class Branch {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Branch o) {
|
||||||
|
return name.compareToIgnoreCase(o.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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 java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.ArtifactVersion;
|
||||||
|
import org.springframework.data.release.model.ModuleIteration;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object to represent a collection of {@link Branch}es.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class Branches implements Iterable<Branch> {
|
||||||
|
|
||||||
|
private final List<Branch> branches;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Branches} instance for the given {@link List} of {@link Branch}es.
|
||||||
|
*
|
||||||
|
* @param source must not be {@literal null}.
|
||||||
|
*/
|
||||||
|
Branches(List<Branch> source) {
|
||||||
|
|
||||||
|
Assert.notNull(source, "Tags must not be null!");
|
||||||
|
|
||||||
|
this.branches = source.stream().//
|
||||||
|
sorted().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all {@link Branch}es as {@link List}.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<Branch> asList() {
|
||||||
|
return branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see java.lang.Iterable#iterator()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Branch> iterator() {
|
||||||
|
return branches.iterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.release.git;
|
package org.springframework.data.release.git;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import java.util.Optional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -23,6 +23,8 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.release.CliComponent;
|
import org.springframework.data.release.CliComponent;
|
||||||
|
import org.springframework.data.release.jira.Ticket;
|
||||||
|
import org.springframework.data.release.jira.TicketBranches;
|
||||||
import org.springframework.data.release.model.Project;
|
import org.springframework.data.release.model.Project;
|
||||||
import org.springframework.data.release.model.ReleaseTrains;
|
import org.springframework.data.release.model.ReleaseTrains;
|
||||||
import org.springframework.data.release.model.Train;
|
import org.springframework.data.release.model.Train;
|
||||||
@@ -30,8 +32,12 @@ import org.springframework.data.release.model.TrainIteration;
|
|||||||
import org.springframework.shell.core.CommandMarker;
|
import org.springframework.shell.core.CommandMarker;
|
||||||
import org.springframework.shell.core.annotation.CliCommand;
|
import org.springframework.shell.core.annotation.CliCommand;
|
||||||
import org.springframework.shell.core.annotation.CliOption;
|
import org.springframework.shell.core.annotation.CliOption;
|
||||||
|
import org.springframework.shell.support.table.Table;
|
||||||
|
import org.springframework.shell.support.table.TableHeader;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
*/
|
*/
|
||||||
@@ -67,8 +73,8 @@ public class GitCommands implements CommandMarker {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets all projects contained in the given {@link Train}.
|
* Resets all projects contained in the given {@link Train}.
|
||||||
*
|
*
|
||||||
* @param trainName
|
* @param iteration
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@CliCommand("git reset")
|
@CliCommand("git reset")
|
||||||
@@ -84,7 +90,7 @@ public class GitCommands implements CommandMarker {
|
|||||||
/**
|
/**
|
||||||
* Pushes all changes of all modules of the given {@link TrainIteration} to the remote server. If {@code tags} is
|
* Pushes all changes of all modules of the given {@link TrainIteration} to the remote server. If {@code tags} is
|
||||||
* given, only the tags are pushed.
|
* given, only the tags are pushed.
|
||||||
*
|
*
|
||||||
* @param iteration
|
* @param iteration
|
||||||
* @param tags
|
* @param tags
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
@@ -118,4 +124,44 @@ public class GitCommands implements CommandMarker {
|
|||||||
|
|
||||||
git.backportChangelogs(iteration, targets);
|
git.backportChangelogs(iteration, targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the branches with their tickets of the git repository.
|
||||||
|
*
|
||||||
|
* @param projectName
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@CliCommand("git issuebranches")
|
||||||
|
public Table issuebranches(@CliOption(key = { "" }, mandatory = true) String projectName,
|
||||||
|
@CliOption(key = "resolved") Boolean resolved) throws Exception {
|
||||||
|
|
||||||
|
Project project = ReleaseTrains.getProjectByName(projectName);
|
||||||
|
|
||||||
|
Table table = new Table();
|
||||||
|
TicketBranches ticketBranches = git.listTicketBranches(project);
|
||||||
|
|
||||||
|
table.addHeader(1, new TableHeader("Branch"));
|
||||||
|
table.addHeader(2, new TableHeader("Status"));
|
||||||
|
table.addHeader(3, new TableHeader("Description"));
|
||||||
|
|
||||||
|
ticketBranches.stream().sorted().//
|
||||||
|
filter(branch -> {
|
||||||
|
Optional<Ticket> ticket = ticketBranches.getTicket(branch);
|
||||||
|
if (resolved != null && resolved) {
|
||||||
|
if (!ticket.isPresent() || (ticket.isPresent() && ticket.get().getTicketStatus().isResolved())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}).//
|
||||||
|
forEachOrdered(branch -> {
|
||||||
|
Optional<Ticket> ticket = ticketBranches.getTicket(branch);
|
||||||
|
table.addRow(branch.toString(), ticket.map(t -> t.getTicketStatus().getLabel()).orElse(""),
|
||||||
|
ticket.map(t -> t.getSummary()).orElse(""));
|
||||||
|
});
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.release.git;
|
package org.springframework.data.release.git;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import org.eclipse.jgit.api.CheckoutCommand;
|
import org.eclipse.jgit.api.CheckoutCommand;
|
||||||
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
|
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.api.ResetCommand.ResetType;
|
import org.eclipse.jgit.api.ResetCommand.ResetType;
|
||||||
import org.eclipse.jgit.api.errors.RefNotFoundException;
|
import org.eclipse.jgit.api.errors.RefNotFoundException;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
@@ -40,6 +47,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.data.release.io.Workspace;
|
import org.springframework.data.release.io.Workspace;
|
||||||
import org.springframework.data.release.jira.IssueTracker;
|
import org.springframework.data.release.jira.IssueTracker;
|
||||||
import org.springframework.data.release.jira.Ticket;
|
import org.springframework.data.release.jira.Ticket;
|
||||||
|
import org.springframework.data.release.jira.TicketBranches;
|
||||||
import org.springframework.data.release.model.ArtifactVersion;
|
import org.springframework.data.release.model.ArtifactVersion;
|
||||||
import org.springframework.data.release.model.ModuleIteration;
|
import org.springframework.data.release.model.ModuleIteration;
|
||||||
import org.springframework.data.release.model.Project;
|
import org.springframework.data.release.model.Project;
|
||||||
@@ -93,7 +101,7 @@ public class GitOperations {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks out all projects of the given {@link TrainIteration}.
|
* Checks out all projects of the given {@link TrainIteration}.
|
||||||
*
|
*
|
||||||
* @param train
|
* @param train
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@@ -269,6 +277,63 @@ public class GitOperations {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a list of remote branches where their related ticket is resolved.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TicketBranches listTicketBranches(Project project) {
|
||||||
|
|
||||||
|
IssueTracker tracker = issueTracker.getPluginFor(project);
|
||||||
|
|
||||||
|
try (Git git = new Git(getRepository(project))) {
|
||||||
|
update(project);
|
||||||
|
Pattern pattern = Pattern.compile(project.getTracker().getTicketPattern());
|
||||||
|
|
||||||
|
Collection<Ref> branches = git.lsRemote().setHeads(true).setTags(false).call();
|
||||||
|
|
||||||
|
Set<String> possibleTicketIds = branches.stream().//
|
||||||
|
filter(branch -> pattern.matcher(branch.getName()).find()).//
|
||||||
|
map(branch -> {
|
||||||
|
Matcher matcher = pattern.matcher(branch.getName());
|
||||||
|
matcher.find();
|
||||||
|
return matcher.group(1);
|
||||||
|
}).//
|
||||||
|
collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Collection<Ticket> tickets = tracker.findTickets(project, possibleTicketIds);
|
||||||
|
Map<String, Ticket> ticketMap = tickets.stream().collect(Collectors.toMap(Ticket::getId, ticket -> ticket));
|
||||||
|
|
||||||
|
Map<Branch, Ticket> ticketBranches = new HashMap<>();
|
||||||
|
branches.stream().//
|
||||||
|
map(branch -> {
|
||||||
|
if (branch.getName().startsWith(Constants.R_HEADS)) {
|
||||||
|
return branch.getName().substring(Constants.R_HEADS.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.getName().startsWith(Constants.R_REMOTES)) {
|
||||||
|
return branch.getName().substring(Constants.R_REMOTES.length());
|
||||||
|
}
|
||||||
|
return branch.getName();
|
||||||
|
}).filter(branchName -> {
|
||||||
|
Matcher matcher = pattern.matcher(branchName);
|
||||||
|
return matcher.find();
|
||||||
|
}).//
|
||||||
|
forEach((branchName) -> {
|
||||||
|
|
||||||
|
Matcher matcher = pattern.matcher(branchName);
|
||||||
|
matcher.find();
|
||||||
|
String ticketId = matcher.group(1);
|
||||||
|
ticketBranches.put(Branch.from(branchName), ticketMap.get(ticketId));
|
||||||
|
});
|
||||||
|
|
||||||
|
return TicketBranches.from(ticketBranches);
|
||||||
|
} catch (Exception o_O) {
|
||||||
|
throw new RuntimeException(o_O);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void tagRelease(TrainIteration iteration) {
|
public void tagRelease(TrainIteration iteration) {
|
||||||
|
|
||||||
ExecutionUtils.run(iteration, module -> {
|
ExecutionUtils.run(iteration, module -> {
|
||||||
@@ -300,7 +365,7 @@ public class GitOperations {
|
|||||||
/**
|
/**
|
||||||
* Commits all changes currently made to all modules of the given {@link TrainIteration}. The summary can contain a
|
* Commits all changes currently made to all modules of the given {@link TrainIteration}. The summary can contain a
|
||||||
* single {@code %s} placeholder which the version of the current module will get replace into.
|
* single {@code %s} placeholder which the version of the current module will get replace into.
|
||||||
*
|
*
|
||||||
* @param iteration must not be {@literal null}.
|
* @param iteration must not be {@literal null}.
|
||||||
* @param summary must not be {@literal null} or empty.
|
* @param summary must not be {@literal null} or empty.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
@@ -328,7 +393,7 @@ public class GitOperations {
|
|||||||
/**
|
/**
|
||||||
* Commits the given files for the given {@link ModuleIteration} using the given summary for the commit message. If no
|
* Commits the given files for the given {@link ModuleIteration} using the given summary for the commit message. If no
|
||||||
* files are given, all pending changes are committed.
|
* files are given, all pending changes are committed.
|
||||||
*
|
*
|
||||||
* @param module must not be {@literal null}.
|
* @param module must not be {@literal null}.
|
||||||
* @param summary must not be {@literal null} or empty.
|
* @param summary must not be {@literal null} or empty.
|
||||||
* @param files can be empty.
|
* @param files can be empty.
|
||||||
@@ -505,7 +570,7 @@ public class GitOperations {
|
|||||||
* starting with the release ticket identifier, followed by a dash separated by spaces and the key word
|
* starting with the release ticket identifier, followed by a dash separated by spaces and the key word
|
||||||
* {@code Release}. To prevent skimming through the entire Git history, we expect such a commit to be found within the
|
* {@code Release}. To prevent skimming through the entire Git history, we expect such a commit to be found within the
|
||||||
* 50 most recent commits.
|
* 50 most recent commits.
|
||||||
*
|
*
|
||||||
* @param module
|
* @param module
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class GitHubIssue {
|
|||||||
|
|
||||||
private String number;
|
private String number;
|
||||||
private String title;
|
private String title;
|
||||||
|
private String state;
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "#".concat(number);
|
return "#".concat(number);
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.release.jira;
|
package org.springframework.data.release.jira;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -24,6 +22,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
@@ -41,6 +42,8 @@ import org.springframework.data.release.utils.Logger;
|
|||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.client.HttpStatusCodeException;
|
||||||
import org.springframework.web.client.RestOperations;
|
import org.springframework.web.client.RestOperations;
|
||||||
import org.springframework.web.util.UriTemplate;
|
import org.springframework.web.util.UriTemplate;
|
||||||
|
|
||||||
@@ -48,19 +51,34 @@ import org.springframework.web.util.UriTemplate;
|
|||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
class GitHubIssueTracker implements IssueTracker {
|
class GitHubIssueTracker implements GitHubConnector {
|
||||||
|
|
||||||
private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}";
|
private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}";
|
||||||
private static final String URI_TEMPLATE = "https://api.github.com/repos/spring-projects/{repoName}/issues?milestone={id}&state=all";
|
private static final String ISSUES_BY_MILESTONE_URI_TEMPLATE = "https://api.github.com/repos/spring-projects/{repoName}/issues?milestone={id}&state=all";
|
||||||
|
private static final String ISSUE_BY_ID_URI_TEMPLATE = "https://api.github.com/repos/spring-projects/{repoName}/issues/{id}";
|
||||||
|
|
||||||
private static final ParameterizedTypeReference<List<GitHubMilestone>> MILESTONES_TYPE = new ParameterizedTypeReference<List<GitHubMilestone>>() {};
|
private static final ParameterizedTypeReference<List<GitHubMilestone>> MILESTONES_TYPE = new ParameterizedTypeReference<List<GitHubMilestone>>() {
|
||||||
private static final ParameterizedTypeReference<List<GitHubIssue>> ISSUES_TYPE = new ParameterizedTypeReference<List<GitHubIssue>>() {};
|
};
|
||||||
|
private static final ParameterizedTypeReference<List<GitHubIssue>> ISSUES_TYPE = new ParameterizedTypeReference<List<GitHubIssue>>() {
|
||||||
|
};
|
||||||
|
private static final ParameterizedTypeReference<GitHubIssue> ISSUE_TYPE = new ParameterizedTypeReference<GitHubIssue>() {
|
||||||
|
};
|
||||||
|
|
||||||
private final RestOperations operations;
|
private final RestOperations operations;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final GitProperties properties;
|
private final GitProperties properties;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see org.springframework.data.release.jira.JiraConnector#flushTickets()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@CacheEvict(value = "tickets", allEntries = true)
|
||||||
|
public void reset() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.release.jira.IssueTracker#getReleaseTicketFor(org.springframework.data.release.model.ModuleIteration)
|
* @see org.springframework.data.release.jira.IssueTracker#getReleaseTicketFor(org.springframework.data.release.model.ModuleIteration)
|
||||||
*/
|
*/
|
||||||
@@ -71,12 +89,50 @@ class GitHubIssueTracker implements IssueTracker {
|
|||||||
return getIssuesFor(module).stream().//
|
return getIssuesFor(module).stream().//
|
||||||
filter(issue -> issue.isReleaseTicket(module)).//
|
filter(issue -> issue.isReleaseTicket(module)).//
|
||||||
findFirst().//
|
findFirst().//
|
||||||
map(issue -> new Ticket(issue.getId(), issue.getTitle())).//
|
map(issue -> toTicket(issue)).//
|
||||||
orElseThrow(
|
orElseThrow(
|
||||||
() -> new IllegalArgumentException(String.format("Could not find a release ticket for %s!", module)));
|
() -> new IllegalArgumentException(String.format("Could not find a release ticket for %s!", module)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private Ticket toTicket(GitHubIssue issue) {
|
||||||
|
return new Ticket(issue.getId(), issue.getTitle(), new GithubTicketStatus(issue.getState()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
|
* @see IssueTracker#findTickets(Project, Collection)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Cacheable("tickets")
|
||||||
|
public Collection<Ticket> findTickets(Project project, Collection<String> ticketIds) {
|
||||||
|
|
||||||
|
String repositoryName = new GitProject(project, new GitServer()).getRepositoryName();
|
||||||
|
List<Ticket> tickets = new ArrayList<>();
|
||||||
|
for (String ticketId : ticketIds) {
|
||||||
|
|
||||||
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
|
parameters.put("repoName", repositoryName);
|
||||||
|
parameters.put("id", ticketId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
GitHubIssue gitHubIssue = operations
|
||||||
|
.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.GET, new HttpEntity<>(getAuthenticationHeaders()), ISSUE_TYPE, parameters)
|
||||||
|
.getBody();
|
||||||
|
|
||||||
|
tickets.add(toTicket(gitHubIssue));
|
||||||
|
} catch (HttpStatusCodeException e) {
|
||||||
|
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.release.jira.IssueTracker#getChangelogFor(org.springframework.data.release.model.ModuleIteration)
|
* @see org.springframework.data.release.jira.IssueTracker#getChangelogFor(org.springframework.data.release.model.ModuleIteration)
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +141,7 @@ class GitHubIssueTracker implements IssueTracker {
|
|||||||
public Changelog getChangelogFor(ModuleIteration module) {
|
public Changelog getChangelogFor(ModuleIteration module) {
|
||||||
|
|
||||||
List<Ticket> tickets = getIssuesFor(module).stream().//
|
List<Ticket> tickets = getIssuesFor(module).stream().//
|
||||||
map(issue -> new Ticket(issue.getId(), issue.getTitle())).//
|
map(issue -> toTicket(issue)).//
|
||||||
collect(Collectors.toList());
|
collect(Collectors.toList());
|
||||||
|
|
||||||
logger.log(module, "Created changelog with %s entries.", tickets.size());
|
logger.log(module, "Created changelog with %s entries.", tickets.size());
|
||||||
@@ -113,7 +169,7 @@ class GitHubIssueTracker implements IssueTracker {
|
|||||||
parameters.put("id", milestone.getNumber());
|
parameters.put("id", milestone.getNumber());
|
||||||
|
|
||||||
return operations
|
return operations
|
||||||
.exchange(URI_TEMPLATE, HttpMethod.GET, new HttpEntity<>(getAuthenticationHeaders()), ISSUES_TYPE, parameters)
|
.exchange(ISSUES_BY_MILESTONE_URI_TEMPLATE, HttpMethod.GET, new HttpEntity<>(getAuthenticationHeaders()), ISSUES_TYPE, parameters)
|
||||||
.getBody();
|
.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import org.springframework.data.release.model.Iteration;
|
||||||
|
import org.springframework.data.release.model.Train;
|
||||||
|
import org.springframework.data.release.model.TrainIteration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public interface GitHubConnector extends IssueTracker {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object for a GitHub ticket status.
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
class GithubTicketStatus implements TicketStatus {
|
||||||
|
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResolved() {
|
||||||
|
return "closed".equalsIgnoreCase(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.release.jira;
|
package org.springframework.data.release.jira;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.springframework.data.release.model.ModuleIteration;
|
import org.springframework.data.release.model.ModuleIteration;
|
||||||
import org.springframework.data.release.model.Project;
|
import org.springframework.data.release.model.Project;
|
||||||
import org.springframework.plugin.core.Plugin;
|
import org.springframework.plugin.core.Plugin;
|
||||||
@@ -24,6 +26,11 @@ import org.springframework.plugin.core.Plugin;
|
|||||||
*/
|
*/
|
||||||
public interface IssueTracker extends Plugin<Project> {
|
public interface IssueTracker extends Plugin<Project> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset internal state (cache, ...).
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link Ticket} that tracks modifications in the context of a release.
|
* Returns the {@link Ticket} that tracks modifications in the context of a release.
|
||||||
*
|
*
|
||||||
@@ -32,5 +39,16 @@ public interface IssueTracker extends Plugin<Project> {
|
|||||||
*/
|
*/
|
||||||
Ticket getReleaseTicketFor(ModuleIteration module);
|
Ticket getReleaseTicketFor(ModuleIteration module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the issue tracker for multiple {@link Ticket#id ticket Ids}. Tickets that are not found are not returned
|
||||||
|
* within the result.
|
||||||
|
*
|
||||||
|
* @param project
|
||||||
|
* @param ticketIds collection of {@link Ticket#id ticket Ids}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Collection<Ticket> findTickets(Project project, Collection<String> ticketIds);
|
||||||
|
|
||||||
Changelog getChangelogFor(ModuleIteration iteration);
|
Changelog getChangelogFor(ModuleIteration iteration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.release.jira;
|
package org.springframework.data.release.jira;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
@@ -71,7 +73,7 @@ class Jira implements JiraConnector {
|
|||||||
|
|
||||||
Map<String, Object> parameters = new HashMap<>();
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
parameters.put("jql", query);
|
parameters.put("jql", query);
|
||||||
parameters.put("fields", "summary");
|
parameters.put("fields", "summary,status,resolution");
|
||||||
parameters.put("startAt", 0);
|
parameters.put("startAt", 0);
|
||||||
|
|
||||||
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
|
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
|
||||||
@@ -83,7 +85,34 @@ class Jira implements JiraConnector {
|
|||||||
|
|
||||||
JiraIssue issue = issues.getIssues().get(0);
|
JiraIssue issue = issues.getIssues().get(0);
|
||||||
|
|
||||||
return new Ticket(issue.getKey(), issue.getFields().getSummary());
|
return toTicket(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see org.springframework.data.release.jira.IssueTracker#findTickets(Project, Collection)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Cacheable("tickets")
|
||||||
|
public Collection<Ticket> findTickets(Project project, Collection<String> ticketIds) {
|
||||||
|
|
||||||
|
if (ticketIds.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
JqlQuery query = JqlQuery.from(ticketIds).and(" resolution is not EMPTY");
|
||||||
|
|
||||||
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
|
parameters.put("jql", query);
|
||||||
|
parameters.put("fields", "summary,status,resolution");
|
||||||
|
parameters.put("startAt", 0);
|
||||||
|
|
||||||
|
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
|
||||||
|
.getBody();
|
||||||
|
|
||||||
|
return issues.getIssues().stream().//
|
||||||
|
map(this::toTicket).//
|
||||||
|
collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -118,7 +147,7 @@ class Jira implements JiraConnector {
|
|||||||
|
|
||||||
Map<String, Object> parameters = new HashMap<>();
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
parameters.put("jql", query);
|
parameters.put("jql", query);
|
||||||
parameters.put("fields", "summary,fixVersions");
|
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||||
parameters.put("startAt", startAt);
|
parameters.put("startAt", startAt);
|
||||||
|
|
||||||
issues = operations
|
issues = operations
|
||||||
@@ -128,7 +157,7 @@ class Jira implements JiraConnector {
|
|||||||
|
|
||||||
for (JiraIssue issue : issues) {
|
for (JiraIssue issue : issues) {
|
||||||
if (!issue.wasBackportedFrom(iteration.getTrain())) {
|
if (!issue.wasBackportedFrom(iteration.getTrain())) {
|
||||||
tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary()));
|
tickets.add(toTicket(issue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +168,24 @@ class Jira implements JiraConnector {
|
|||||||
return new Tickets(Collections.unmodifiableList(tickets), issues.getTotal());
|
return new Tickets(Collections.unmodifiableList(tickets), issues.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Ticket toTicket(JiraIssue issue) {
|
||||||
|
|
||||||
|
JiraIssue.Fields fields = issue.getFields();
|
||||||
|
|
||||||
|
JiraTicketStatus jiraTicketStatus;
|
||||||
|
if (fields.getStatus() != null && fields.getResolution() != null) {
|
||||||
|
JiraIssue.Status status = fields.getStatus();
|
||||||
|
boolean resolved = status.getStatusCategory().getKey().equals("done");
|
||||||
|
JiraIssue.Resolution resolution = fields.getResolution();
|
||||||
|
|
||||||
|
jiraTicketStatus = new JiraTicketStatus(resolved, status.getName(), resolution.getName());
|
||||||
|
} else {
|
||||||
|
jiraTicketStatus = JiraTicketStatus.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Ticket(issue.getKey(), fields.getSummary(), jiraTicketStatus);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
|
* @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
|
||||||
@@ -180,7 +227,7 @@ class Jira implements JiraConnector {
|
|||||||
|
|
||||||
Map<String, Object> parameters = new HashMap<>();
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
parameters.put("jql", JqlQuery.from(module));
|
parameters.put("jql", JqlQuery.from(module));
|
||||||
parameters.put("fields", "summary,fixVersions");
|
parameters.put("fields", "summary,status,resolution,fixVersions");
|
||||||
parameters.put("startAt", 0);
|
parameters.put("startAt", 0);
|
||||||
|
|
||||||
URI searchUri = new UriTemplate(SEARCH_TEMPLATE).expand(parameters);
|
URI searchUri = new UriTemplate(SEARCH_TEMPLATE).expand(parameters);
|
||||||
@@ -191,7 +238,7 @@ class Jira implements JiraConnector {
|
|||||||
List<Ticket> tickets = new ArrayList<>();
|
List<Ticket> tickets = new ArrayList<>();
|
||||||
|
|
||||||
for (JiraIssue issue : issues) {
|
for (JiraIssue issue : issues) {
|
||||||
tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary()));
|
tickets.add(toTicket(issue));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(module, "Created changelog with %s entries.", tickets.size());
|
logger.log(module, "Created changelog with %s entries.", tickets.size());
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ public interface JiraConnector extends IssueTracker {
|
|||||||
/**
|
/**
|
||||||
* Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}.
|
* Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}.
|
||||||
*
|
*
|
||||||
* @param train must not be {@literal null}.
|
|
||||||
* @param iteration must not be {@literal null}.
|
* @param iteration must not be {@literal null}.
|
||||||
|
* @param credentials may be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Tickets getTicketsFor(TrainIteration iteration, Credentials credentials);
|
Tickets getTicketsFor(TrainIteration iteration, Credentials credentials);
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class JiraIssue {
|
|||||||
|
|
||||||
String summary;
|
String summary;
|
||||||
List<FixVersions> fixVersions;
|
List<FixVersions> fixVersions;
|
||||||
|
Status status;
|
||||||
|
Resolution resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -61,4 +63,29 @@ class JiraIssue {
|
|||||||
|
|
||||||
String name;
|
String name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class Status {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
StatusCategory statusCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class Resolution {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
static class StatusCategory {
|
||||||
|
|
||||||
|
String key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class JiraTicketStatus implements TicketStatus {
|
||||||
|
|
||||||
|
public static final JiraTicketStatus UNKNOWN = new JiraTicketStatus(false, "unknown", null);
|
||||||
|
|
||||||
|
private final boolean resolved;
|
||||||
|
private final String status;
|
||||||
|
private final String resolution;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel() {
|
||||||
|
return resolution == null ? status : status + "/" + resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResolved() {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,10 @@ import static org.springframework.data.release.model.Projects.*;
|
|||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.data.release.model.ModuleIteration;
|
import org.springframework.data.release.model.ModuleIteration;
|
||||||
import org.springframework.data.release.model.TrainIteration;
|
import org.springframework.data.release.model.TrainIteration;
|
||||||
@@ -33,6 +36,7 @@ import org.springframework.util.StringUtils;
|
|||||||
class JqlQuery {
|
class JqlQuery {
|
||||||
|
|
||||||
private static final String PROJECT_VERSION_TEMPLATE = "project = %s AND fixVersion = \"%s\"";
|
private static final String PROJECT_VERSION_TEMPLATE = "project = %s AND fixVersion = \"%s\"";
|
||||||
|
private static final String ISSUE_KEY_IN_TEMPLATE = "issueKey in (%s)";
|
||||||
|
|
||||||
private final String query;
|
private final String query;
|
||||||
|
|
||||||
@@ -51,6 +55,13 @@ class JqlQuery {
|
|||||||
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
|
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JqlQuery from(Collection<String> ticketIds) {
|
||||||
|
|
||||||
|
String joinedTicketIds = ticketIds.stream().collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
return new JqlQuery(String.format(ISSUE_KEY_IN_TEMPLATE, joinedTicketIds));
|
||||||
|
}
|
||||||
|
|
||||||
public static JqlQuery from(TrainIteration iteration) {
|
public static JqlQuery from(TrainIteration iteration) {
|
||||||
|
|
||||||
List<String> parts = new ArrayList<>();
|
List<String> parts = new ArrayList<>();
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class Ticket {
|
|||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final String summary;
|
private final String summary;
|
||||||
|
private final TicketStatus ticketStatus;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import org.springframework.data.release.git.Branch;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object to represent a collection of {@link Branch}es with optionally assigned tickets.
|
||||||
|
*
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class TicketBranches implements Iterable<Branch> {
|
||||||
|
|
||||||
|
private Map<Branch, Ticket> ticketBranches;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link TicketBranches} instance for the given {@link Map} of {@link Branch}es and {@link Ticket}s.
|
||||||
|
*
|
||||||
|
* @param ticketBranches must not be {@literal null}.
|
||||||
|
*/
|
||||||
|
TicketBranches(Map<Branch, Ticket> ticketBranches) {
|
||||||
|
|
||||||
|
Assert.notNull(ticketBranches, "TicketBranches must not be null!");
|
||||||
|
this.ticketBranches = ticketBranches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TicketBranches from(Map<Branch, Ticket> ticketBranches) {
|
||||||
|
|
||||||
|
return new TicketBranches(ticketBranches);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Branch> iterator() {
|
||||||
|
return ticketBranches.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Branch> stream() {
|
||||||
|
return StreamSupport.stream(spliterator(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Ticket> getTicket(Branch branch) {
|
||||||
|
Assert.notNull(branch, "Branch must not be null!");
|
||||||
|
return Optional.ofNullable(ticketBranches.get(branch));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public interface TicketStatus {
|
||||||
|
|
||||||
|
String getLabel();
|
||||||
|
|
||||||
|
boolean isResolved();
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ public class Project implements Comparable<Project> {
|
|||||||
private final @Getter ProjectKey key;
|
private final @Getter ProjectKey key;
|
||||||
private final @Getter String name;
|
private final @Getter String name;
|
||||||
private final @Getter List<Project> dependencies;
|
private final @Getter List<Project> dependencies;
|
||||||
private final Tracker tracker;
|
private final @Getter Tracker tracker;
|
||||||
private final @Getter ArtifactCoordinates additionalArtifacts;
|
private final @Getter ArtifactCoordinates additionalArtifacts;
|
||||||
|
|
||||||
Project(String key, String name, List<Project> dependencies) {
|
Project(String key, String name, List<Project> dependencies) {
|
||||||
|
|||||||
@@ -20,5 +20,18 @@ package org.springframework.data.release.model;
|
|||||||
*/
|
*/
|
||||||
public enum Tracker {
|
public enum Tracker {
|
||||||
|
|
||||||
JIRA, GITHUB;
|
JIRA("(([A-Z]{1,10})+-\\d+)"),
|
||||||
|
GITHUB("((#)?\\d+)");
|
||||||
|
|
||||||
|
private String ticketPattern;
|
||||||
|
|
||||||
|
Tracker(String ticketPattern) {
|
||||||
|
this.ticketPattern = ticketPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTicketPattern()
|
||||||
|
{
|
||||||
|
return ticketPattern;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,4 +54,9 @@ public class GitOperationsIntegrationTests extends AbstractIntegrationTests {
|
|||||||
public void obtainsVersionTagsForRepoThatAlsoHasOtherTags() {
|
public void obtainsVersionTagsForRepoThatAlsoHasOtherTags() {
|
||||||
gitOperations.getTags(MONGO_DB);
|
gitOperations.getTags(MONGO_DB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getResolvedBranches() {
|
||||||
|
gitOperations.listTicketBranches(REDIS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.release.AbstractIntegrationTests;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class GitHubIssueTrackerIntegrationTests extends AbstractIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired GitHubConnector gitHubIssueTracker;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getTickets() throws Exception {
|
||||||
|
|
||||||
|
Collection<Ticket> tickets = gitHubIssueTracker.findTickets(Projects.BUILD, Arrays.asList("1", "2", "-1"));
|
||||||
|
assertThat(tickets, hasSize(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.jira;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.release.AbstractIntegrationTests;
|
||||||
|
import org.springframework.data.release.model.Projects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class JiraIntegrationTests extends AbstractIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired JiraConnector jira;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void findResolvedTicketsByTicketIds() throws Exception {
|
||||||
|
|
||||||
|
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Arrays.asList("DATAREDIS-1", "DATAJPA-1"));
|
||||||
|
assertThat(tickets, hasSize(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ignoresUnknownTicketsByTicketId() throws Exception {
|
||||||
|
|
||||||
|
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Arrays.asList("XYZ-1", "UNKOWN-1"));
|
||||||
|
assertThat(tickets, hasSize(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyResultWithEmptyTicketIds() throws Exception {
|
||||||
|
|
||||||
|
Collection<Ticket> tickets = jira.findTickets(Projects.COMMONS, Arrays.asList());
|
||||||
|
assertThat(tickets, hasSize(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 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.model;
|
||||||
|
|
||||||
|
import static org.hamcrest.core.Is.is;
|
||||||
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mark Paluch
|
||||||
|
*/
|
||||||
|
public class TrackerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatchesTicketNumber() throws Exception {
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(Tracker.JIRA.getTicketPattern());
|
||||||
|
Matcher matcher = pattern.matcher("DATAREDIS-1");
|
||||||
|
matcher.find();
|
||||||
|
|
||||||
|
assertThat(matcher.group(1), is(equalTo("DATAREDIS-1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatchesBranchNamedLikeTicket() throws Exception {
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(Tracker.JIRA.getTicketPattern());
|
||||||
|
Matcher matcher = pattern.matcher("issues/DATAREDIS-1-dummy");
|
||||||
|
matcher.find();
|
||||||
|
|
||||||
|
assertThat(matcher.group(1), is(equalTo("DATAREDIS-1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVersionBranch() throws Exception {
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile(Tracker.GITHUB.getTicketPattern());
|
||||||
|
Matcher matcher = pattern.matcher("1.2.x");
|
||||||
|
matcher.find();
|
||||||
|
|
||||||
|
assertThat(matcher.group(1), is(equalTo("1")));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user