From 5e09fda269814916d0e81117f21602cae0009543 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 16 Apr 2014 20:18:37 +0200 Subject: [PATCH] Further new features. Added support for creating changelogs from GitHub (for Spring Data Build project). A lot of internal refactorings, strengthening the domain model. Moved project declarations into Projects class. Made types package protected where possible. Added dedicated TrainIteration type as well as a Shell Converter implementation to allow this type to be handed into the command implementations. Unified logging into separate component to make sure we have consistent output. --- pom.xml | 6 + .../announcement/AnnouncementOperations.java | 18 +-- ...Commands.java => IssueTracerCommands.java} | 52 ++++--- .../data/release/cli/ModelCommands.java | 10 +- .../data/release/cli/ReleaseCommands.java | 45 +++--- .../data/release/cli/TrainConverter.java | 62 ++++++++ .../release/cli/TrainIterationConverter.java | 80 ++++++++++ .../data/release/git/Branch.java | 2 +- .../data/release/git/GiCommands.java | 22 +-- .../data/release/git/GitOperations.java | 25 ++- .../io/CommonsExecOsCommandOperations.java | 12 +- .../data/release/jira/Changelog.java | 2 +- .../data/release/jira/GitHubIssue.java | 28 ++++ .../data/release/jira/GitHubIssueTracker.java | 143 ++++++++++++++++++ .../data/release/jira/GitHubMilestone.java | 28 ++++ .../data/release/jira/IssueTracker.java | 28 ++++ .../{RestJiraConnector.java => Jira.java} | 84 ++++++++-- .../data/release/jira/JiraConfiguration.java | 2 + .../data/release/jira/JiraConnector.java | 13 +- .../data/release/jira/JqlQuery.java | 14 +- .../data/release/jira/Tickets.java | 6 + .../data/release/maven/Artifact.java | 10 +- .../data/release/maven/MavenOperations.java | 91 ++++++----- .../data/release/maven/MavenProject.java | 2 +- .../data/release/misc/ReleaseOperations.java | 15 +- .../data/release/model/ArtifactVersion.java | 12 +- .../data/release/model/Iteration.java | 14 ++ .../data/release/model/IterationVersion.java | 2 + .../data/release/model/Iterations.java | 76 ---------- .../data/release/model/Module.java | 4 +- .../data/release/model/ModuleIteration.java | 25 +++ .../data/release/model/Phase.java | 24 +++ .../data/release/model/Project.java | 23 ++- .../data/release/model/Projects.java | 49 ++++++ .../data/release/model/ReleaseTrains.java | 23 +-- .../data/release/model/Tracker.java | 24 +++ .../data/release/model/Train.java | 58 ++++++- .../data/release/model/TrainIteration.java | 64 ++++++++ .../data/release/utils/Logger.java | 49 ++++++ .../data/release/ArtifactUnitTests.java | 41 +++++ .../git/GitOperationsIntegrationTests.java | 4 +- .../release/model/IterationUnitTests.java | 24 +++ .../model/ModuleIterationUnitTests.java | 45 ++++++ 43 files changed, 1069 insertions(+), 292 deletions(-) rename src/main/java/org/springframework/data/release/cli/{JiraCommands.java => IssueTracerCommands.java} (60%) create mode 100644 src/main/java/org/springframework/data/release/cli/TrainConverter.java create mode 100644 src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java create mode 100644 src/main/java/org/springframework/data/release/jira/GitHubIssue.java create mode 100644 src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java create mode 100644 src/main/java/org/springframework/data/release/jira/GitHubMilestone.java create mode 100644 src/main/java/org/springframework/data/release/jira/IssueTracker.java rename src/main/java/org/springframework/data/release/jira/{RestJiraConnector.java => Jira.java} (62%) delete mode 100644 src/main/java/org/springframework/data/release/model/Iterations.java create mode 100644 src/main/java/org/springframework/data/release/model/Phase.java create mode 100644 src/main/java/org/springframework/data/release/model/Projects.java create mode 100644 src/main/java/org/springframework/data/release/model/Tracker.java create mode 100644 src/main/java/org/springframework/data/release/model/TrainIteration.java create mode 100644 src/main/java/org/springframework/data/release/utils/Logger.java create mode 100644 src/test/java/org/springframework/data/release/ArtifactUnitTests.java create mode 100644 src/test/java/org/springframework/data/release/model/IterationUnitTests.java create mode 100644 src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java diff --git a/pom.xml b/pom.xml index 70b5ec5..bfbff66 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,12 @@ 1.12.4 provided + + + org.springframework.plugin + spring-plugin-core + 1.0.0.RELEASE + org.springframework.boot diff --git a/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java b/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java index 651f8d0..900d257 100644 --- a/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java +++ b/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java @@ -15,14 +15,16 @@ */ package org.springframework.data.release.announcement; +import static org.springframework.data.release.model.Projects.*; + import org.springframework.data.release.cli.StaticResources; import org.springframework.data.release.maven.Artifact; -import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; import org.springframework.util.Assert; /** @@ -38,25 +40,19 @@ public class AnnouncementOperations { * @param iteration must not be {@literal null}. * @return */ - public String getProjectBulletpoints(Train train, Iteration iteration) { + public String getProjectBulletpoints(TrainIteration iteration) { - Assert.notNull(train, "Train must not be null!"); Assert.notNull(iteration, "Iteration must not be null!"); StringBuilder builder = new StringBuilder(); - for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) { + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { Project project = module.getProject(); builder.append("* "); builder.append(project.getFullName()).append(" "); - builder.append(ArtifactVersion.from(module).toShortString()); - - if (!iteration.isServiceIteration()) { - builder.append(" ").append(module.getIteration().getName()); - } - + builder.append(module.getVersionString()); builder.append(" - "); Artifact artifact = new Artifact(module); @@ -83,6 +79,6 @@ public class AnnouncementOperations { public static void main(String[] args) { AnnouncementOperations operations = new AnnouncementOperations(); - System.out.println(operations.getProjectBulletpoints(ReleaseTrains.CODD, Iteration.SR2)); + System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.CODD, Iteration.SR2))); } } diff --git a/src/main/java/org/springframework/data/release/cli/JiraCommands.java b/src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java similarity index 60% rename from src/main/java/org/springframework/data/release/cli/JiraCommands.java rename to src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java index a764689..15268dc 100644 --- a/src/main/java/org/springframework/data/release/cli/JiraCommands.java +++ b/src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java @@ -18,10 +18,12 @@ package org.springframework.data.release.cli; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.data.release.jira.Credentials; +import org.springframework.data.release.jira.IssueTracker; import org.springframework.data.release.jira.JiraConnector; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.plugin.core.PluginRegistry; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; @@ -32,58 +34,62 @@ import org.springframework.util.StringUtils; * @author Oliver Gierke */ @Component -public class JiraCommands implements CommandMarker { +public class IssueTracerCommands implements CommandMarker { - private final JiraConnector connector; + private final PluginRegistry tracker; + private final JiraConnector jira; private final Credentials credentials; /** - * @param connector + * @param tracker * @param environment */ @Autowired - public JiraCommands(JiraConnector connector, Environment environment) { + public IssueTracerCommands(PluginRegistry tracker, JiraConnector jira, Environment environment) { String username = environment.getProperty("jira.username", (String) null); String password = environment.getProperty("jira.password", (String) null); - this.connector = connector; + this.tracker = tracker; + this.jira = jira; this.credentials = StringUtils.hasText(username) ? new Credentials(username, password) : null; } @CliCommand("jira evict") public void jiraEvict() { - connector.reset(); + jira.reset(); } @CliCommand(value = "jira tickets") public String jira( - @CliOption(key = { "", "train" }, mandatory = true, help = "The name of the release train.") String trainName, // - @CliOption(key = "iteration", mandatory = true, help = "An iteration key (one of M1, RC1, GA).") String iterationName, // + @CliOption(key = "", mandatory = true) TrainIteration iteration, // @CliOption(key = "for-current-user", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") boolean forCurrentUser) { if (forCurrentUser && credentials == null) { return "No authentication specified! Use 'jira authenticate' first!"; } - Train train = ReleaseTrains.getTrainByName(trainName); - Iteration iteration = train.getIterations().getIterationByName(iterationName); - - return connector.getTicketsFor(train, iteration, forCurrentUser ? credentials : null).toString(); + return jira.getTicketsFor(iteration, forCurrentUser ? credentials : null).toString(); } - @CliCommand("changelog") - public String changelog(@CliOption(key = { "", "train" }, mandatory = true) String trainName, // - @CliOption(key = { "iteration" }, mandatory = true) String iterationName, // + @CliCommand("tracker changelog") + public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration, // @CliOption(key = "module") String moduleName) { - Train train = ReleaseTrains.getTrainByName(trainName); - Iteration iteration = train.getIteration(iterationName); - if (StringUtils.hasText(moduleName)) { - return connector.getChangelogFor(train.getModuleIteration(iteration, moduleName)).toString(); + + ModuleIteration module = iteration.getModule(moduleName); + return tracker.getPluginFor(module.getProject()).getChangelogFor(module).toString(); } - return ""; + StringBuilder builder = new StringBuilder(); + + for (ModuleIteration module : iteration) { + + IssueTracker issues = tracker.getPluginFor(module.getProject()); + builder.append(issues.getChangelogFor(module)).append("\n"); + } + + return builder.toString(); } } diff --git a/src/main/java/org/springframework/data/release/cli/ModelCommands.java b/src/main/java/org/springframework/data/release/cli/ModelCommands.java index 2344df7..522e3b1 100644 --- a/src/main/java/org/springframework/data/release/cli/ModelCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ModelCommands.java @@ -33,16 +33,16 @@ import org.springframework.util.StringUtils; public class ModelCommands implements CommandMarker { @CliCommand("trains") - public String train(@CliOption(key = { "", "train" }) String trainName) { + public String train(@CliOption(key = { "", "train" }) Train train) { - if (StringUtils.hasText(trainName)) { - return ReleaseTrains.getTrainByName(trainName).toString(); + if (train != null) { + return train.toString(); } List names = new ArrayList<>(); - for (Train train : ReleaseTrains.TRAINS) { - names.add(train.getName()); + for (Train releaseTrain : ReleaseTrains.TRAINS) { + names.add(releaseTrain.getName()); } return StringUtils.collectionToDelimitedString(names, ", "); diff --git a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java index 35f26e5..2b4667c 100644 --- a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -15,6 +15,7 @@ */ package org.springframework.data.release.cli; +import static org.springframework.data.release.model.Projects.*; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -24,11 +25,11 @@ import org.springframework.data.release.maven.MavenOperations; import org.springframework.data.release.maven.Pom; import org.springframework.data.release.misc.ReleaseOperations; import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.Module; -import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Phase; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; @@ -48,17 +49,16 @@ public class ReleaseCommands implements CommandMarker { @CliCommand("release predict") public String predictTrainAndIteration() throws Exception { - Project commons = ReleaseTrains.COMMONS; - Pom pom = maven.getMavenProject(commons); + Pom pom = maven.getMavenProject(COMMONS); - Tags tags = git.getTags(commons); + Tags tags = git.getTags(COMMONS); ArtifactVersion version = tags.getLatest().toArtifactVersion(); System.out.println(version); for (Train train : ReleaseTrains.TRAINS) { - Module module = train.getModule(commons); + Module module = train.getModule(COMMONS); if (!pom.getVersion().toString().startsWith(module.getVersion().toMajorMinorBugfix())) { continue; @@ -78,14 +78,10 @@ public class ReleaseCommands implements CommandMarker { * @throws Exception */ @CliCommand("release distribute") - public void distribute(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption( - key = "iteration", mandatory = true) String iterationName) throws Exception { + public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - Train train = ReleaseTrains.getTrainByName(trainName); - Iteration iteration = train.getIteration(iterationName); - - git.checkout(train, iteration); - maven.triggerDistributionBuild(train, iteration); + git.checkout(iteration); + maven.triggerDistributionBuild(iteration); } /** @@ -96,17 +92,22 @@ public class ReleaseCommands implements CommandMarker { * @throws Exception */ @CliCommand(value = "release prepare", help = "Prepares the release of the iteration of the given train.") - public void prepare(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption( - key = "iteration", mandatory = true) String iterationName) throws Exception { + public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - Train train = ReleaseTrains.getTrainByName(trainName); - Iteration iteration = train.getIteration(iterationName); + git.prepare(iteration); + misc.prepareChangelogs(iteration); + maven.updatePom(iteration, Phase.PREPARE); + } - git.prepare(train, iteration); - misc.prepareChangelogs(train, iteration); + @CliCommand(value = "release conclude") + public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - for (Module module : train) { - maven.prepareProject(train, iteration, module.getProject()); - } + // - pull updates + // - tag release + // - post release pom updates + + maven.updatePom(iteration, Phase.CLEANUP); + + // - push } } diff --git a/src/main/java/org/springframework/data/release/cli/TrainConverter.java b/src/main/java/org/springframework/data/release/cli/TrainConverter.java new file mode 100644 index 0000000..5ab2deb --- /dev/null +++ b/src/main/java/org/springframework/data/release/cli/TrainConverter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014 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.cli; + +import java.util.List; + +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.Train; +import org.springframework.shell.core.Completion; +import org.springframework.shell.core.Converter; +import org.springframework.shell.core.MethodTarget; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +/** + * @author Oliver Gierke + */ +@Component +public class TrainConverter implements Converter { + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String) + */ + @Override + public boolean supports(Class type, String optionContext) { + return Train.class.isAssignableFrom(type); + } + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String) + */ + @Override + public Train convertFromText(String value, Class targetType, String optionContext) { + return StringUtils.hasText(value) ? ReleaseTrains.getTrainByName(value) : null; + } + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget) + */ + @Override + public boolean getAllPossibleValues(List completions, Class targetType, String existingData, + String optionContext, MethodTarget target) { + + return false; + } +} diff --git a/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java b/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java new file mode 100644 index 0000000..61ab822 --- /dev/null +++ b/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java @@ -0,0 +1,80 @@ +/* + * Copyright 2014 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.cli; + +import java.util.List; + +import org.springframework.data.release.model.Iteration; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.shell.core.Completion; +import org.springframework.shell.core.Converter; +import org.springframework.shell.core.MethodTarget; +import org.springframework.stereotype.Component; + +/** + * @author Oliver Gierke + */ +@Component +public class TrainIterationConverter implements Converter { + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String) + */ + @Override + public boolean supports(Class type, String optionContext) { + return TrainIteration.class.equals(type); + } + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String) + */ + @Override + public TrainIteration convertFromText(String value, Class targetType, String optionContext) { + + String[] parts = value.split(" "); + Train train = ReleaseTrains.getTrainByName(parts[0].trim()); + Iteration iteration = train.getIteration(parts[1].trim()); + + return new TrainIteration(train, iteration); + } + + /* + * (non-Javadoc) + * @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget) + */ + @Override + public boolean getAllPossibleValues(List completions, Class targetType, String existingData, + String optionContext, MethodTarget target) { + + for (Train train : ReleaseTrains.TRAINS) { + + // if (!StringUtils.hasText(existingData) && + // !train.getName().toLowerCase().startsWith(existingData.toLowerCase())) { + // continue; + // } + + for (Iteration iteration : train.getIterations()) { + completions.add(new Completion(new TrainIteration(train, iteration).toString())); + } + } + + return false; + } +} diff --git a/src/main/java/org/springframework/data/release/git/Branch.java b/src/main/java/org/springframework/data/release/git/Branch.java index 516e818..0e74377 100644 --- a/src/main/java/org/springframework/data/release/git/Branch.java +++ b/src/main/java/org/springframework/data/release/git/Branch.java @@ -29,7 +29,7 @@ import org.springframework.util.Assert; */ @RequiredArgsConstructor @EqualsAndHashCode -public class Branch { +class Branch { private static final Branch MASTER = new Branch("master"); diff --git a/src/main/java/org/springframework/data/release/git/GiCommands.java b/src/main/java/org/springframework/data/release/git/GiCommands.java index da7c504..a98956b 100644 --- a/src/main/java/org/springframework/data/release/git/GiCommands.java +++ b/src/main/java/org/springframework/data/release/git/GiCommands.java @@ -18,10 +18,9 @@ package org.springframework.data.release.git; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; @@ -33,24 +32,18 @@ import org.springframework.util.StringUtils; */ @Component @RequiredArgsConstructor(onConstructor = @__(@Autowired)) -public class GiCommands implements CommandMarker { +class GiCommands implements CommandMarker { private final GitOperations git; @CliCommand("git checkout") - public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption( - key = "iteration", mandatory = true) String iterationName) throws Exception { - - Train train = ReleaseTrains.getTrainByName(trainName); - Iteration iteration = train.getIteration(iterationName); - - git.checkout(train, iteration); + public void checkout(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { + git.checkout(iteration); } @CliCommand("git update") public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception, InterruptedException { - git.update(ReleaseTrains.getTrainByName(trainName)); } @@ -69,10 +62,7 @@ public class GiCommands implements CommandMarker { } @CliCommand("git prepare") - public void prepare(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption( - key = "iteration", mandatory = true) String iterationName) throws Exception { - - Train train = ReleaseTrains.getTrainByName(trainName); - git.prepare(train, train.getIteration(iterationName)); + public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { + git.prepare(iteration); } } diff --git a/src/main/java/org/springframework/data/release/git/GitOperations.java b/src/main/java/org/springframework/data/release/git/GitOperations.java index 03f026d..3b8cd7d 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; -import java.util.logging.Logger; import lombok.RequiredArgsConstructor; @@ -34,7 +33,8 @@ import org.springframework.data.release.model.Module; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.Train; -import org.springframework.shell.support.logging.HandlerUtils; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.utils.Logger; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -48,11 +48,10 @@ import org.springframework.util.StringUtils; @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class GitOperations { - private static final Logger LOGGER = HandlerUtils.getLogger(GitOperations.class); - private final GitServer server = new GitServer(); private final OsCommandOperations osCommandOperations; private final Workspace workspace; + private final Logger logger; public GitProject getGitProject(Project project) { return new GitProject(project, server); @@ -80,11 +79,11 @@ public class GitOperations { * @param iteration * @throws Exception */ - public void checkout(Train train, Iteration iteration) throws Exception { + public void checkout(TrainIteration iteration) throws Exception { - update(train); + update(iteration.getTrain()); - for (ModuleIteration module : train.getModuleIterations(iteration)) { + for (ModuleIteration module : iteration) { Project project = module.getProject(); ArtifactVersion artifactVersion = ArtifactVersion.from(module); @@ -99,13 +98,12 @@ public class GitOperations { osCommandOperations.executeCommand(String.format("git checkout %s", tag), project).get(); } - LOGGER.info(String.format("Successfully checked out iteration %s for release train %s.", iteration.getName(), - train.getName())); + logger.log(iteration, "Successfully checked out projects."); } - public void prepare(Train train, Iteration iteration) throws Exception { + public void prepare(TrainIteration iteration) throws Exception { - for (ModuleIteration module : train.getModuleIterations(iteration)) { + for (ModuleIteration module : iteration) { Branch branch = Branch.from(module); @@ -139,15 +137,14 @@ public class GitOperations { if (workspace.hasProjectDirectory(project)) { - LOGGER.info(String.format("Found existing repository %s. Obtaining latest changes…", repositoryName)); + logger.log(project, "Found existing repository %s. Obtaining latest changes…", repositoryName); return osCommandOperations.executeCommand("git checkout master && git fetch --tags && git pull origin master", project); } else { - LOGGER.info(String.format("No repository found for project %s. Cloning repository from %s…", repositoryName, - gitProject.getProjectUri())); + logger.log(project, "No repository found! Cloning from %s…", gitProject.getProjectUri()); File projectDirectory = workspace.getProjectDirectory(project); String command = String.format("git clone %s %s", gitProject.getProjectUri(), projectDirectory.getName()); diff --git a/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java b/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java index a028585..c8a6dd4 100644 --- a/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java +++ b/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java @@ -21,7 +21,6 @@ import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; -import java.util.logging.Logger; import javax.annotation.PostConstruct; @@ -33,11 +32,10 @@ import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.io.output.WriterOutputStream; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.release.git.GitOperations; import org.springframework.data.release.model.Project; +import org.springframework.data.release.utils.Logger; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; -import org.springframework.shell.support.logging.HandlerUtils; import org.springframework.stereotype.Component; /** @@ -51,12 +49,10 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor(onConstructor = @__(@Autowired)) class CommonsExecOsCommandOperations implements OsCommandOperations { - private static final Logger LOGGER = HandlerUtils.getLogger(GitOperations.class); - private static final String PREFIX_TEMPLATE = "%s > %s"; - private static final Map ENVIRONMENT = new HashMap<>(); private final Workspace workspace; + private final Logger logger; /* * (non-Javadoc) @@ -76,7 +72,7 @@ class CommonsExecOsCommandOperations implements OsCommandOperations { @Override public Future executeCommand(String command, Project project) throws IOException { - LOGGER.info(String.format(PREFIX_TEMPLATE, project.getName(), command)); + logger.log(project, command); return executeCommand(command, workspace.getProjectDirectory(project), true); } @@ -88,7 +84,7 @@ class CommonsExecOsCommandOperations implements OsCommandOperations { @Override public Future executeWithOutput(String command, Project project) throws IOException { - LOGGER.info(String.format(PREFIX_TEMPLATE, project.getName(), command)); + logger.log(project, command); return executeCommand(command, workspace.getProjectDirectory(project), false); } diff --git a/src/main/java/org/springframework/data/release/jira/Changelog.java b/src/main/java/org/springframework/data/release/jira/Changelog.java index 8397c9a..facb97b 100644 --- a/src/main/java/org/springframework/data/release/jira/Changelog.java +++ b/src/main/java/org/springframework/data/release/jira/Changelog.java @@ -61,7 +61,7 @@ public class Changelog { String summary = ticket.getSummary(); - builder.append("* ").append(ticket.getId()).append(" - ").append(summary); + builder.append("* ").append(ticket.getId()).append(" - ").append(summary.trim()); if (!summary.endsWith(".")) { builder.append("."); diff --git a/src/main/java/org/springframework/data/release/jira/GitHubIssue.java b/src/main/java/org/springframework/data/release/jira/GitHubIssue.java new file mode 100644 index 0000000..7e0edde --- /dev/null +++ b/src/main/java/org/springframework/data/release/jira/GitHubIssue.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.jira; + +import lombok.Data; + +/** + * @author Oliver Gierke + */ +@Data +class GitHubIssue { + + private String number; + private String title; +} diff --git a/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java b/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java new file mode 100644 index 0000000..4a7c7b4 --- /dev/null +++ b/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java @@ -0,0 +1,143 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.jira; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +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.GitServer; +import org.springframework.data.release.model.Iteration; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.Tracker; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.utils.Logger; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.util.UriTemplate; + +/** + * @author Oliver Gierke + */ +@Component +@RequiredArgsConstructor(onConstructor = @_(@Autowired)) +class GitHubIssueTracker implements IssueTracker { + + private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}"; + private static final String URI_TEMPLATE = "https://api.github.com/repos/spring-projects/{repoName}/issues?milestone={id}&state=all"; + + private static final ParameterizedTypeReference> MILESTONES_TYPE = new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> ISSUES_TYPE = new ParameterizedTypeReference>() {}; + + private final RestOperations operations; + private final Logger logger; + + /* + * (non-Javadoc) + * @see org.springframework.data.release.jira.IssueTracker#getChangelogFor(org.springframework.data.release.model.ModuleIteration) + */ + @Override + public Changelog getChangelogFor(ModuleIteration module) { + + String repositoryName = new GitProject(module.getProject(), new GitServer()).getRepositoryName(); + + GitHubMilestone milestone = findMilestone(module, repositoryName); + + Map parameters = new HashMap<>(); + parameters.put("repoName", repositoryName); + parameters.put("id", milestone.getNumber()); + + List issues = operations.exchange(URI_TEMPLATE, HttpMethod.GET, null, ISSUES_TYPE, parameters) + .getBody(); + List tickets = new ArrayList<>(issues.size()); + + for (GitHubIssue issue : issues) { + tickets.add(new Ticket("#" + issue.getNumber(), issue.getTitle())); + } + + logger.log(module, "Created changelog with %s entries.", tickets.size()); + + return new Changelog(module, new Tickets(tickets)); + } + + /* + * (non-Javadoc) + * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) + */ + @Override + public boolean supports(Project project) { + return project.uses(Tracker.GITHUB); + } + + private GitHubMilestone findMilestone(ModuleIteration module, String repositoryName) { + + for (String state : Arrays.asList("close", "open")) { + + Map parameters = new HashMap<>(); + parameters.put("repoName", repositoryName); + parameters.put("state", state); + + URI milestoneUri = new UriTemplate(MILESTONE_URI).expand(parameters); + + logger.log(module, "Looking up milestone from %s…", milestoneUri); + + List exchange = operations.exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE, + parameters).getBody(); + + GitHubMilestone milestone = null; + + for (GitHubMilestone candidate : exchange) { + if (candidate.getTitle().contains(module.getVersionString())) { + milestone = candidate; + } + } + + if (milestone != null) { + logger.log(module, "Found milestone %s.", milestone); + return milestone; + } + } + + throw new IllegalStateException(String.format("No milestone found containing %s!", module.getVersionString())); + } + + public static void main(String[] args) { + + try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( + "META-INF/spring/spring-shell-plugin.xml")) { + + GitHubIssueTracker tracker = context.getBean(GitHubIssueTracker.class); + + TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2); + Changelog changelog = tracker.getChangelogFor(iteration.getModule("Build")); + + System.out.println(changelog); + } + } +} diff --git a/src/main/java/org/springframework/data/release/jira/GitHubMilestone.java b/src/main/java/org/springframework/data/release/jira/GitHubMilestone.java new file mode 100644 index 0000000..7cee9a4 --- /dev/null +++ b/src/main/java/org/springframework/data/release/jira/GitHubMilestone.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.jira; + +import lombok.Data; + +/** + * @author Oliver Gierke + */ +@Data +class GitHubMilestone { + + private String title; + private Long number; +} diff --git a/src/main/java/org/springframework/data/release/jira/IssueTracker.java b/src/main/java/org/springframework/data/release/jira/IssueTracker.java new file mode 100644 index 0000000..9af5a21 --- /dev/null +++ b/src/main/java/org/springframework/data/release/jira/IssueTracker.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 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 org.springframework.data.release.model.Project; +import org.springframework.plugin.core.Plugin; + +/** + * @author Oliver Gierke + */ +public interface IssueTracker extends Plugin { + + Changelog getChangelogFor(ModuleIteration iteration); +} diff --git a/src/main/java/org/springframework/data/release/jira/RestJiraConnector.java b/src/main/java/org/springframework/data/release/jira/Jira.java similarity index 62% rename from src/main/java/org/springframework/data/release/jira/RestJiraConnector.java rename to src/main/java/org/springframework/data/release/jira/Jira.java index 812fbb6..3faf275 100644 --- a/src/main/java/org/springframework/data/release/jira/RestJiraConnector.java +++ b/src/main/java/org/springframework/data/release/jira/Jira.java @@ -15,35 +15,40 @@ */ package org.springframework.data.release.jira; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; 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.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.Project; +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; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; +import org.springframework.web.util.UriTemplate; /** * @author Oliver Gierke */ @Component @RequiredArgsConstructor(onConstructor = @_(@Autowired)) -class RestJiraConnector implements JiraConnector { - - protected final Logger LOGGER = Logger.getLogger(getClass().getName()); +class Jira implements JiraConnector { private static final String JIRA_HOST = "https://jira.spring.io"; private static final String BASE_URI = "/rest/api/2"; @@ -51,6 +56,7 @@ class RestJiraConnector implements JiraConnector { + "/search?jql={jql}&fields={fields}&startAt={startAt}"; private final RestOperations operations; + private final Logger logger; /* * (non-Javadoc) @@ -62,15 +68,33 @@ class RestJiraConnector implements JiraConnector { } + @Cacheable("release-tickets") + public Ticket getReleaseTicketFor(ModuleIteration iteration) { + + JqlQuery query = JqlQuery.from(iteration).and("summary ~ \"Release\""); + + Map parameters = new HashMap<>(); + parameters.put("jql", query); + parameters.put("fields", "summary"); + parameters.put("startAt", 0); + + JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters) + .getBody(); + + JiraIssue issue = issues.getIssues().get(0); + + return new Ticket(issue.getKey(), issue.getFields().getSummary()); + } + /* * (non-Javadoc) * @see org.springframework.data.release.jira.JiraConnector#getTicketsFor(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration, org.springframework.data.release.jira.Credentials) */ @Override @Cacheable("tickets") - public Tickets getTicketsFor(Train train, Iteration iteration, Credentials credentials) { + public Tickets getTicketsFor(TrainIteration iteration, Credentials credentials) { - JqlQuery query = JqlQuery.from(train, iteration); + JqlQuery query = JqlQuery.from(iteration); HttpHeaders headers = new HttpHeaders(); int startAt = 0; @@ -83,10 +107,9 @@ class RestJiraConnector implements JiraConnector { headers.set("Authorization", String.format("Basic %s", credentials.asBase64())); - LOGGER.info(String.format("Retrieving tickets for %s %s (for user %s).", train.getName(), iteration.getName(), - credentials.getUsername())); + logger.log(iteration, "Retrieving tickets (for user %s)…", credentials.getUsername()); } else { - LOGGER.info(String.format("Retrieving tickets for %s %s.", train.getName(), iteration.getName())); + logger.log(iteration, "Retrieving tickets…"); } query = query.orderBy("updatedDate DESC"); @@ -101,10 +124,10 @@ class RestJiraConnector implements JiraConnector { issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers), JiraIssues.class, parameters).getBody(); - LOGGER.info(String.format("Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal())); + logger.log(iteration, "Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal()); for (JiraIssue issue : issues) { - if (!issue.wasBackportedFrom(train)) { + if (!issue.wasBackportedFrom(iteration.getTrain())) { tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary())); } } @@ -121,7 +144,7 @@ class RestJiraConnector implements JiraConnector { * @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration) */ @Override - public void verifyBeforeRelease(Train train, Iteration iteration) { + public void verifyBeforeRelease(TrainIteration iteration) { // for each module @@ -134,7 +157,7 @@ class RestJiraConnector implements JiraConnector { * @see org.springframework.data.release.jira.JiraConnector#closeIteration(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration, org.springframework.data.release.jira.Credentials) */ @Override - public void closeIteration(Train train, Iteration iteration, Credentials credentials) { + public void closeIteration(TrainIteration iteration, Credentials credentials) { // for each module @@ -159,13 +182,44 @@ class RestJiraConnector implements JiraConnector { parameters.put("fields", "summary,fixVersions"); parameters.put("startAt", 0); - JiraIssues issues = operations.getForObject(SEARCH_TEMPLATE, JiraIssues.class, parameters); + URI searchUri = new UriTemplate(SEARCH_TEMPLATE).expand(parameters); + + logger.log(module, "Looking up JIRA issues from %s…", searchUri); + + JiraIssues issues = operations.getForObject(searchUri, JiraIssues.class); List tickets = new ArrayList<>(); for (JiraIssue issue : issues) { tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary())); } + logger.log(module, "Created changelog with %s entries.", tickets.size()); + return new Changelog(module, new Tickets(tickets, tickets.size())); } + + /* + * (non-Javadoc) + * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) + */ + @Override + public boolean supports(Project project) { + return project.uses(Tracker.JIRA); + } + + public static void main(String[] args) { + + try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( + "META-INF/spring/spring-shell-plugin.xml")) { + + JiraConnector tracker = context.getBean(JiraConnector.class); + TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2); + ModuleIteration module = iteration.getModule("JPA"); + + // Changelog changelog = tracker.getChangelogFor(module); + // System.out.println(changelog); + + System.out.println(tracker.getReleaseTicketFor(module)); + } + } } diff --git a/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java b/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java index d795836..6a87c67 100644 --- a/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java +++ b/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.plugin.core.config.EnablePluginRegistries; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -37,6 +38,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; @Configuration @EnableCaching @PropertySource(value = "file:jira.properties", ignoreResourceNotFound = true) +@EnablePluginRegistries({ IssueTracker.class }) class JiraConfiguration { @Bean diff --git a/src/main/java/org/springframework/data/release/jira/JiraConnector.java b/src/main/java/org/springframework/data/release/jira/JiraConnector.java index 22914a1..45af741 100644 --- a/src/main/java/org/springframework/data/release/jira/JiraConnector.java +++ b/src/main/java/org/springframework/data/release/jira/JiraConnector.java @@ -18,14 +18,17 @@ package org.springframework.data.release.jira; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; /** * @author Oliver Gierke */ -public interface JiraConnector { +public interface JiraConnector extends IssueTracker { void reset(); + Ticket getReleaseTicketFor(ModuleIteration iteration); + /** * Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}. * @@ -33,11 +36,9 @@ public interface JiraConnector { * @param iteration must not be {@literal null}. * @return */ - Tickets getTicketsFor(Train train, Iteration iteration, Credentials credentials); + Tickets getTicketsFor(TrainIteration iteration, Credentials credentials); - void verifyBeforeRelease(Train train, Iteration iteration); + void verifyBeforeRelease(TrainIteration iteration); - void closeIteration(Train train, Iteration iteration, Credentials credentials); - - Changelog getChangelogFor(ModuleIteration iteration); + void closeIteration(TrainIteration iteration, Credentials credentials); } diff --git a/src/main/java/org/springframework/data/release/jira/JqlQuery.java b/src/main/java/org/springframework/data/release/jira/JqlQuery.java index b59806c..d54d1c6 100644 --- a/src/main/java/org/springframework/data/release/jira/JqlQuery.java +++ b/src/main/java/org/springframework/data/release/jira/JqlQuery.java @@ -15,15 +15,15 @@ */ package org.springframework.data.release.jira; +import static org.springframework.data.release.model.Projects.*; + import java.util.ArrayList; import java.util.List; import lombok.Value; -import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; import org.springframework.util.StringUtils; /** @@ -51,15 +51,11 @@ class JqlQuery { return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version)); } - public static JqlQuery from(Train train, Iteration iteration) { + public static JqlQuery from(TrainIteration iteration) { List parts = new ArrayList<>(); - for (ModuleIteration module : train.getModuleIterations(iteration)) { - - if (ReleaseTrains.BUILD.equals(module.getProject())) { - continue; - } + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { JiraVersion version = new JiraVersion(module); parts.add(String.format(PROJECT_VERSION_TEMPLATE, module.getProjectKey(), version)); diff --git a/src/main/java/org/springframework/data/release/jira/Tickets.java b/src/main/java/org/springframework/data/release/jira/Tickets.java index a56a7fc..c7d6160 100644 --- a/src/main/java/org/springframework/data/release/jira/Tickets.java +++ b/src/main/java/org/springframework/data/release/jira/Tickets.java @@ -18,6 +18,7 @@ package org.springframework.data.release.jira; import java.util.Iterator; import java.util.List; +import lombok.RequiredArgsConstructor; import lombok.Value; import org.springframework.util.StringUtils; @@ -28,11 +29,16 @@ import org.springframework.util.StringUtils; * @author Oliver Gierke */ @Value +@RequiredArgsConstructor public class Tickets implements Iterable { private final List tickets; private final int overallTotal; + public Tickets(List tickets) { + this(tickets, tickets.size()); + } + /* * (non-Javadoc) * @see java.lang.Iterable#iterator() diff --git a/src/main/java/org/springframework/data/release/maven/Artifact.java b/src/main/java/org/springframework/data/release/maven/Artifact.java index 7727d42..cd99321 100644 --- a/src/main/java/org/springframework/data/release/maven/Artifact.java +++ b/src/main/java/org/springframework/data/release/maven/Artifact.java @@ -15,11 +15,12 @@ */ package org.springframework.data.release.maven; +import static org.springframework.data.release.model.Projects.*; +import lombok.EqualsAndHashCode; import lombok.Getter; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.ReleaseTrains; import org.springframework.util.Assert; /** @@ -27,6 +28,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ +@EqualsAndHashCode public class Artifact { private static final GroupId GROUP_ID = new GroupId("org.springframework.data"); @@ -57,7 +59,11 @@ public class Artifact { public String getArtifactId() { String artifactId = String.format("spring-data-%s", module.getProject().getName().toLowerCase()); - return ReleaseTrains.REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId; + return REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId; + } + + public ArtifactVersion getNextDevelopmentVersion() { + return version.getNextDevelopmentVersion(); } /** diff --git a/src/main/java/org/springframework/data/release/maven/MavenOperations.java b/src/main/java/org/springframework/data/release/maven/MavenOperations.java index 4098ad0..012454c 100644 --- a/src/main/java/org/springframework/data/release/maven/MavenOperations.java +++ b/src/main/java/org/springframework/data/release/maven/MavenOperations.java @@ -15,9 +15,11 @@ */ package org.springframework.data.release.maven; +import static org.springframework.data.release.model.Phase.*; +import static org.springframework.data.release.model.Projects.*; + import java.io.File; import java.io.IOException; -import java.util.logging.Logger; import lombok.RequiredArgsConstructor; @@ -26,12 +28,12 @@ import org.springframework.data.release.io.CommandResult; import org.springframework.data.release.io.OsCommandOperations; import org.springframework.data.release.io.Workspace; import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Phase; import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; -import org.springframework.shell.support.logging.HandlerUtils; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.utils.Logger; import org.springframework.stereotype.Component; import org.xmlbeam.ProjectionFactory; import org.xmlbeam.io.XBFileIO; @@ -44,12 +46,12 @@ import org.xmlbeam.io.XBFileIO; public class MavenOperations { private static final String COMMONS_VERSION_PROPERTY = "springdata.commons"; - private static final Logger LOGGER = HandlerUtils.getLogger(MavenOperations.class); private static final String POM_XML = "pom.xml"; private final Workspace workspace; private final ProjectionFactory projectionFactory; private final OsCommandOperations os; + private final Logger logger; public Pom getMavenProject(Project project) throws IOException { @@ -57,38 +59,36 @@ public class MavenOperations { return projectionFactory.io().file(file).read(Pom.class); } - public void prepareProject(Train train, Iteration iteration, final Project project) throws Exception { + public void updatePom(TrainIteration iteration, final Phase phase) throws Exception { - updateBomPom(train, iteration); + updateBomPom(iteration, phase); - if (ReleaseTrains.BUILD.equals(project)) { - return; - } + final Repository repository = new Repository(iteration.getIteration()); + final ArtifactVersion commonsVersion = iteration.getModuleVersion(COMMONS); + final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD); - final ArtifactVersion commonsVersion = train.getModuleVersion(ReleaseTrains.COMMONS, iteration); - final ArtifactVersion buildVersion = train.getModuleVersion(ReleaseTrains.BUILD, iteration); - final Repository repository = new Repository(iteration); + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { - File file = workspace.getFile(POM_XML, project); + final Project project = module.getProject(); + File pomFile = workspace.getFile(POM_XML, project); - execute(file, new PomCallback() { + execute(pomFile, new PomCallback() { - @Override - public Pom doWith(Pom pom) { + @Override + public Pom doWith(Pom pom) { - if (!project.equals(ReleaseTrains.COMMONS)) { + if (!project.equals(COMMONS)) { + pom.setProperty(COMMONS_VERSION_PROPERTY, + CLEANUP.equals(phase) ? commonsVersion.getNextDevelopmentVersion() : commonsVersion); + } - System.out.println(pom.getProperty(COMMONS_VERSION_PROPERTY)); - pom.setProperty(COMMONS_VERSION_PROPERTY, commonsVersion); + pom.setParentVersion(CLEANUP.equals(phase) ? buildVersion.getNextDevelopmentVersion() : buildVersion); + updateRepository(pom, repository, phase); + + return pom; } - - pom.setParentVersion(buildVersion); - pom.setRepositoryId("spring-libs-snapshot", "spring-libs-release"); - pom.setRepositoryUrl(repository.getId(), repository.getUrl()); - - return pom; - } - }); + }); + } } /** @@ -99,23 +99,22 @@ public class MavenOperations { * @throws IOException * @throws InterruptedException */ - public void triggerDistributionBuild(Train train, Iteration iteration) throws Exception { + public void triggerDistributionBuild(TrainIteration iteration) throws Exception { - for (ModuleIteration moduleIteration : train.getModuleIterations(iteration)) { + for (ModuleIteration moduleIteration : iteration) { Project project = moduleIteration.getProject(); - if (ReleaseTrains.BUILD.equals(project)) { + if (BUILD.equals(project)) { continue; } if (!isMavenProject(project)) { - LOGGER.info(String.format("Skipping project %s as no pom.xml could be found in the working directory!", - project.getFullName())); + logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); continue; } - LOGGER.info(String.format("Triggering distribution build for %s…", project.getFullName())); + logger.log(project, "Triggering distribution build…"); ArtifactVersion version = ArtifactVersion.from(moduleIteration); @@ -133,7 +132,7 @@ public class MavenOperations { throw result.getException(); } - LOGGER.info(String.format("Successfully finished distribution build for %s!", project)); + logger.log(project, "Successfully finished distribution build!"); } } @@ -141,19 +140,22 @@ public class MavenOperations { return workspace.getFile(POM_XML, project).exists(); } - private void updateBomPom(final Train train, final Iteration iteration) throws Exception { + private void updateBomPom(final TrainIteration iteration, final Phase phase) throws Exception { - File bomPomFile = workspace.getFile("bom/pom.xml", ReleaseTrains.BUILD); + File bomPomFile = workspace.getFile("bom/pom.xml", BUILD); execute(bomPomFile, new PomCallback() { @Override public Pom doWith(Pom pom) { - for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) { + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { Artifact artifact = new Artifact(module); - pom.setDependencyVersion(artifact.getArtifactId(), artifact.getVersion()); + ArtifactVersion version = artifact.getVersion(); + version = PREPARE.equals(phase) ? version : version.getNextDevelopmentVersion(); + + pom.setDependencyVersion(artifact.getArtifactId(), version); } return pom; @@ -161,6 +163,17 @@ public class MavenOperations { }); } + private void updateRepository(Pom pom, Repository repository, Phase phase) { + + if (PREPARE.equals(phase)) { + pom.setRepositoryId(repository.getSnapshotId(), repository.getId()); + pom.setRepositoryUrl(repository.getId(), repository.getUrl()); + } else { + pom.setRepositoryId(repository.getId(), repository.getSnapshotId()); + pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl()); + } + } + private void execute(File file, PomCallback callback) throws Exception { XBFileIO io = projectionFactory.io().file(file); diff --git a/src/main/java/org/springframework/data/release/maven/MavenProject.java b/src/main/java/org/springframework/data/release/maven/MavenProject.java index 3682d4f..6d1d7eb 100644 --- a/src/main/java/org/springframework/data/release/maven/MavenProject.java +++ b/src/main/java/org/springframework/data/release/maven/MavenProject.java @@ -24,7 +24,7 @@ import org.springframework.data.release.model.Module; * @author Oliver Gierke */ @Value -public class MavenProject { +class MavenProject { private final Module module; diff --git a/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java b/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java index 4053584..6c02351 100644 --- a/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java +++ b/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java @@ -24,11 +24,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.io.Workspace; import org.springframework.data.release.jira.Changelog; -import org.springframework.data.release.jira.JiraConnector; +import org.springframework.data.release.jira.IssueTracker; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.Project; import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.plugin.core.PluginRegistry; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -41,7 +43,7 @@ import com.google.common.io.Files; @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ReleaseOperations { - private final JiraConnector jira; + private final PluginRegistry trackers; private final Workspace workspace; /** @@ -51,14 +53,13 @@ public class ReleaseOperations { * @param iteration must not be {@literal null}. * @throws Exception */ - public void prepareChangelogs(Train train, Iteration iteration) throws Exception { + public void prepareChangelogs(TrainIteration iteration) throws Exception { - Assert.notNull(train, "Release train must not be null!"); Assert.notNull(iteration, "Iteration must not be null!"); - for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) { + for (ModuleIteration module : iteration) { - Changelog changelog = jira.getChangelogFor(module); + Changelog changelog = trackers.getPluginFor(module.getProject()).getChangelogFor(module); File file = workspace.getFile("src/main/resources/changelog.txt", module.getProject()); StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/org/springframework/data/release/model/ArtifactVersion.java b/src/main/java/org/springframework/data/release/model/ArtifactVersion.java index 6034d0f..2c0ff68 100644 --- a/src/main/java/org/springframework/data/release/model/ArtifactVersion.java +++ b/src/main/java/org/springframework/data/release/model/ArtifactVersion.java @@ -83,18 +83,18 @@ public class ArtifactVersion implements Comparable { Assert.notNull(iterationVersion, "IterationVersion must not be null!"); Version version = iterationVersion.getVersion(); - String iterationName = iterationVersion.getIteration().getName(); + Iteration iteration = iterationVersion.getIteration(); - if (iterationName.equals("GA")) { + if (iteration.isGAVersion()) { return new ArtifactVersion(version, RELEASE_SUFFIX); } - if (iterationName.startsWith("SR")) { - int bugfixDigits = Integer.parseInt(iterationName.substring(2, iterationName.length())); - return new ArtifactVersion(version.withBugfix(bugfixDigits), RELEASE_SUFFIX); + if (iteration.isServiceIteration()) { + Version bugfixVersion = version.withBugfix(iteration.getBugfixValue()); + return new ArtifactVersion(bugfixVersion, RELEASE_SUFFIX); } - return new ArtifactVersion(version, iterationName); + return new ArtifactVersion(version, iteration.getName()); } /** diff --git a/src/main/java/org/springframework/data/release/model/Iteration.java b/src/main/java/org/springframework/data/release/model/Iteration.java index f0c0b47..935a1d4 100644 --- a/src/main/java/org/springframework/data/release/model/Iteration.java +++ b/src/main/java/org/springframework/data/release/model/Iteration.java @@ -18,6 +18,8 @@ package org.springframework.data.release.model; import lombok.NonNull; import lombok.Value; +import org.springframework.util.Assert; + /** * Value object to represent an individual release train iteration. * @@ -40,6 +42,18 @@ public class Iteration { private final @NonNull String name; private final Iteration next; + Iteration(String name, Iteration next) { + + Assert.hasText(name, "Name must not be null or empty!"); + + this.name = name; + this.next = next; + } + + public boolean isGAVersion() { + return this.equals(GA); + } + public boolean isPublicVersion() { return isServiceIteration() || this.equals(GA); } diff --git a/src/main/java/org/springframework/data/release/model/IterationVersion.java b/src/main/java/org/springframework/data/release/model/IterationVersion.java index a6159d3..09ee3bc 100644 --- a/src/main/java/org/springframework/data/release/model/IterationVersion.java +++ b/src/main/java/org/springframework/data/release/model/IterationVersion.java @@ -16,6 +16,8 @@ package org.springframework.data.release.model; /** + * A {@link Version} tied to an {@link Iteration}. + * * @author Oliver Gierke */ public interface IterationVersion { diff --git a/src/main/java/org/springframework/data/release/model/Iterations.java b/src/main/java/org/springframework/data/release/model/Iterations.java deleted file mode 100644 index c74f410..0000000 --- a/src/main/java/org/springframework/data/release/model/Iterations.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.model; - -import static org.springframework.data.release.model.Iteration.*; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import lombok.Value; - -import org.springframework.util.Assert; - -/** - * Value object to represent a set of {@link Iteration}s. - * - * @author Oliver Gierke - */ -@Value -public class Iterations implements Iterable { - - public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4); - - private final List iterations; - - /** - * Creates a new {@link Iterations} from the given {@link Iteration}. - * - * @param iterations - */ - Iterations(Iteration... iterations) { - this.iterations = Arrays.asList(iterations); - } - - /** - * Returns the iteration with the given name. - * - * @param name must not be {@literal null} or empty. - * @return - */ - public Iteration getIterationByName(String name) { - - Assert.hasText(name, "Name must not be null or empty!"); - - for (Iteration iteration : this) { - if (iteration.getName().equalsIgnoreCase(name)) { - return iteration; - } - } - - return null; - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return iterations.iterator(); - } -} diff --git a/src/main/java/org/springframework/data/release/model/Module.java b/src/main/java/org/springframework/data/release/model/Module.java index e228bda..35d320b 100644 --- a/src/main/java/org/springframework/data/release/model/Module.java +++ b/src/main/java/org/springframework/data/release/model/Module.java @@ -29,11 +29,11 @@ public class Module { private final Version version; private final Iteration customFirstIteration; - public Module(Project project, String version) { + Module(Project project, String version) { this(project, version, null); } - public Module(Project project, String version, String customFirstIteration) { + Module(Project project, String version, String customFirstIteration) { Assert.notNull(project, "Project must not be null!"); diff --git a/src/main/java/org/springframework/data/release/model/ModuleIteration.java b/src/main/java/org/springframework/data/release/model/ModuleIteration.java index 62a8e09..30d1753 100644 --- a/src/main/java/org/springframework/data/release/model/ModuleIteration.java +++ b/src/main/java/org/springframework/data/release/model/ModuleIteration.java @@ -62,4 +62,29 @@ public class ModuleIteration implements IterationVersion { return String.format("%s %s (%s)", module.getVersion(), iteration.getName(), train.getName()); } + /** + * Returns the {@link String} representation of the logical version of the {@link ModuleIteration}. + * + * @return + */ + public String getVersionString() { + + StringBuilder builder = new StringBuilder(); + builder.append(ArtifactVersion.from(this).toShortString()); + + if (!iteration.isServiceIteration()) { + builder.append(" ").append(iteration.getName()); + } + + return builder.toString(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("%s %s", module.getProject().getFullName(), getVersionString()); + } } diff --git a/src/main/java/org/springframework/data/release/model/Phase.java b/src/main/java/org/springframework/data/release/model/Phase.java new file mode 100644 index 0000000..e35b9fa --- /dev/null +++ b/src/main/java/org/springframework/data/release/model/Phase.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +/** + * @author Oliver Gierke + */ +public enum Phase { + + PREPARE, CLEANUP; +} diff --git a/src/main/java/org/springframework/data/release/model/Project.java b/src/main/java/org/springframework/data/release/model/Project.java index dd4b93c..8cb89d2 100644 --- a/src/main/java/org/springframework/data/release/model/Project.java +++ b/src/main/java/org/springframework/data/release/model/Project.java @@ -18,23 +18,36 @@ package org.springframework.data.release.model; import java.util.Arrays; import java.util.List; -import lombok.Value; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; /** * @author Oliver Gierke */ -@Value +@ToString +@EqualsAndHashCode public class Project { - private final ProjectKey key; - private final String name; + private final @Getter ProjectKey key; + private final @Getter String name; private final List dependencies; + private final Tracker tracker; - public Project(String key, String name, Project... dependencies) { + Project(String key, String name, Project... dependencies) { + this(key, name, Tracker.JIRA, dependencies); + } + + Project(String key, String name, Tracker tracker, Project... dependencies) { this.key = new ProjectKey(key); this.name = name; this.dependencies = Arrays.asList(dependencies); + this.tracker = tracker; + } + + public boolean uses(Tracker tracker) { + return this.tracker.equals(tracker); } public String getFullName() { diff --git a/src/main/java/org/springframework/data/release/model/Projects.java b/src/main/java/org/springframework/data/release/model/Projects.java new file mode 100644 index 0000000..2ad8c77 --- /dev/null +++ b/src/main/java/org/springframework/data/release/model/Projects.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Oliver Gierke + */ +public class Projects { + + public static final Project COMMONS, BUILD, REST, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, + REDIS, GEMFIRE; + public static final List PROJECTS; + + static { + + BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB); + COMMONS = new Project("DATACMNS", "Commons", BUILD); + JPA = new Project("DATAJPA", "JPA", COMMONS); + MONGO_DB = new Project("DATAMONGO", "MongoDB", COMMONS); + NEO4J = new Project("DATAGRAPH", "Neo4j", COMMONS); + SOLR = new Project("DATASOLR", "Solr", COMMONS); + COUCHBASE = new Project("DATACOUCH", "Couchbase", COMMONS); + CASSANDRA = new Project("DATACASS", "Cassandra", COMMONS); + ELASTICSEARCH = new Project("DATAES", "Elasticsearch", COMMONS); + REDIS = new Project("DATAREDIS", "Redis"); + GEMFIRE = new Project("SGF", "Gemfire", COMMONS); + + REST = new Project("DATAREST", "REST", COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE); + + PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, + GEMFIRE, REST); + } +} diff --git a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java index f0bb297..4ddfd28 100644 --- a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java +++ b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java @@ -15,6 +15,8 @@ */ package org.springframework.data.release.model; +import static org.springframework.data.release.model.Projects.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -26,30 +28,9 @@ public class ReleaseTrains { public static final List TRAINS; public static final Train CODD, DIJKSTRA, EVANS, FOWLER; - public static final Project COMMONS, BUILD, REST; - - private static final Project JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, GEMFIRE; - private static final List PROJECTS; static { - BUILD = new Project("DATABUILD", "Build"); - COMMONS = new Project("DATACMNS", "Commons", BUILD); - JPA = new Project("DATAJPA", "JPA", COMMONS); - MONGO_DB = new Project("DATAMONGO", "MongoDB", COMMONS); - NEO4J = new Project("DATAGRAPH", "Neo4j", COMMONS); - SOLR = new Project("DATASOLR", "Solr", COMMONS); - COUCHBASE = new Project("DATACOUCH", "Couchbase", COMMONS); - CASSANDRA = new Project("DATACASS", "Cassandra", COMMONS); - ELASTICSEARCH = new Project("DATAES", "Elasticsearch", COMMONS); - REDIS = new Project("DATAREDIS", "Redis"); - GEMFIRE = new Project("SGF", "Gemfire", COMMONS); - - REST = new Project("DATAREST", "REST", COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE); - - PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, - GEMFIRE, REST); - CODD = codd(); DIJKSTRA = dijkstra(); EVANS = DIJKSTRA.next("Evans", Transition.MINOR); diff --git a/src/main/java/org/springframework/data/release/model/Tracker.java b/src/main/java/org/springframework/data/release/model/Tracker.java new file mode 100644 index 0000000..4383523 --- /dev/null +++ b/src/main/java/org/springframework/data/release/model/Tracker.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +/** + * @author Oliver Gierke + */ +public enum Tracker { + + JIRA, GITHUB; +} diff --git a/src/main/java/org/springframework/data/release/model/Train.java b/src/main/java/org/springframework/data/release/model/Train.java index 758df5b..7f06b5b 100644 --- a/src/main/java/org/springframework/data/release/model/Train.java +++ b/src/main/java/org/springframework/data/release/model/Train.java @@ -15,15 +15,20 @@ */ package org.springframework.data.release.model; +import static org.springframework.data.release.model.Iteration.*; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; +import lombok.EqualsAndHashCode; +import lombok.ToString; import lombok.Value; import org.springframework.shell.support.util.OsUtils; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -107,7 +112,7 @@ public class Train implements Iterable { return getModuleIterations(iteration, new Project[0]); } - public Iterable getModuleIterations(Iteration iteration, Project... exclusions) { + List getModuleIterations(Iteration iteration, Project... exclusions) { List iterations = new ArrayList<>(modules.size()); List exclusionList = Arrays.asList(exclusions); @@ -144,4 +149,55 @@ public class Train implements Iterable { return builder.toString(); } + + /** + * Value object to represent a set of {@link Iteration}s. + * + * @author Oliver Gierke + */ + @EqualsAndHashCode + @ToString + private static class Iterations implements Iterable { + + public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4); + + private final List iterations; + + /** + * Creates a new {@link Iterations} from the given {@link Iteration}. + * + * @param iterations + */ + Iterations(Iteration... iterations) { + this.iterations = Arrays.asList(iterations); + } + + /** + * Returns the iteration with the given name. + * + * @param name must not be {@literal null} or empty. + * @return + */ + Iteration getIterationByName(String name) { + + Assert.hasText(name, "Name must not be null or empty!"); + + for (Iteration iteration : this) { + if (iteration.getName().equalsIgnoreCase(name)) { + return iteration; + } + } + + return null; + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return iterations.iterator(); + } + } } diff --git a/src/main/java/org/springframework/data/release/model/TrainIteration.java b/src/main/java/org/springframework/data/release/model/TrainIteration.java new file mode 100644 index 0000000..b00372c --- /dev/null +++ b/src/main/java/org/springframework/data/release/model/TrainIteration.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import java.util.Iterator; + +import lombok.Value; + +/** + * @author Oliver Gierke + */ +@Value +public class TrainIteration implements Iterable { + + private final Train train; + private final Iteration iteration; + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return train.getModuleIterations(iteration).iterator(); + } + + public ArtifactVersion getModuleVersion(Project project) { + return train.getModuleVersion(project, iteration); + } + + public ModuleIteration getModule(String name) { + return train.getModuleIteration(iteration, name); + } + + public ModuleIteration getModule(Project project) { + return train.getModuleIteration(iteration, project.getName()); + } + + public Iterable getModulesExcept(Project... exclusions) { + return train.getModuleIterations(iteration, exclusions); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("%s - %s", train.getName(), iteration.getName()); + } +} diff --git a/src/main/java/org/springframework/data/release/utils/Logger.java b/src/main/java/org/springframework/data/release/utils/Logger.java new file mode 100644 index 0000000..94c854c --- /dev/null +++ b/src/main/java/org/springframework/data/release/utils/Logger.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014 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.utils; + +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.shell.support.logging.HandlerUtils; +import org.springframework.stereotype.Component; + +/** + * @author Oliver Gierke + */ +@Component +public class Logger { + + private static final String PREFIX_TEMPLATE = "%s > %s"; + + private final java.util.logging.Logger LOGGER = HandlerUtils.getLogger(getClass()); + + public void log(ModuleIteration module, String template, Object... args) { + log(module.getProject(), template, args); + } + + public void log(Project project, String template, Object... args) { + log(project.getName(), template, args); + } + + public void log(TrainIteration iteration, String template, Object... args) { + log(iteration.toString(), template, args); + } + + private void log(String context, String template, Object... args) { + LOGGER.info(String.format(PREFIX_TEMPLATE, context, String.format(template, args))); + } +} diff --git a/src/test/java/org/springframework/data/release/ArtifactUnitTests.java b/src/test/java/org/springframework/data/release/ArtifactUnitTests.java new file mode 100644 index 0000000..32af713 --- /dev/null +++ b/src/test/java/org/springframework/data/release/ArtifactUnitTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 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; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.release.maven.Artifact; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Iteration; +import org.springframework.data.release.model.ReleaseTrains; + +/** + * @author Oliver Gierke + */ +public class ArtifactUnitTests { + + @Test + public void testname() { + + Artifact artifact = new Artifact(ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.M1, "JPA")); + + assertThat(artifact.getArtifactId(), is("spring-data-jpa")); + assertThat(artifact.getVersion(), is(ArtifactVersion.parse("1.6.0.M1"))); + assertThat(artifact.getNextDevelopmentVersion(), is(ArtifactVersion.parse("1.6.0.BUILD-SNAPSHOT"))); + } +} diff --git a/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java b/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java index 1e9af7c..a2cb2ca 100644 --- a/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java @@ -15,6 +15,8 @@ */ package org.springframework.data.release.git; +import static org.springframework.data.release.model.Projects.*; + import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -39,7 +41,7 @@ public class GitOperationsIntegrationTests extends AbstractIntegrationTests { @Ignore public void showTags() throws Exception { - Tags tags = gitOperations.getTags(ReleaseTrains.COMMONS); + Tags tags = gitOperations.getTags(COMMONS); System.out.println(StringUtils.collectionToDelimitedString(tags.asList(), "\n")); } } diff --git a/src/test/java/org/springframework/data/release/model/IterationUnitTests.java b/src/test/java/org/springframework/data/release/model/IterationUnitTests.java new file mode 100644 index 0000000..5277b87 --- /dev/null +++ b/src/test/java/org/springframework/data/release/model/IterationUnitTests.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +/** + * + * @author Oliver Gierke + */ +public class IterationUnitTests { + +} diff --git a/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java b/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java new file mode 100644 index 0000000..a6f1b7b --- /dev/null +++ b/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * @author Oliver Gierke + */ +public class ModuleIterationUnitTests { + + @Test + public void abbreviatesTrailingZerosForNonServiceReleases() { + + TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.M1); + ModuleIteration module = iteration.getModule(Projects.JPA); + + assertThat(module.getVersionString(), is("1.6 M1")); + } + + @Test + public void doesNotListIterationSuffixForServiceReleases() { + + TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.SR1); + ModuleIteration module = iteration.getModule(Projects.JPA); + + assertThat(module.getVersionString(), is("1.6.1")); + } +}