#5 - Polishing.
Refactored issue tracker implementation into dedicated packages. Removed package tangles by moving some types around. Extracted GitHub specific properties into its own properties type reusing the Git credentials as we're talking to GitHub anyway. Added Jackson parameter name module and enabled Java 8 parameter names to be able to get rid of the manually declared constructors for payload classes. Turned them into value objects where possible. Original pull request: #14.
This commit is contained in:
@@ -39,6 +39,11 @@
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-parameter-names</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
@@ -137,6 +142,16 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<compilerArgument>-parameters</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.CliComponent;
|
||||
import org.springframework.data.release.io.Workspace;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@CliComponent
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
|
||||
public class BuildCommands {
|
||||
|
||||
private final @NonNull BuildOperations build;
|
||||
private final @NonNull Workspace workspace;
|
||||
private final @NonNull Logger logger;
|
||||
|
||||
/**
|
||||
* Removes all Spring Data artifacts from the local repository.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@CliCommand("workspace purge artifacts")
|
||||
public void purge() throws IOException {
|
||||
|
||||
logger.log("Workspace", "Cleaning up workspace directory at %s.",
|
||||
workspace.getWorkingDirectory().getAbsolutePath());
|
||||
|
||||
workspace.purge(build.getLocalRepository(),
|
||||
path -> build.getLocalRepository().relativize(path).startsWith("org/springframework/data"));
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.plugin.core.PluginRegistry.Supplier;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -56,13 +55,13 @@ public class BuildOperations {
|
||||
Assert.notNull(iteration, "Train iteration must not be null!");
|
||||
Assert.notNull(phase, "Phase must not be null!");
|
||||
|
||||
UpdateInformation updateInformation = new UpdateInformation(iteration, phase);
|
||||
UpdateInformation updateInformation = UpdateInformation.of(iteration, phase);
|
||||
|
||||
doWithBuildSystem(iteration, (system, it) -> system.updateProjectDescriptors(it, updateInformation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the distribution builds for all modules particitpating in the given {@link TrainIteration}.
|
||||
* Triggers the distribution builds for all modules participating in the given {@link TrainIteration}.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.springframework.data.release.deployment.DeploymentInformation;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
@@ -30,16 +29,35 @@ import org.springframework.plugin.core.Plugin;
|
||||
interface BuildSystem extends Plugin<Project> {
|
||||
|
||||
/**
|
||||
* @param iteration
|
||||
* @param train
|
||||
* @param phase
|
||||
* @throws Exception
|
||||
* Updates the project descriptors for the given {@link ModuleIteration} using the given {@link UpdateInformation}.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
* @param updateInformation must not be {@literal null}.
|
||||
*/
|
||||
ModuleIteration updateProjectDescriptors(ModuleIteration iteration, UpdateInformation updateInformation);
|
||||
|
||||
/**
|
||||
* Prepares the project descriptor of the {@link ModuleIteration} for the given release {@link Phase}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @param phase must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
ModuleIteration prepareVersion(ModuleIteration module, Phase phase);
|
||||
|
||||
ModuleIteration triggerDistributionBuild(ModuleIteration module);
|
||||
|
||||
/**
|
||||
* Deploy artifacts for the given {@link ModuleIteration} and return the {@link DeploymentInformation}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
DeploymentInformation deploy(ModuleIteration module);
|
||||
|
||||
/**
|
||||
* Runs the distribution build.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
ModuleIteration triggerDistributionBuild(ModuleIteration module);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -61,7 +60,7 @@ class GradleBuildSystem implements BuildSystem {
|
||||
*/
|
||||
public void updateProject(TrainIteration iteration, final Phase phase) throws Exception {
|
||||
|
||||
UpdateInformation updateInformation = new UpdateInformation(iteration, phase);
|
||||
UpdateInformation updateInformation = UpdateInformation.of(iteration, phase);
|
||||
|
||||
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
|
||||
updateProjectDescriptors(module, updateInformation);
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.Train;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.springframework.data.release.utils.ExecutionUtils;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -218,7 +217,7 @@ class MavenBuildSystem implements BuildSystem {
|
||||
public ModuleIteration prepareVersion(ModuleIteration module, Phase phase) {
|
||||
|
||||
Project project = module.getProject();
|
||||
UpdateInformation information = new UpdateInformation(module.getTrainIteration(), phase);
|
||||
UpdateInformation information = UpdateInformation.of(module.getTrainIteration(), phase);
|
||||
|
||||
mvn.execute(project, "versions:set", "versions:commit",
|
||||
"-DnewVersion=".concat(information.getProjectVersionToSet(project).toString()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -23,7 +23,6 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
package org.springframework.data.release.model;
|
||||
/*
|
||||
* Copyright 2015-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.build;
|
||||
|
||||
import static org.springframework.data.release.model.Projects.*;
|
||||
|
||||
@@ -6,7 +21,10 @@ import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.data.release.build.Repository;
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -14,7 +32,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class UpdateInformation {
|
||||
|
||||
private final @NonNull @Getter TrainIteration iteration;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -20,7 +20,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.release.jira.Ticket;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -24,8 +24,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.issues.Ticket;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.Train;
|
||||
@@ -118,8 +117,9 @@ public class GitCommands implements CommandMarker {
|
||||
public void backportChangelogs(@CliOption(key = "", mandatory = true) TrainIteration iteration, //
|
||||
@CliOption(key = "target", mandatory = true) String trains) {
|
||||
|
||||
List<Train> targets = Stream.of(trains.split(",")).map(it -> ReleaseTrains.getTrainByName(it))
|
||||
.collect(Collectors.toList());
|
||||
List<Train> targets = Stream.of(trains.split(",")).//
|
||||
map(it -> ReleaseTrains.getTrainByName(it)).//
|
||||
collect(Collectors.toList());
|
||||
|
||||
git.backportChangelogs(iteration, targets);
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class GitCommands implements CommandMarker {
|
||||
filter(branch -> ticketBranches.hasTicketFor(branch, resolved)).//
|
||||
forEachOrdered(branch -> {
|
||||
|
||||
Optional<Ticket> ticket = ticketBranches.getTicket(branch);
|
||||
Optional<Ticket> ticket = ticketBranches.findTicket(branch);
|
||||
|
||||
table.addRow(branch.toString(), //
|
||||
ticket.map(t -> t.getTicketStatus().getLabel()).orElse(""), //
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.release.git;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -41,9 +43,8 @@ import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.TagOpt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.io.Workspace;
|
||||
import org.springframework.data.release.jira.IssueTracker;
|
||||
import org.springframework.data.release.jira.Ticket;
|
||||
import org.springframework.data.release.jira.TicketBranches;
|
||||
import org.springframework.data.release.issues.IssueTracker;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
@@ -62,13 +63,14 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
public class GitOperations {
|
||||
|
||||
private final GitServer server = new GitServer();
|
||||
private final Workspace workspace;
|
||||
private final Logger logger;
|
||||
private final PluginRegistry<IssueTracker, Project> issueTracker;
|
||||
private final GitProperties gitProperties;
|
||||
GitServer server = new GitServer();
|
||||
Workspace workspace;
|
||||
Logger logger;
|
||||
PluginRegistry<IssueTracker, Project> issueTracker;
|
||||
GitProperties gitProperties;
|
||||
|
||||
/**
|
||||
* Returns the {@link GitProject} for the given {@link Project}.
|
||||
@@ -297,8 +299,8 @@ public class GitOperations {
|
||||
|
||||
Collection<Ticket> tickets = tracker.findTickets(project, ticketIds.keySet());
|
||||
|
||||
return TicketBranches.from(tickets.stream()
|
||||
.collect(Collectors.toMap(ticket -> ticketIds.get(ticket.getId()), ticket -> ticket)));
|
||||
return TicketBranches
|
||||
.from(tickets.stream().collect(Collectors.toMap(ticket -> ticketIds.get(ticket.getId()), ticket -> ticket)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -659,12 +661,10 @@ public class GitOperations {
|
||||
}
|
||||
|
||||
private static interface GitCallback<T> {
|
||||
|
||||
T doWithGit(Git git) throws Exception;
|
||||
}
|
||||
|
||||
private static interface VoidGitCallback {
|
||||
|
||||
void doWithGit(Git git) throws Exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import javax.annotation.PostConstruct;
|
||||
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.data.release.utils.HttpBasicCredentials;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -43,8 +42,6 @@ public class GitProperties {
|
||||
private @Getter(AccessLevel.PRIVATE) String password;
|
||||
private String username, author, email;
|
||||
|
||||
@Value("${github.api.url}") private String githubApiBaseUrl;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
@@ -52,7 +49,6 @@ public class GitProperties {
|
||||
Assert.hasText(password, "No GitHub password (git.password) configured!");
|
||||
Assert.hasText(author, "No Git author (git.author) configured!");
|
||||
Assert.hasText(email, "No Git email (git.email) configured!");
|
||||
Assert.hasText(githubApiBaseUrl, "No GitHub API base url configured!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.git;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
@@ -22,7 +22,7 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.release.Streamable;
|
||||
import org.springframework.data.release.git.Branch;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -53,7 +53,7 @@ public class TicketBranches implements Streamable<Branch> {
|
||||
|
||||
Assert.notNull(branch, "Branch must not be null!");
|
||||
|
||||
return getTicket(branch).//
|
||||
return findTicket(branch).//
|
||||
map(ticket -> requireResolved ? ticket.getTicketStatus().isResolved() : true).//
|
||||
orElse(false);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class TicketBranches implements Streamable<Branch> {
|
||||
* @param branch must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public Optional<Ticket> getTicket(Branch branch) {
|
||||
public Optional<Ticket> findTicket(Branch branch) {
|
||||
|
||||
Assert.notNull(branch, "Branch must not be null!");
|
||||
return Optional.ofNullable(ticketBranches.get(branch));
|
||||
@@ -21,7 +21,6 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.CliComponent;
|
||||
import org.springframework.data.release.build.BuildOperations;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.shell.core.CommandMarker;
|
||||
import org.springframework.shell.core.annotation.CliCommand;
|
||||
@@ -34,7 +33,6 @@ import org.springframework.shell.core.annotation.CliCommand;
|
||||
public class WorkspaceCommands implements CommandMarker {
|
||||
|
||||
private final Workspace workspace;
|
||||
private final BuildOperations build;
|
||||
private final Logger logger;
|
||||
|
||||
@CliCommand("workspace cleanup")
|
||||
@@ -45,19 +43,4 @@ public class WorkspaceCommands implements CommandMarker {
|
||||
|
||||
workspace.cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all Spring Data artifacts from the local repository.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@CliCommand("workspace purge artifacts")
|
||||
public void purge() throws IOException {
|
||||
|
||||
logger.log("Workspace", "Cleaning up workspace directory at %s.",
|
||||
workspace.getWorkingDirectory().getAbsolutePath());
|
||||
|
||||
workspace.purge(build.getLocalRepository(),
|
||||
path -> build.getLocalRepository().relativize(path).startsWith("org/springframework/data"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,15 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.data.release.model.ArtifactVersion;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.format.datetime.DateFormatter;
|
||||
@@ -30,7 +29,7 @@ import org.springframework.shell.support.util.OsUtils;
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
@EqualsAndHashCode
|
||||
public class Changelog {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -25,13 +25,15 @@ import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
* Interface for issue tracker operations.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface IssueTracker extends Plugin<Project> {
|
||||
|
||||
/**
|
||||
* Reset internal state (cache, ...).
|
||||
* Reset internal state (cache, etc).
|
||||
*/
|
||||
void reset();
|
||||
|
||||
@@ -55,7 +57,7 @@ public interface IssueTracker extends Plugin<Project> {
|
||||
/**
|
||||
* Returns the {@link Ticket} that tracks modifications in the context of a release.
|
||||
*
|
||||
* @param module the module to lookup the {@link Ticket} for.
|
||||
* @param module the module to lookup the {@link Ticket} for, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Ticket getReleaseTicketFor(ModuleIteration module);
|
||||
@@ -64,8 +66,8 @@ public interface IssueTracker extends Plugin<Project> {
|
||||
* 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}
|
||||
* @param project must not be {@literal null}.
|
||||
* @param ticketIds collection of {@link Ticket#id ticket Ids}, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Collection<Ticket> findTickets(Project project, Collection<String> ticketIds);
|
||||
@@ -73,18 +75,40 @@ public interface IssueTracker extends Plugin<Project> {
|
||||
/**
|
||||
* Creates a release version if release version is missing.
|
||||
*
|
||||
* @param moduleIteration must not be {@literal null}.
|
||||
* @param credentials must not be {@literal null}.
|
||||
* @param module must not be {@literal null}.
|
||||
*/
|
||||
void createReleaseVersion(ModuleIteration moduleIteration);
|
||||
void createReleaseVersion(ModuleIteration module);
|
||||
|
||||
/**
|
||||
* Create release ticket if release ticket is missing.
|
||||
* <p>
|
||||
* TODO: Return created ticket
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
* @param module must not be {@literal null}.
|
||||
*/
|
||||
void createReleaseTicket(ModuleIteration module);
|
||||
|
||||
/**
|
||||
* Assigns the ticket to the current user.
|
||||
*
|
||||
* @param ticket must not be {@literal null}.
|
||||
* @param credentials must not be {@literal null}.
|
||||
*/
|
||||
void createReleaseTicket(ModuleIteration moduleIteration);
|
||||
void assignTicketToMe(Ticket ticket);
|
||||
|
||||
Changelog getChangelogFor(ModuleIteration iteration);
|
||||
/**
|
||||
* Assigns the release ticket for the given {@link ModuleIteration} to the current user.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Ticket assignReleaseTicketToMe(ModuleIteration module);
|
||||
|
||||
/**
|
||||
* Returns the {@link Changelog} for the given {@link ModuleIteration}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Changelog getChangelogFor(ModuleIteration module);
|
||||
}
|
||||
@@ -13,17 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.cli;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import static org.springframework.data.release.utils.ExecutionUtils.*;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.CliComponent;
|
||||
import org.springframework.data.release.jira.Changelog;
|
||||
import org.springframework.data.release.jira.IssueTracker;
|
||||
import org.springframework.data.release.jira.JiraConnector;
|
||||
import org.springframework.data.release.jira.Tickets;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
@@ -39,56 +44,38 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@CliComponent
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
|
||||
public class IssueTrackerCommands implements CommandMarker {
|
||||
|
||||
private final PluginRegistry<IssueTracker, Project> tracker;
|
||||
private final JiraConnector jira;
|
||||
|
||||
/**
|
||||
* @param tracker must not be {@literal null}.
|
||||
* @param jira must not be {@literal null}.
|
||||
* @param environment must not be {@literal null}.
|
||||
*/
|
||||
@Autowired
|
||||
public IssueTrackerCommands(PluginRegistry<IssueTracker, Project> tracker, JiraConnector jira) {
|
||||
|
||||
this.tracker = tracker;
|
||||
this.jira = jira;
|
||||
}
|
||||
@NonNull PluginRegistry<IssueTracker, Project> tracker;
|
||||
|
||||
@CliCommand("tracker evict")
|
||||
public void evict() {
|
||||
StreamSupport.stream(tracker.spliterator(), false).forEach(IssueTracker::reset);
|
||||
}
|
||||
|
||||
@CliCommand(value = "jira tickets")
|
||||
@CliCommand(value = "tracker tickets")
|
||||
public String jira(@CliOption(key = "", mandatory = true) TrainIteration iteration, //
|
||||
@CliOption(key = "for-current-user", specifiedDefaultValue = "true",
|
||||
unspecifiedDefaultValue = "false") boolean forCurrentUser) {
|
||||
|
||||
Tickets tickets = StreamSupport.stream(tracker.spliterator(), false). //
|
||||
flatMap(issueTracker -> issueTracker. //
|
||||
getTicketsFor(iteration, forCurrentUser).stream())
|
||||
. //
|
||||
collect(Tickets.toTicketsCollector());
|
||||
return tickets.toString();
|
||||
return tracker.getPlugins().stream().//
|
||||
flatMap(it -> it.getTicketsFor(iteration, forCurrentUser).stream()).//
|
||||
collect(Tickets.toTicketsCollector()).toString();
|
||||
}
|
||||
|
||||
@CliCommand(value = "tracker releasetickets")
|
||||
public String releaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) {
|
||||
|
||||
Tickets tickets = StreamSupport.stream(tracker.spliterator(), false). //
|
||||
flatMap(issueTracker -> issueTracker.getTicketsFor(iteration).stream()). //
|
||||
collect(Tickets.toTicketsCollector());
|
||||
return tickets.getReleaseTickets(iteration).toString();
|
||||
return runAndReturn(iteration, module -> getTrackerFor(module).getReleaseTicketFor(module),
|
||||
Tickets.toTicketsCollector()).toString();
|
||||
}
|
||||
|
||||
@CliCommand(value = "jira self-assign releasetickets")
|
||||
@CliCommand(value = "tracker self-assign releasetickets")
|
||||
public String jiraSelfAssignReleaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) {
|
||||
|
||||
Tickets releaseTickets = jira.getTicketsFor(iteration).getReleaseTickets(iteration);
|
||||
releaseTickets.forEach(ticket -> jira.assignTicketToMe(ticket));
|
||||
return releaseTickets(iteration);
|
||||
return runAndReturn(iteration, module -> getTrackerFor(module).assignReleaseTicketToMe(module),
|
||||
Tickets.toTicketsCollector()).toString();
|
||||
}
|
||||
|
||||
@CliCommand(value = "tracker create releaseversions")
|
||||
@@ -99,21 +86,14 @@ public class IssueTrackerCommands implements CommandMarker {
|
||||
@CliCommand(value = "tracker create releasetickets")
|
||||
public String createReleaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) {
|
||||
|
||||
iteration.forEach(this::createReleaseVersion);
|
||||
iteration.stream().//
|
||||
forEach(module -> getTrackerFor(module).createReleaseTicket(module));
|
||||
|
||||
evict();
|
||||
|
||||
return releaseTickets(iteration);
|
||||
}
|
||||
|
||||
private void createReleaseVersion(ModuleIteration moduleIteration) {
|
||||
getPluginFor(moduleIteration).createReleaseVersion(moduleIteration);
|
||||
}
|
||||
|
||||
private IssueTracker getPluginFor(ModuleIteration moduleIteration) {
|
||||
return tracker.getPluginFor(moduleIteration.getProject());
|
||||
}
|
||||
|
||||
@CliCommand("tracker changelog")
|
||||
public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration, //
|
||||
@CliOption(key = "module") String moduleName) {
|
||||
@@ -121,7 +101,7 @@ public class IssueTrackerCommands implements CommandMarker {
|
||||
if (StringUtils.hasText(moduleName)) {
|
||||
|
||||
ModuleIteration module = iteration.getModule(moduleName);
|
||||
return tracker.getPluginFor(module.getProject()).getChangelogFor(module).toString();
|
||||
return getTrackerFor(module).getChangelogFor(module).toString();
|
||||
}
|
||||
|
||||
return ExecutionUtils.runAndReturn(iteration, this::getChangelog).//
|
||||
@@ -129,6 +109,18 @@ public class IssueTrackerCommands implements CommandMarker {
|
||||
}
|
||||
|
||||
private Changelog getChangelog(ModuleIteration module) {
|
||||
return tracker.getPluginFor(module.getProject()).getChangelogFor(module);
|
||||
return getTrackerFor(module).getChangelogFor(module);
|
||||
}
|
||||
|
||||
private void createReleaseVersion(ModuleIteration moduleIteration) {
|
||||
getTrackerFor(moduleIteration).createReleaseVersion(moduleIteration);
|
||||
}
|
||||
|
||||
private <T> Stream<T> doWithBuildSystem(TrainIteration train, BiFunction<IssueTracker, ModuleIteration, T> function) {
|
||||
return train.stream().map(module -> function.apply(getTrackerFor(module), module));
|
||||
}
|
||||
|
||||
private IssueTracker getTrackerFor(ModuleIteration moduleIteration) {
|
||||
return tracker.getPluginFor(moduleIteration.getProject());
|
||||
}
|
||||
}
|
||||
@@ -13,60 +13,63 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.release.git.GitProperties;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.plugin.core.OrderAwarePluginRegistry;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
|
||||
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
@EnableCaching(proxyTargetClass = true)
|
||||
class IssueTrackerConfiguration {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
CacheManager cacheManager() {
|
||||
return new ConcurrentMapCacheManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PluginRegistry<IssueTracker, Project> issueTrackers(List<? extends IssueTracker> plugins) {
|
||||
return OrderAwarePluginRegistry.create(plugins);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper jacksonObjectMapper() {
|
||||
ObjectMapper jacksonObjectMapper() {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
mapper.setSerializationInclusion(Include.NON_NULL);
|
||||
mapper.registerModule(new ParameterNamesModule(Mode.PROPERTIES));
|
||||
mapper.registerModule(new SyntheticLambdaFactoryMethodIgnoringModule());
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
@Qualifier("tracker")
|
||||
RestTemplate restTemplate() {
|
||||
|
||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
||||
converter.setObjectMapper(jacksonObjectMapper());
|
||||
@@ -81,12 +84,36 @@ class IssueTrackerConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Jira jira(Logger logger, JiraProperties jiraProperties) {
|
||||
return new Jira(restTemplate(), logger, jiraProperties);
|
||||
PluginRegistry<IssueTracker, Project> issueTrackers(List<? extends IssueTracker> plugins) {
|
||||
return OrderAwarePluginRegistry.create(plugins);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GitHubIssueTracker github(Logger logger, GitProperties properties) {
|
||||
return new GitHubIssueTracker(restTemplate(), logger, properties);
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class SyntheticLambdaFactoryMethodIgnoringModule extends SimpleModule {
|
||||
|
||||
private static final long serialVersionUID = -3075786389813846512L;
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
|
||||
context.insertAnnotationIntrospector(new NopAnnotationIntrospector() {
|
||||
|
||||
private static final long serialVersionUID = 479313244908256455L;
|
||||
|
||||
@Override
|
||||
public boolean hasIgnoreMarker(AnnotatedMember m) {
|
||||
|
||||
if (!(m instanceof AnnotatedMethod)) {
|
||||
return super.hasIgnoreMarker(m);
|
||||
}
|
||||
|
||||
AnnotatedMethod method = (AnnotatedMethod) m;
|
||||
|
||||
return method.getName().startsWith("lambda$") ? true : super.hasIgnoreMarker(m);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,13 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Tracker;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
|
||||
import lombok.Value;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value object to represent a {@link Ticket}.
|
||||
@@ -29,9 +30,8 @@ import lombok.Value;
|
||||
@Value
|
||||
public class Ticket {
|
||||
|
||||
private final String id;
|
||||
private final String summary;
|
||||
private final TicketStatus ticketStatus;
|
||||
String id, summary;
|
||||
TicketStatus ticketStatus;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -39,18 +39,32 @@ public class Ticket {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%13s - %s", id, summary);
|
||||
return String.format("%14s - %s", id, summary);
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return ticketStatus.isResolved();
|
||||
}
|
||||
|
||||
public boolean isReleaseTicketFor(ModuleIteration moduleIteration) {
|
||||
return summary.equals(Tracker.releaseTicketSummary(moduleIteration));
|
||||
/**
|
||||
* Returns whether the current {@link Ticket} is the release ticket for the given {@link ModuleIteration}.
|
||||
*
|
||||
* @param module must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseTicketFor(ModuleIteration module) {
|
||||
|
||||
Assert.notNull(module, "Module must not be null!");
|
||||
return summary.equals(Tracker.releaseTicketSummary(module));
|
||||
}
|
||||
|
||||
public boolean isReleaseTicketFor(TrainIteration iteration) {
|
||||
return iteration.stream().filter(this::isReleaseTicketFor).findFirst().isPresent();
|
||||
/**
|
||||
* Returns whether the current {@link Ticket} is a release ticket for the given {@link TrainIteration}.
|
||||
*
|
||||
* @param train must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseTicketFor(TrainIteration train) {
|
||||
return train.stream().anyMatch(this::isReleaseTicketFor);
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface TicketStatus {
|
||||
|
||||
/**
|
||||
* Returns
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is marked as resolved.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isResolved();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,7 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -33,9 +36,6 @@ import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Value object to represent a list of {@link Ticket}s.
|
||||
*
|
||||
@@ -46,8 +46,8 @@ import lombok.Value;
|
||||
@RequiredArgsConstructor
|
||||
public class Tickets implements Streamable<Ticket> {
|
||||
|
||||
private final List<Ticket> tickets;
|
||||
private final int overallTotal;
|
||||
List<Ticket> tickets;
|
||||
int overallTotal;
|
||||
|
||||
public Tickets(List<Ticket> tickets) {
|
||||
this(Collections.unmodifiableList(tickets), tickets.size());
|
||||
@@ -68,9 +68,7 @@ public class Tickets implements Streamable<Ticket> {
|
||||
|
||||
public Ticket getReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
Optional<Ticket> releaseTicket = findReleaseTicket(moduleIteration);
|
||||
|
||||
return releaseTicket.orElseThrow(
|
||||
return findReleaseTicket(moduleIteration).orElseThrow(
|
||||
() -> new IllegalStateException(String.format("Did not find a release ticket for %s!", moduleIteration)));
|
||||
}
|
||||
|
||||
@@ -84,19 +82,26 @@ public class Tickets implements Streamable<Ticket> {
|
||||
|
||||
return stream().//
|
||||
filter(ticket -> ticket.isReleaseTicketFor(iteration)).//
|
||||
distinct().//
|
||||
collect(toTicketsCollector());
|
||||
}
|
||||
|
||||
private Optional<Ticket> findReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
return stream().//
|
||||
filter(ticket -> ticket.isReleaseTicketFor(moduleIteration)).//
|
||||
findFirst();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append(String.format("Train only tickets: %s of %s", tickets.size(), overallTotal));
|
||||
builder.append("\n");
|
||||
builder.append(StringUtils.collectionToDelimitedString(tickets, "\n"));
|
||||
@@ -112,16 +117,29 @@ public class Tickets implements Streamable<Ticket> {
|
||||
public static Collector<? super Ticket, ?, Tickets> toTicketsCollector() {
|
||||
|
||||
return new Collector<Ticket, List<Ticket>, Tickets>() {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#supplier()
|
||||
*/
|
||||
@Override
|
||||
public Supplier<List<Ticket>> supplier() {
|
||||
return ArrayList::new;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#accumulator()
|
||||
*/
|
||||
@Override
|
||||
public BiConsumer<List<Ticket>, Ticket> accumulator() {
|
||||
return List::add;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#combiner()
|
||||
*/
|
||||
@Override
|
||||
public BinaryOperator<List<Ticket>> combiner() {
|
||||
return (left, right) -> {
|
||||
@@ -130,16 +148,23 @@ public class Tickets implements Streamable<Ticket> {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#finisher()
|
||||
*/
|
||||
@Override
|
||||
public Function<List<Ticket>, Tickets> finisher() {
|
||||
return tickets -> new Tickets(tickets);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#characteristics()
|
||||
*/
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.github;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -23,19 +23,21 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.data.release.git.GitProject;
|
||||
import org.springframework.data.release.git.GitProperties;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.issues.Changelog;
|
||||
import org.springframework.data.release.issues.IssueTracker;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.issues.github.GitHubIssue.Milestone;
|
||||
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.Tracker;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
@@ -43,19 +45,18 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class GitHubIssueTracker implements IssueTracker {
|
||||
@Component
|
||||
class GitHub implements IssueTracker {
|
||||
|
||||
private static final String MILESTONE_URI = "{githubBaseUrl}/repos/spring-projects/{repoName}/milestones?state={state}";
|
||||
private static final String ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE = "{githubBaseUrl}/repos/spring-projects/{repoName}/issues?milestone={id}&state=all&assignee={assignee}";
|
||||
@@ -64,13 +65,26 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
private static final String ISSUE_BY_ID_URI_TEMPLATE = "{githubBaseUrl}/repos/spring-projects/{repoName}/issues/{id}";
|
||||
private static final String ISSUES_URI_TEMPLATE = "{githubBaseUrl}/repos/spring-projects/{repoName}/issues";
|
||||
|
||||
private static final ParameterizedTypeReference<List<GitHubIssue.Milestone>> MILESTONES_TYPE = new ParameterizedTypeReference<List<GitHubIssue.Milestone>>() {};
|
||||
private static final ParameterizedTypeReference<List<Milestone>> MILESTONES_TYPE = new ParameterizedTypeReference<List<Milestone>>() {};
|
||||
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 Logger logger;
|
||||
private final GitProperties properties;
|
||||
private final GitHubProperties properties;
|
||||
|
||||
/**
|
||||
* @param operations
|
||||
* @param logger
|
||||
* @param properties
|
||||
*/
|
||||
@Autowired
|
||||
public GitHub(@Qualifier("tracker") RestOperations operations, Logger logger, GitHubProperties properties) {
|
||||
|
||||
this.operations = operations;
|
||||
this.logger = logger;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -103,24 +117,29 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
|
||||
String repositoryName = GitProject.of(project).getRepositoryName();
|
||||
List<Ticket> tickets = new ArrayList<>();
|
||||
for (String ticketId : ticketIds) {
|
||||
|
||||
ticketIds.forEach(ticketId -> {
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("repoName", repositoryName);
|
||||
parameters.put("id", ticketId);
|
||||
|
||||
try {
|
||||
|
||||
GitHubIssue gitHubIssue = operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.GET,
|
||||
new HttpEntity<>(newUserScopedHttpHeaders()), ISSUE_TYPE, parameters).getBody();
|
||||
|
||||
tickets.add(toTicket(gitHubIssue));
|
||||
|
||||
} catch (HttpStatusCodeException e) {
|
||||
|
||||
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tickets;
|
||||
}
|
||||
@@ -133,13 +152,13 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
@Cacheable("changelogs")
|
||||
public Changelog getChangelogFor(ModuleIteration moduleIteration) {
|
||||
|
||||
Tickets tickets = getIssuesFor(moduleIteration, false).stream().//
|
||||
Tickets tickets = getIssuesFor(moduleIteration, false).//
|
||||
map(issue -> toTicket(issue)).//
|
||||
collect(Tickets.toTicketsCollector());
|
||||
|
||||
logger.log(moduleIteration, "Created changelog with %s entries.", tickets.getOverallTotal());
|
||||
|
||||
return new Changelog(moduleIteration, tickets);
|
||||
return Changelog.of(moduleIteration, tickets);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -151,11 +170,6 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
return project.uses(Tracker.GITHUB);
|
||||
}
|
||||
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(ModuleIteration iteration) {
|
||||
return getTicketsFor(iteration, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tickets getTicketsFor(TrainIteration iteration) {
|
||||
return getTicketsFor(iteration, false);
|
||||
@@ -182,14 +196,17 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
return tickets;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.tracker.IssueTracker#createReleaseVersion(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void createReleaseVersion(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
|
||||
String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName();
|
||||
|
||||
Optional<GitHubIssue.Milestone> milestone = findMilestone(moduleIteration, repositoryName);
|
||||
Optional<Milestone> milestone = findMilestone(moduleIteration, repositoryName);
|
||||
|
||||
if (milestone.isPresent()) {
|
||||
return;
|
||||
@@ -206,13 +223,17 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
new HttpEntity<Object>(githubMilestone.toMilestone(), httpHeaders), GitHubIssue.Milestone.class, parameters);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.tracker.IssueTracker#createReleaseTicket(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void createReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
|
||||
HttpHeaders httpHeaders = newUserScopedHttpHeaders();
|
||||
|
||||
Tickets tickets = getTicketsFor(moduleIteration);
|
||||
|
||||
if (tickets.hasReleaseTicket(moduleIteration)) {
|
||||
@@ -220,59 +241,61 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
}
|
||||
|
||||
String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName();
|
||||
Milestone milestone = getMilestone(moduleIteration, repositoryName);
|
||||
GitHubIssue gitHubIssue = GitHubIssue.of(Tracker.releaseTicketSummary(moduleIteration), milestone);
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("repoName", repositoryName);
|
||||
|
||||
GitHubIssue.Milestone milestone = getMilestone(moduleIteration, repositoryName);
|
||||
|
||||
logger.log(moduleIteration, "Creating release ticket…");
|
||||
|
||||
GitHubIssue gitHubIssue = new GitHubIssue(Tracker.releaseTicketSummary(moduleIteration), milestone);
|
||||
|
||||
operations.exchange(ISSUES_URI_TEMPLATE, HttpMethod.POST, new HttpEntity<Object>(gitHubIssue, httpHeaders),
|
||||
GitHubIssue.class, parameters).getBody();
|
||||
|
||||
}
|
||||
|
||||
private Tickets getTicketsFor(ModuleIteration moduleIteration, boolean forCurrentUser) {
|
||||
|
||||
List<GitHubIssue> issues = getIssuesFor(moduleIteration, forCurrentUser);
|
||||
return issues.stream().map(this::toTicket).collect(Tickets.toTicketsCollector());
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(ModuleIteration iteration) {
|
||||
return getTicketsFor(iteration, false);
|
||||
}
|
||||
|
||||
private List<GitHubIssue> getIssuesFor(ModuleIteration moduleIteration, boolean forCurrentUser) {
|
||||
|
||||
String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName();
|
||||
|
||||
GitHubIssue.Milestone milestone = getMilestone(moduleIteration, repositoryName);
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("repoName", repositoryName);
|
||||
parameters.put("id", milestone.getNumber());
|
||||
|
||||
if (forCurrentUser) {
|
||||
parameters.put("assignee", properties.getUsername());
|
||||
|
||||
return operations.exchange(ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE, HttpMethod.GET,
|
||||
new HttpEntity<>(newUserScopedHttpHeaders()), ISSUES_TYPE, parameters).getBody();
|
||||
}
|
||||
|
||||
return operations.exchange(ISSUES_BY_MILESTONE_URI_TEMPLATE, HttpMethod.GET,
|
||||
new HttpEntity<>(newUserScopedHttpHeaders()), ISSUES_TYPE, parameters).getBody();
|
||||
/*
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#assignTicketToMe(org.springframework.data.release.jira.Ticket)
|
||||
*/
|
||||
@Override
|
||||
public void assignTicketToMe(Ticket ticket) {
|
||||
logger.log("Ticket", "Skipping ticket assignment for %s", ticket);
|
||||
}
|
||||
|
||||
private GitHubIssue.Milestone getMilestone(ModuleIteration moduleIteration, String repositoryName) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#assignReleaseTicketToMe(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public Ticket assignReleaseTicketToMe(ModuleIteration module) {
|
||||
|
||||
Optional<GitHubIssue.Milestone> milestone = findMilestone(moduleIteration, repositoryName);
|
||||
return milestone
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("No milestone for %s found containing %s!", //
|
||||
moduleIteration.getProject().getFullName(), //
|
||||
moduleIteration.getShortVersionString())));
|
||||
logger.log("Ticket", "Skipping ticket assignment for %s", module);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Cacheable("milestone")
|
||||
protected Optional<GitHubIssue.Milestone> findMilestone(ModuleIteration moduleIteration, String repositoryName) {
|
||||
private HttpHeaders newUserScopedHttpHeaders() {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", properties.getHttpCredentials().toString());
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
private Map<String, Object> newUrlTemplateVariables() {
|
||||
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("githubBaseUrl", properties.getApiUrl());
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private Optional<Milestone> findMilestone(ModuleIteration moduleIteration, String repositoryName) {
|
||||
|
||||
for (String state : Arrays.asList("close", "open")) {
|
||||
|
||||
@@ -288,7 +311,7 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
new HttpEntity<>(newUserScopedHttpHeaders()), MILESTONES_TYPE, parameters).getBody();
|
||||
|
||||
Optional<GitHubIssue.Milestone> milestone = milestones.stream(). //
|
||||
filter(m -> m.matchesIteration(moduleIteration)). //
|
||||
filter(m -> m.matches(moduleIteration)). //
|
||||
findFirst(). //
|
||||
map(m -> {
|
||||
logger.log(moduleIteration, "Found milestone %s.", m);
|
||||
@@ -303,38 +326,50 @@ class GitHubIssueTracker implements IssueTracker {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private HttpHeaders newUserScopedHttpHeaders() {
|
||||
private Tickets getTicketsFor(ModuleIteration moduleIteration, boolean forCurrentUser) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", properties.getHttpCredentials().toString());
|
||||
|
||||
return headers;
|
||||
return getIssuesFor(moduleIteration, forCurrentUser).//
|
||||
map(GitHub::toTicket).//
|
||||
collect(Tickets.toTicketsCollector());
|
||||
}
|
||||
|
||||
private Map<String, Object> newUrlTemplateVariables() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("githubBaseUrl", properties.getGithubApiBaseUrl());
|
||||
return parameters;
|
||||
private Stream<GitHubIssue> getIssuesFor(ModuleIteration moduleIteration, boolean forCurrentUser) {
|
||||
|
||||
String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName();
|
||||
|
||||
GitHubIssue.Milestone milestone = getMilestone(moduleIteration, repositoryName);
|
||||
|
||||
Map<String, Object> parameters = newUrlTemplateVariables();
|
||||
parameters.put("repoName", repositoryName);
|
||||
parameters.put("id", milestone.getNumber());
|
||||
|
||||
if (forCurrentUser) {
|
||||
parameters.put("assignee", properties.getUsername());
|
||||
|
||||
return getForIssues(ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE, parameters);
|
||||
}
|
||||
|
||||
return getForIssues(ISSUES_BY_MILESTONE_URI_TEMPLATE, parameters);
|
||||
}
|
||||
|
||||
private Ticket toTicket(GitHubIssue issue) {
|
||||
private Stream<GitHubIssue> getForIssues(String template, Map<String, Object> parameters) {
|
||||
|
||||
return operations
|
||||
.exchange(template, HttpMethod.GET, new HttpEntity<>(newUserScopedHttpHeaders()), ISSUES_TYPE, parameters)
|
||||
.getBody().stream();
|
||||
}
|
||||
|
||||
private Milestone getMilestone(ModuleIteration moduleIteration, String repositoryName) {
|
||||
|
||||
Optional<Milestone> milestone = findMilestone(moduleIteration, repositoryName);
|
||||
|
||||
return milestone
|
||||
.orElseThrow(() -> new IllegalStateException(String.format("No milestone for %s found containing %s!", //
|
||||
moduleIteration.getProject().getFullName(), //
|
||||
moduleIteration.getShortVersionString())));
|
||||
}
|
||||
|
||||
private static Ticket toTicket(GitHubIssue issue) {
|
||||
return new Ticket(issue.getId(), issue.getTitle(), new GithubTicketStatus(issue.getState()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"META-INF/spring/spring-shell-plugin.xml")) {
|
||||
|
||||
IssueTracker tracker = context.getBean("gitHubIssueTracker", IssueTracker.class);
|
||||
|
||||
TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2);
|
||||
ModuleIteration module = iteration.getModule(Projects.BUILD);
|
||||
|
||||
Changelog changelog = tracker.getChangelogFor(module);
|
||||
System.out.println(changelog);
|
||||
|
||||
System.out.println(tracker.getReleaseTicketFor(module));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014-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.issues.github;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
class GitHubIssue {
|
||||
|
||||
String number, title, state;
|
||||
Object milestone;
|
||||
|
||||
public static GitHubIssue of(String title, Milestone milestone) {
|
||||
return new GitHubIssue(null, title, null, milestone.number);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return number == null ? null : "#".concat(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
static class Milestone {
|
||||
|
||||
Long number;
|
||||
String title, description;
|
||||
|
||||
public static Milestone of(String title, String description) {
|
||||
return new Milestone(null, title, description);
|
||||
}
|
||||
|
||||
public boolean matches(ModuleIteration moduleIteration) {
|
||||
return title.contains(moduleIteration.getShortVersionString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,19 +13,28 @@
|
||||
* 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.ModuleIteration;
|
||||
package org.springframework.data.release.issues.github;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.issues.github.GitHubIssue.Milestone;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class GithubMilestone {
|
||||
|
||||
private ModuleIteration module;
|
||||
ModuleIteration module;
|
||||
|
||||
public String getDescription() {
|
||||
return module.getTrain().getName() + " " + module.getIteration().getName();
|
||||
}
|
||||
|
||||
public Milestone toMilestone() {
|
||||
return Milestone.of(toString(), getDescription());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -35,12 +44,4 @@ class GithubMilestone {
|
||||
public String toString() {
|
||||
return module.getMediumVersionString();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return module.getTrain().getName() + " " + module.getIteration().getName();
|
||||
}
|
||||
|
||||
public GitHubIssue.Milestone toMilestone() {
|
||||
return new GitHubIssue.Milestone(toString(), getDescription());
|
||||
}
|
||||
}
|
||||
@@ -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.issues.github;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.data.release.git.GitProperties;
|
||||
import org.springframework.data.release.utils.HttpBasicCredentials;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "github")
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
|
||||
public class GitHubProperties {
|
||||
|
||||
private final @Getter(AccessLevel.NONE) GitProperties gitProperties;
|
||||
|
||||
private String apiUrl;
|
||||
|
||||
public String getUsername() {
|
||||
return gitProperties.getUsername();
|
||||
}
|
||||
|
||||
public HttpBasicCredentials getHttpCredentials() {
|
||||
return gitProperties.getHttpCredentials();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Assert.hasText(apiUrl, "No GitHub API base url configured!");
|
||||
}
|
||||
}
|
||||
@@ -14,25 +14,42 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.github;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import org.springframework.data.release.issues.TicketStatus;
|
||||
|
||||
/**
|
||||
* Value object for a GitHub ticket status.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
|
||||
class GithubTicketStatus implements TicketStatus {
|
||||
|
||||
private final String status;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.TicketStatus#getLabel()
|
||||
*/
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.TicketStatus#isResolved()
|
||||
*/
|
||||
@Override
|
||||
public boolean isResolved() {
|
||||
return "closed".equalsIgnoreCase(status);
|
||||
@@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Value object for a created Jira issue.
|
||||
* Value object for a created JIRA issue.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Data
|
||||
public class CreatedJiraIssue {
|
||||
|
||||
private String id;
|
||||
private String key;
|
||||
|
||||
@Value
|
||||
class CreatedJiraIssue {
|
||||
String id, key;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,19 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Credentials {
|
||||
class Credentials {
|
||||
|
||||
private final String username, password;
|
||||
String username, password;
|
||||
|
||||
public String asBase64() {
|
||||
return DatatypeConverter.printBase64Binary(String.format("%s:%s", username, password).getBytes());
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -25,19 +25,21 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.data.release.Application;
|
||||
import org.springframework.data.release.jira.JiraIssue.Component;
|
||||
import org.springframework.data.release.jira.JiraIssue.Fields;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.issues.Changelog;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Component;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Fields;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Resolution;
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.Status;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.Tracker;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.utils.Logger;
|
||||
@@ -48,13 +50,11 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@org.springframework.stereotype.Component
|
||||
class Jira implements JiraConnector {
|
||||
|
||||
private static final String BASE_URI = "{jiraBaseUrl}/rest/api/2";
|
||||
@@ -69,9 +69,21 @@ class Jira implements JiraConnector {
|
||||
|
||||
private final RestOperations operations;
|
||||
private final Logger logger;
|
||||
|
||||
private final JiraProperties jiraProperties;
|
||||
|
||||
/**
|
||||
* @param operations
|
||||
* @param logger
|
||||
* @param jiraProperties
|
||||
*/
|
||||
@Autowired
|
||||
public Jira(@Qualifier("tracker") RestOperations operations, Logger logger, JiraProperties jiraProperties) {
|
||||
|
||||
this.operations = operations;
|
||||
this.logger = logger;
|
||||
this.jiraProperties = jiraProperties;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#reset()
|
||||
@@ -93,13 +105,11 @@ class Jira implements JiraConnector {
|
||||
|
||||
JiraIssues issues = getJiraIssues(query, new HttpHeaders(), 0);
|
||||
|
||||
if (issues.getIssues().isEmpty()) {
|
||||
if (!issues.hasIssues()) {
|
||||
throw new IllegalArgumentException(String.format("Did not find a release ticket for %s!", moduleIteration));
|
||||
}
|
||||
|
||||
JiraIssue issue = issues.getIssues().get(0);
|
||||
|
||||
return toTicket(issue);
|
||||
return toTicket(issues.stream().findFirst().get());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,19 +135,27 @@ class Jira implements JiraConnector {
|
||||
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
|
||||
.getBody();
|
||||
|
||||
return issues.getIssues().stream().//
|
||||
return issues.stream().//
|
||||
map(this::toTicket).//
|
||||
collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Cacheable("tickets")
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#getTicketsFor(org.springframework.data.release.model.TrainIteration)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(TrainIteration iteration) {
|
||||
return getTicketsFor(iteration, false);
|
||||
}
|
||||
|
||||
@Cacheable("tickets")
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#getTicketsFor(org.springframework.data.release.model.TrainIteration, boolean)
|
||||
*/
|
||||
@Override
|
||||
@Cacheable("tickets")
|
||||
public Tickets getTicketsFor(TrainIteration trainIteration, boolean forCurrentUser) {
|
||||
|
||||
JqlQuery query = JqlQuery
|
||||
@@ -208,7 +226,7 @@ class Jira implements JiraConnector {
|
||||
JiraVersion jiraVersion = new JiraVersion(moduleIteration);
|
||||
logger.log(moduleIteration, "Creating Jira release version %s", jiraVersion);
|
||||
|
||||
JiraReleaseVersion jiraReleaseVersion = new JiraReleaseVersion(moduleIteration, jiraVersion);
|
||||
JiraReleaseVersion jiraReleaseVersion = JiraReleaseVersion.of(moduleIteration, jiraVersion);
|
||||
|
||||
operations.exchange(VERSIONS_TEMPLATE, HttpMethod.POST, new HttpEntity<Object>(jiraReleaseVersion, httpHeaders),
|
||||
JiraReleaseVersion.class, parameters).getBody();
|
||||
@@ -235,6 +253,10 @@ class Jira implements JiraConnector {
|
||||
return versionsForModuleIteration.stream().findFirst();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#createReleaseTicket(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public void createReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
@@ -250,8 +272,7 @@ class Jira implements JiraConnector {
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<JiraReleaseVersion> jiraReleaseVersion = findJiraReleaseVersion(moduleIteration);
|
||||
jiraReleaseVersion.orElseThrow(
|
||||
findJiraReleaseVersion(moduleIteration).orElseThrow(
|
||||
() -> new IllegalStateException(String.format("Did not find a release version for %s", moduleIteration)));
|
||||
|
||||
JiraComponents jiraComponents = getJiraComponents(moduleIteration.getProjectKey());
|
||||
@@ -264,6 +285,10 @@ class Jira implements JiraConnector {
|
||||
CreatedJiraIssue.class, parameters).getBody();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.JiraConnector#assignTicketToMe(org.springframework.data.release.jira.Ticket)
|
||||
*/
|
||||
@Override
|
||||
public void assignTicketToMe(Ticket ticket) {
|
||||
|
||||
@@ -280,19 +305,16 @@ class Jira implements JiraConnector {
|
||||
String.class, parameters).getBody();
|
||||
}
|
||||
|
||||
private JiraIssue prepareJiraIssueToCreate(ModuleIteration moduleIteration, JiraComponents jiraComponents) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.release.jira.IssueTracker#assignReleaseTicketToMe(org.springframework.data.release.model.ModuleIteration)
|
||||
*/
|
||||
@Override
|
||||
public Ticket assignReleaseTicketToMe(ModuleIteration module) {
|
||||
|
||||
JiraIssue jiraIssue = JiraIssue.createTask();
|
||||
jiraIssue.project(moduleIteration.getProjectKey()).summary(Tracker.releaseTicketSummary(moduleIteration))
|
||||
.fixVersion(moduleIteration);
|
||||
|
||||
Fields fields = jiraIssue.getFields();
|
||||
|
||||
Optional<JiraComponent> component = jiraComponents.findComponent(INFRASTRUCTURE_COMPONENT_NAME);
|
||||
component.ifPresent(
|
||||
jiraComponent -> fields.setComponents(Collections.singletonList(Component.from(jiraComponent.getName()))));
|
||||
|
||||
return jiraIssue;
|
||||
Ticket ticket = getReleaseTicketFor(module);
|
||||
assignTicketToMe(ticket);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -304,8 +326,8 @@ class Jira implements JiraConnector {
|
||||
|
||||
// for each module
|
||||
for (ModuleIteration moduleIteration : trainIteration) {
|
||||
Tickets tickets = getTicketsFor(moduleIteration);
|
||||
|
||||
Tickets tickets = getTicketsFor(moduleIteration);
|
||||
Ticket releaseTicket = tickets.getReleaseTicket(moduleIteration);
|
||||
|
||||
if (releaseTicket.isResolved()) {
|
||||
@@ -366,7 +388,7 @@ class Jira implements JiraConnector {
|
||||
Tickets tickets = issues.stream().map(this::toTicket).collect(Tickets.toTicketsCollector());
|
||||
logger.log(moduleIteration, "Created changelog with %s entries.", tickets.getOverallTotal());
|
||||
|
||||
return new Changelog(moduleIteration, tickets);
|
||||
return Changelog.of(moduleIteration, tickets);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -391,6 +413,21 @@ class Jira implements JiraConnector {
|
||||
return JiraComponents.of(components);
|
||||
}
|
||||
|
||||
private JiraIssue prepareJiraIssueToCreate(ModuleIteration moduleIteration, JiraComponents jiraComponents) {
|
||||
|
||||
JiraIssue jiraIssue = JiraIssue.createTask();
|
||||
jiraIssue.project(moduleIteration.getProjectKey()).summary(Tracker.releaseTicketSummary(moduleIteration))
|
||||
.fixVersion(moduleIteration);
|
||||
|
||||
Fields fields = jiraIssue.getFields();
|
||||
|
||||
Optional<JiraComponent> component = jiraComponents.findComponent(INFRASTRUCTURE_COMPONENT_NAME);
|
||||
component.ifPresent(
|
||||
jiraComponent -> fields.setComponents(Collections.singletonList(Component.of(jiraComponent.getName()))));
|
||||
|
||||
return jiraIssue;
|
||||
}
|
||||
|
||||
private JiraIssues execute(String context, JqlQuery query, HttpHeaders headers, JiraIssuesCallback callback) {
|
||||
|
||||
JiraIssues issues;
|
||||
@@ -458,15 +495,16 @@ class Jira implements JiraConnector {
|
||||
|
||||
private Ticket toTicket(JiraIssue issue) {
|
||||
|
||||
JiraIssue.Fields fields = issue.getFields();
|
||||
|
||||
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());
|
||||
if (fields.hasStatusAndResolution()) {
|
||||
|
||||
Status status = fields.getStatus();
|
||||
boolean resolved = status.getStatusCategory().getKey().equals("done");
|
||||
Resolution resolution = fields.getResolution();
|
||||
|
||||
jiraTicketStatus = JiraTicketStatus.of(resolved, status.getName(), resolution.getName());
|
||||
} else {
|
||||
jiraTicketStatus = JiraTicketStatus.UNKNOWN;
|
||||
}
|
||||
@@ -486,24 +524,9 @@ class Jira implements JiraConnector {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try (ConfigurableApplicationContext context = SpringApplication.run(Application.class, args)) {
|
||||
|
||||
JiraConnector tracker = context.getBean(JiraConnector.class);
|
||||
TrainIteration iteration = new TrainIteration(ReleaseTrains.GOSLING, Iteration.SR1);
|
||||
ModuleIteration module = iteration.getModule("JPA");
|
||||
|
||||
// Changelog changelog = tracker.getChangelogFor(module);
|
||||
// System.out.println(changelog);
|
||||
|
||||
System.out.println(tracker.getReleaseTicketFor(module));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> newUrlTemplateVariables() {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("jiraBaseUrl", jiraProperties.getUrl());
|
||||
parameters.put("jiraBaseUrl", jiraProperties.getApiUrl());
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@@ -13,30 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class JiraComponent {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
@JsonCreator
|
||||
public JiraComponent(@JsonProperty("id") String id, @JsonProperty("name") String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
String id, name;
|
||||
|
||||
public boolean hasName(String name) {
|
||||
return this.name.equals(name);
|
||||
@@ -14,28 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Value object to represent a list of {@link JiraComponent}s.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Value(staticConstructor = "of")
|
||||
class JiraComponents implements Iterable<JiraComponent> {
|
||||
|
||||
@NonNull private Collection<JiraComponent> jiraComponents;
|
||||
@NonNull Collection<JiraComponent> jiraComponents;
|
||||
|
||||
/**
|
||||
* Try to find a component by its {@code name}.
|
||||
@@ -47,12 +44,10 @@ class JiraComponents implements Iterable<JiraComponent> {
|
||||
return jiraComponents.stream().filter(jiraComponent -> jiraComponent.hasName(name)).findFirst();
|
||||
}
|
||||
|
||||
public static JiraComponents of(Collection<JiraComponent> jiraComponents) {
|
||||
|
||||
Assert.notNull(jiraComponents, "JiraComponents must not be null!");
|
||||
return new JiraComponents(jiraComponents);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<JiraComponent> iterator() {
|
||||
return jiraComponents.iterator();
|
||||
@@ -13,10 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.release.issues.IssueTracker;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
|
||||
@@ -24,32 +26,28 @@ import org.springframework.data.release.model.TrainIteration;
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface JiraConnector extends IssueTracker {
|
||||
interface JiraConnector extends IssueTracker {
|
||||
|
||||
/**
|
||||
* Verifies the state of all {@link Tickets} before releasing. In particular: Check whether the release ticket exists,
|
||||
* check whether all other issue tickets are in a resolved state.
|
||||
*
|
||||
* @param iteration
|
||||
* @param iteration must not be {@literal null}.
|
||||
*/
|
||||
void verifyBeforeRelease(TrainIteration iteration);
|
||||
|
||||
/**
|
||||
* Closes the given {@link TrainIteration}.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
*/
|
||||
void closeIteration(TrainIteration iteration);
|
||||
|
||||
/**
|
||||
* Lookup a Jira release version.
|
||||
* Lookup a JIRA release version.
|
||||
*
|
||||
* @param moduleIteration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Optional<JiraReleaseVersion> findJiraReleaseVersion(ModuleIteration moduleIteration);
|
||||
|
||||
/**
|
||||
* Assigns the ticket to the current user.
|
||||
*
|
||||
* @param ticket must not be {@literal null}.
|
||||
* @param credentials must not be {@literal null}.
|
||||
*/
|
||||
void assignTicketToMe(Ticket ticket);
|
||||
|
||||
}
|
||||
@@ -13,69 +13,32 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
import org.springframework.data.release.model.Tracker;
|
||||
import org.springframework.data.release.model.Train;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Value
|
||||
class JiraIssue {
|
||||
|
||||
private String key;
|
||||
private Fields fields;
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is only fixed in the given {@link Train}. {@literal false} indicates the {@link Ticket}
|
||||
* has been resolved for
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
*/
|
||||
public boolean wasBackportedFrom(Train train) {
|
||||
|
||||
List<FixVersions> fixVersions = fields.getFixVersions();
|
||||
return fixVersions != null && wasBackportedFrom(train, fixVersions);
|
||||
}
|
||||
|
||||
private boolean wasBackportedFrom(Train train, List<FixVersions> fixVersions) {
|
||||
return fixVersions.size() > 1 && isPresent(train, fixVersions);
|
||||
}
|
||||
|
||||
private boolean isPresent(Train train, List<FixVersions> fixVersions) {
|
||||
return fixVersions.stream().//
|
||||
filter(fixVersion -> fixVersion.isTrain(train)).//
|
||||
findFirst().//
|
||||
isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is a release ticket.
|
||||
*
|
||||
* @param moduleIteration
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
List<FixVersions> fixVersions = fields.getFixVersions();
|
||||
return fixVersions.size() == 1 && isPresent(moduleIteration.getTrain(), fixVersions)
|
||||
&& fields.hasSummary(Tracker.releaseTicketSummary(moduleIteration));
|
||||
}
|
||||
String key;
|
||||
Fields fields;
|
||||
|
||||
/**
|
||||
* Creates a new Jira issue value object.
|
||||
@@ -83,10 +46,7 @@ class JiraIssue {
|
||||
* @return
|
||||
*/
|
||||
public static JiraIssue create() {
|
||||
|
||||
JiraIssue jiraIssue = new JiraIssue();
|
||||
jiraIssue.setFields(new Fields());
|
||||
return jiraIssue;
|
||||
return new JiraIssue(null, new Fields());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +60,32 @@ class JiraIssue {
|
||||
return jiraIssue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is only fixed in the given {@link Train}. {@literal false} indicates the {@link Ticket}
|
||||
* has been resolved for
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
*/
|
||||
public boolean wasBackportedFrom(Train train) {
|
||||
|
||||
List<FixVersion> fixVersions = fields.getFixVersions();
|
||||
return fixVersions != null && wasBackportedFrom(train, fixVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ticket is a release ticket.
|
||||
*
|
||||
* @param moduleIteration
|
||||
* @return
|
||||
*/
|
||||
public boolean isReleaseTicket(ModuleIteration moduleIteration) {
|
||||
|
||||
List<FixVersion> fixVersions = fields.getFixVersions();
|
||||
return fixVersions.size() == 1 && isPresent(moduleIteration.getTrain(), fixVersions)
|
||||
&& fields.hasSummary(Tracker.releaseTicketSummary(moduleIteration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign to the user specified in {@link Credentials}.
|
||||
*
|
||||
@@ -131,7 +117,7 @@ class JiraIssue {
|
||||
*/
|
||||
public JiraIssue project(ProjectKey projectKey) {
|
||||
|
||||
getFields().setProject(new Project(projectKey));
|
||||
getFields().setProject(Project.from(projectKey));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -156,18 +142,29 @@ class JiraIssue {
|
||||
public JiraIssue fixVersion(ModuleIteration moduleIteration) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null!");
|
||||
getFields().setFixVersions(Collections.singletonList(new JiraVersion(moduleIteration).toFixVersions()));
|
||||
getFields().setFixVersions(Collections.singletonList(new JiraVersion(moduleIteration).toFixVersion()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static boolean wasBackportedFrom(Train train, List<FixVersion> fixVersions) {
|
||||
return fixVersions.size() > 1 && isPresent(train, fixVersions);
|
||||
}
|
||||
|
||||
private static boolean isPresent(Train train, List<FixVersion> fixVersions) {
|
||||
return fixVersions.stream().//
|
||||
filter(fixVersion -> fixVersion.isTrain(train)).//
|
||||
findFirst().//
|
||||
isPresent();
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Fields {
|
||||
|
||||
Project project;
|
||||
IssueType issuetype;
|
||||
String summary;
|
||||
List<FixVersions> fixVersions;
|
||||
List<FixVersion> fixVersions;
|
||||
List<Component> components;
|
||||
Status status;
|
||||
Resolution resolution;
|
||||
@@ -176,33 +173,31 @@ class JiraIssue {
|
||||
public boolean hasSummary(String other) {
|
||||
return summary.equals(other);
|
||||
}
|
||||
|
||||
public boolean hasStatusAndResolution() {
|
||||
return status != null && resolution != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class Component {
|
||||
|
||||
String id;
|
||||
String name;
|
||||
String id, name;
|
||||
|
||||
public static Component from(String name) {
|
||||
public static Component of(String name) {
|
||||
|
||||
Assert.hasText(name, "Name must not be empty!");
|
||||
return new Component(null, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
static class FixVersions {
|
||||
@Value
|
||||
static class FixVersion {
|
||||
|
||||
String id;
|
||||
String name;
|
||||
String id, name;
|
||||
|
||||
/**
|
||||
* Returns whether this {@link FixVersions} matches the given {@link Train}.
|
||||
* Returns whether this {@link FixVersion} matches the given {@link Train}.
|
||||
*
|
||||
* @param train
|
||||
* @return
|
||||
@@ -212,9 +207,7 @@ class JiraIssue {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class IssueType {
|
||||
|
||||
public final static IssueType TASK = new IssueType("Task");
|
||||
@@ -222,50 +215,39 @@ class JiraIssue {
|
||||
String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class Status {
|
||||
|
||||
String name;
|
||||
StatusCategory statusCategory;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class Project {
|
||||
|
||||
String key;
|
||||
|
||||
public Project(ProjectKey projectKey) {
|
||||
this.key = projectKey.getKey();
|
||||
public static Project from(ProjectKey projectKey) {
|
||||
return new Project(projectKey.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class Resolution {
|
||||
|
||||
String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class StatusCategory {
|
||||
|
||||
String key;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Value
|
||||
static class JiraUser {
|
||||
|
||||
String name;
|
||||
String displayName;
|
||||
String name, displayName;
|
||||
|
||||
static JiraUser from(String username) {
|
||||
return new JiraUser(username, null);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,26 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.release.Streamable;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Data
|
||||
@Value
|
||||
class JiraIssues implements Streamable<JiraIssue> {
|
||||
|
||||
private int startAt;
|
||||
private int maxResults;
|
||||
private int total;
|
||||
|
||||
private List<JiraIssue> issues;
|
||||
int startAt, maxResult;
|
||||
@Getter int total;
|
||||
List<JiraIssue> issues = new ArrayList<>();
|
||||
|
||||
public int getNextStartAt() {
|
||||
return startAt + issues.size();
|
||||
@@ -42,6 +42,10 @@ class JiraIssues implements Streamable<JiraIssue> {
|
||||
return startAt + issues.size() < total;
|
||||
}
|
||||
|
||||
public boolean hasIssues() {
|
||||
return !issues.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -22,12 +26,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Configurable properties for Git.
|
||||
* Configurable properties for JIRA.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@@ -37,14 +37,14 @@ import lombok.Getter;
|
||||
class JiraProperties {
|
||||
|
||||
private @Getter(AccessLevel.PRIVATE) String password;
|
||||
private String username, url;
|
||||
private String username, apiUrl;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
Assert.hasText(username, "No Jira username (jira.username) configured!");
|
||||
Assert.hasText(password, "No Jira password (jira.password) configured!");
|
||||
Assert.hasText(url, "No Jira url (jira.url) configured!");
|
||||
Assert.hasText(apiUrl, "No Jira url (jira.api-url) configured!");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,5 +55,4 @@ class JiraProperties {
|
||||
public Credentials getCredentials() {
|
||||
return new Credentials(username, password);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,46 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
public class JiraReleaseVersion {
|
||||
@Value
|
||||
class JiraReleaseVersion {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String project;
|
||||
private String description;
|
||||
String id, name, project, description;
|
||||
|
||||
@JsonCreator
|
||||
public JiraReleaseVersion(@JsonProperty("id") String id, @JsonProperty("name") String name,
|
||||
@JsonProperty("string") String project, @JsonProperty("description") String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.project = project;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public JiraReleaseVersion(ModuleIteration moduleIteration, JiraVersion jiraVersion) {
|
||||
public static JiraReleaseVersion of(ModuleIteration moduleIteration, JiraVersion jiraVersion) {
|
||||
|
||||
Assert.notNull(moduleIteration, "ModuleIteration must not be null.");
|
||||
Assert.notNull(jiraVersion, "JiraVersion must not be null.");
|
||||
|
||||
project = moduleIteration.getProjectKey().getKey();
|
||||
name = jiraVersion.toString();
|
||||
description = jiraVersion.getDescription();
|
||||
return new JiraReleaseVersion(null, jiraVersion.toString(), moduleIteration.getProjectKey().getKey(),
|
||||
jiraVersion.getDescription());
|
||||
}
|
||||
|
||||
public boolean hasSameNameAs(JiraVersion jiraVersion) {
|
||||
@@ -13,28 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.release.Streamable;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Value object to bind REST responses to.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Data
|
||||
@Value
|
||||
class JiraReleaseVersions implements Streamable<JiraReleaseVersion> {
|
||||
|
||||
private int startAt;
|
||||
private int maxResults;
|
||||
private int total;
|
||||
|
||||
private List<JiraReleaseVersion> values;
|
||||
int startAt, maxResults, total;
|
||||
List<JiraReleaseVersion> values = new ArrayList<>();
|
||||
|
||||
public int getNextStartAt() {
|
||||
return startAt + values.size();
|
||||
@@ -14,21 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.issues.TicketStatus;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class JiraTicketStatus implements TicketStatus {
|
||||
@Value(staticConstructor = "of")
|
||||
class JiraTicketStatus implements TicketStatus {
|
||||
|
||||
public static final JiraTicketStatus UNKNOWN = new JiraTicketStatus(false, "unknown", null);
|
||||
public static final JiraTicketStatus UNKNOWN = JiraTicketStatus.of(false, "unknown", null);
|
||||
|
||||
private final boolean resolved;
|
||||
private final String status;
|
||||
private final String resolution;
|
||||
boolean resolved;
|
||||
@NonNull String status;
|
||||
String resolution;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -13,20 +13,20 @@
|
||||
* 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.jira.JiraIssue.FixVersions;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import org.springframework.data.release.issues.jira.JiraIssue.FixVersion;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
class JiraVersion {
|
||||
|
||||
private ModuleIteration module;
|
||||
ModuleIteration module;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -41,7 +41,7 @@ class JiraVersion {
|
||||
return module.getTrain().getName() + " " + module.getIteration().getName();
|
||||
}
|
||||
|
||||
public FixVersions toFixVersions() {
|
||||
return new FixVersions(null, toString());
|
||||
public FixVersion toFixVersion() {
|
||||
return new FixVersion(null, toString());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -13,7 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -22,10 +24,9 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
@@ -38,16 +39,16 @@ class JqlQuery {
|
||||
|
||||
private final String query;
|
||||
|
||||
public JqlQuery and(String clause) {
|
||||
return new JqlQuery(String.format("(%s) AND %s", query, clause));
|
||||
}
|
||||
|
||||
public JqlQuery orderBy(String orderBy) {
|
||||
return new JqlQuery(String.format("%s ORDER BY %s", query, orderBy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JqlQuery} for the given {@link ModuleIteration}.
|
||||
*
|
||||
* @param iteration must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static JqlQuery from(ModuleIteration iteration) {
|
||||
|
||||
Assert.notNull(iteration, "Module iteration must not be null!");
|
||||
|
||||
JiraVersion version = new JiraVersion(iteration);
|
||||
|
||||
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
|
||||
@@ -72,6 +73,18 @@ class JqlQuery {
|
||||
return new JqlQuery(StringUtils.collectionToDelimitedString(parts, " OR "));
|
||||
}
|
||||
|
||||
public JqlQuery and(String clause) {
|
||||
return new JqlQuery(String.format("(%s) AND %s", query, clause));
|
||||
}
|
||||
|
||||
public JqlQuery orderBy(String orderBy) {
|
||||
return new JqlQuery(String.format("%s ORDER BY %s", query, orderBy));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return query;
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.ModuleIteration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
class GitHubIssue {
|
||||
|
||||
private String number;
|
||||
private String title;
|
||||
private String state;
|
||||
private Object milestone;
|
||||
|
||||
@JsonCreator
|
||||
public GitHubIssue(@JsonProperty("number") String number, @JsonProperty("title") String title,
|
||||
@JsonProperty("state") String state, @JsonProperty("milestone") Object milestone) {
|
||||
this.number = number;
|
||||
this.title = title;
|
||||
this.state = state;
|
||||
this.milestone = milestone;
|
||||
}
|
||||
|
||||
public GitHubIssue(String title, Milestone milestone) {
|
||||
|
||||
this.title = title;
|
||||
this.milestone = milestone.getNumber();
|
||||
this.number = null;
|
||||
this.state = null;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
||||
if (number == null) {
|
||||
return null;
|
||||
}
|
||||
return "#".concat(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@Value
|
||||
static class Milestone {
|
||||
|
||||
private final String title;
|
||||
private final Long number;
|
||||
private final String description;
|
||||
|
||||
@JsonCreator
|
||||
public Milestone(@JsonProperty("title") String title, @JsonProperty("number") Long number,
|
||||
@JsonProperty("description") String description) {
|
||||
this.title = title;
|
||||
this.number = number;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Milestone(String title, String description) {
|
||||
this.number = null;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public boolean matchesIteration(ModuleIteration moduleIteration) {
|
||||
return title.contains(moduleIteration.getShortVersionString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@ import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.git.GitOperations;
|
||||
import org.springframework.data.release.io.Workspace;
|
||||
import org.springframework.data.release.jira.Changelog;
|
||||
import org.springframework.data.release.jira.IssueTracker;
|
||||
import org.springframework.data.release.issues.Changelog;
|
||||
import org.springframework.data.release.issues.IssueTracker;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.release.utils;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.release.Streamable;
|
||||
@@ -62,16 +63,22 @@ public class ExecutionUtils {
|
||||
* @return
|
||||
*/
|
||||
public static <T, S> Collection<S> runAndReturn(Streamable<T> streamable, Function<T, S> function) {
|
||||
return runAndReturn(streamable, function, Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, S, R> R runAndReturn(Streamable<T> streamable, Function<T, S> function,
|
||||
Collector<? super S, ?, R> collector) {
|
||||
|
||||
Assert.notNull(streamable, "Iterable must not be null!");
|
||||
Assert.notNull(function, "Consumer must not be null!");
|
||||
|
||||
return streamable.stream().//
|
||||
map(it -> CompletableFuture.supplyAsync(() -> function.apply(it))).//
|
||||
filter(it -> it != null).//
|
||||
collect(Collectors.toList()).//
|
||||
stream().//
|
||||
map(future -> future.join()).//
|
||||
collect(Collectors.toList());
|
||||
collect(collector);
|
||||
}
|
||||
|
||||
public static interface ConsumerWithException<T> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
io.work-dir=~/temp/spring-data-shell/workspace
|
||||
|
||||
# Maven setup
|
||||
maven.localRepository=~/temp/spring-data-shell/repository
|
||||
maven.local-repository=~/temp/spring-data-shell/repository
|
||||
maven.plugins.versions=org.codehaus.mojo:versions-maven-plugin:2.2
|
||||
|
||||
deployment.server.uri=https://repo.spring.io
|
||||
@@ -12,4 +12,10 @@ deployment.username=buildmaster
|
||||
# GPG setup
|
||||
deployment.gpg.executable=/usr/local/bin/gpg2
|
||||
# deployment.gpg.keyname
|
||||
# deployment.gpg.password
|
||||
# deployment.gpg.password
|
||||
|
||||
# JIRA
|
||||
jira.api-url=https://jira.spring.io
|
||||
|
||||
# GitHub
|
||||
github.api-url=https://api.github.com
|
||||
@@ -30,7 +30,6 @@ import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
import org.springframework.data.release.model.UpdateInformation;
|
||||
import org.xmlbeam.ProjectionFactory;
|
||||
import org.xmlbeam.io.XBFileIO;
|
||||
|
||||
@@ -75,7 +74,7 @@ public class MavenIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
TrainIteration iteration = new TrainIteration(ReleaseTrains.HOPPER, Iteration.M1);
|
||||
ModuleIteration build = iteration.getModule(Projects.BUILD);
|
||||
UpdateInformation information = new UpdateInformation(iteration, Phase.PREPARE);
|
||||
UpdateInformation information = UpdateInformation.of(iteration, Phase.PREPARE);
|
||||
|
||||
maven.updateProjectDescriptors(build, information);
|
||||
maven.prepareVersion(build, Phase.PREPARE);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-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.
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.model;
|
||||
package org.springframework.data.release.build;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -21,6 +21,11 @@ import static org.junit.Assert.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.Phase;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.data.release.model.TrainIteration;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link UpdateInformation}.
|
||||
@@ -33,17 +38,17 @@ public class UpdateInformationUnitTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullTrainIteration() {
|
||||
new UpdateInformation(null, Phase.CLEANUP);
|
||||
UpdateInformation.of(null, Phase.CLEANUP);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullPhase() {
|
||||
new UpdateInformation(hopperM1, null);
|
||||
UpdateInformation.of(hopperM1, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exposesMilestoneRepositoryForMilestone() {
|
||||
assertThat(new UpdateInformation(hopperM1, Phase.PREPARE).getRepository().getId(), is("spring-libs-milestone"));
|
||||
assertThat(UpdateInformation.of(hopperM1, Phase.PREPARE).getRepository().getId(), is("spring-libs-milestone"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -51,7 +56,7 @@ public class UpdateInformationUnitTests {
|
||||
|
||||
Arrays.asList(Iteration.GA, Iteration.SR1).forEach(iteration -> {
|
||||
TrainIteration trainIteration = new TrainIteration(ReleaseTrains.HOPPER, iteration);
|
||||
assertThat(new UpdateInformation(trainIteration, Phase.PREPARE).getRepository().getId(),
|
||||
assertThat(UpdateInformation.of(trainIteration, Phase.PREPARE).getRepository().getId(),
|
||||
is("spring-libs-release"));
|
||||
});
|
||||
}
|
||||
@@ -59,10 +64,10 @@ public class UpdateInformationUnitTests {
|
||||
@Test
|
||||
public void calculatesProjectVersionToSetCorrectly() {
|
||||
|
||||
UpdateInformation updateInformation = new UpdateInformation(hopperM1, Phase.PREPARE);
|
||||
UpdateInformation updateInformation = UpdateInformation.of(hopperM1, Phase.PREPARE);
|
||||
assertThat(updateInformation.getProjectVersionToSet(Projects.JPA).toString(), is("1.10.0.M1"));
|
||||
|
||||
updateInformation = new UpdateInformation(hopperM1, Phase.CLEANUP);
|
||||
updateInformation = UpdateInformation.of(hopperM1, Phase.CLEANUP);
|
||||
assertThat(updateInformation.getProjectVersionToSet(Projects.JPA).toString(), is("1.10.0.BUILD-SNAPSHOT"));
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.github;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||
@@ -25,25 +25,31 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.AbstractIntegrationTests;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.github.GitHub;
|
||||
import org.springframework.data.release.issues.github.GitHubProperties;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
|
||||
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
||||
|
||||
/**
|
||||
* Integration Tests for {@link GitHubIssueTracker} using a local {@link WireMockRule} server.
|
||||
* Integration Tests for {@link GitHub} using a local {@link WireMockRule} server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@@ -59,11 +65,17 @@ public class GitHubIssueTrackerIntegrationTests extends AbstractIntegrationTests
|
||||
|
||||
@Rule public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Autowired IssueTracker github;
|
||||
@Autowired GitHub github;
|
||||
@Autowired GitHubProperties properties;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(properties.getApiUrl()).build();
|
||||
Assume.assumeThat(uriComponents.getHost(), is("localhost"));
|
||||
|
||||
github.reset();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.github;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.release.issues.github.GithubMilestone;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -22,6 +22,8 @@ import static org.junit.Assert.*;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.release.issues.jira.JiraComponent;
|
||||
import org.springframework.data.release.issues.jira.JiraComponents;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JiraComponents}.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
|
||||
@@ -26,12 +26,18 @@ import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.release.AbstractIntegrationTests;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.jira.Jira;
|
||||
import org.springframework.data.release.issues.jira.JiraConnector;
|
||||
import org.springframework.data.release.issues.jira.JiraProperties;
|
||||
import org.springframework.data.release.issues.jira.JiraReleaseVersion;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ProjectKey;
|
||||
@@ -39,6 +45,8 @@ import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
|
||||
import com.github.tomakehurst.wiremock.common.ClasspathFileSource;
|
||||
@@ -49,7 +57,7 @@ import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JiraIntegrationTests extends AbstractIntegrationTests {
|
||||
public class JiraConnectorIntegrationTests extends AbstractIntegrationTests {
|
||||
|
||||
public static final String CREATE_ISSUE_URI = "/rest/api/2/issue";
|
||||
public static final String CREATE_VERSION_URI = "/rest/api/2/version";
|
||||
@@ -64,9 +72,14 @@ public class JiraIntegrationTests extends AbstractIntegrationTests {
|
||||
@Rule public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Autowired JiraConnector jira;
|
||||
@Autowired JiraProperties properties;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(properties.getApiUrl()).build();
|
||||
Assume.assumeThat(uriComponents.getHost(), is("localhost"));
|
||||
|
||||
jira.reset();
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.release.issues.jira.JiraVersion;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.release.jira;
|
||||
package org.springframework.data.release.issues.jira;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -23,6 +23,9 @@ import static org.springframework.test.util.AssertionErrors.fail;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.release.issues.Ticket;
|
||||
import org.springframework.data.release.issues.Tickets;
|
||||
import org.springframework.data.release.issues.jira.JiraTicketStatus;
|
||||
import org.springframework.data.release.model.Iteration;
|
||||
import org.springframework.data.release.model.ReleaseTrains;
|
||||
|
||||
@@ -93,5 +96,4 @@ public class TicketsUnitTests {
|
||||
.getReleaseTickets(ReleaseTrains.HOPPER.getModuleIteration(Iteration.GA, "JPA").getTrainIteration());
|
||||
assertThat(result.getTickets().contains(ticket), is(true));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
logging.level.org.springframework=WARN
|
||||
logging.level.org.springframework.data.release=INFO
|
||||
logging.level.org.springframework.web.client=TRACE
|
||||
logging.level.org.springframework.http=DEBUG
|
||||
|
||||
# Deployment
|
||||
deployment.repository-prefix=test-
|
||||
|
||||
jira.username=dummy
|
||||
jira.password=dummy
|
||||
jira.url=http://localhost:8888
|
||||
github.api.url=http://localhost:8888
|
||||
jira.api-url=http://localhost:8888
|
||||
github.api-url=http://localhost:8888
|
||||
Reference in New Issue
Block a user