From 6b2fea839a1ec88a23f67483e93a4f512faa1680 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 20 Nov 2015 12:34:15 +0100 Subject: [PATCH] Significant Java 8 updates and Spring Boot integration. Moved to Spring Boot 1.3 and leverage the new configuration properties mechanism (see GitProperties and IoProperties). Improved ArtifactVersion to be a better value object. Introduced usage of Java 8's CompletableFuture to execute most of the Git operations in parallel. Started to use Lambdas where possible. Git configuration is now supposed to be done using a application-local.properties (Git ignored) file containing the relevant properties (see readme). The test configuration now uses a separate workspace location and a Sample release train to make sure the integration tests run without interfering a local workspace. Added train declaration for Hopper. Integrated BootShim (see [0], [1]) to run the shell on top of Spring Boot. [0] https://github.com/jeffellin/springshellwithboot [1] https://github.com/spring-projects/spring-shell/issues/34 --- infrastructure.properties | 5 - pom.xml | 23 ++- readme.md | 9 +- .../data/release/Application.java | 44 ++++++ .../data/release/BootShim.java | 126 ++++++++++++++++ ...OsConfiguration.java => CliComponent.java} | 19 ++- .../announcement/AnnouncementOperations.java | 2 +- ...ommands.java => IssueTrackerCommands.java} | 32 +++-- .../data/release/cli/ModelCommands.java | 21 +-- .../data/release/cli/ReleaseCommands.java | 32 ++--- .../data/release/cli/StaticResources.java | 2 +- .../data/release/cli/TrainConverter.java | 1 - .../release/docs/DocumentationOperations.java | 4 +- .../data/release/git/GitCommands.java | 12 +- .../data/release/git/GitOperations.java | 136 +++++++----------- .../data/release/git/GitProperties.java | 60 ++++++++ .../springframework/data/release/git/Tag.java | 33 ++++- .../git/{Tags.java => VersionTags.java} | 24 ++-- .../io/CommonsExecOsCommandOperations.java | 4 +- .../data/release/io/IoProperties.java | 46 ++++++ .../data/release/io/Workspace.java | 15 +- .../data/release/jira/Changelog.java | 2 +- .../data/release/jira/GitHubIssueTracker.java | 34 ++--- .../data/release/jira/Jira.java | 26 ++-- .../data/release/maven/Artifact.java | 2 +- .../data/release/maven/MavenOperations.java | 33 ++--- .../data/release/maven/MavenProject.java | 2 +- .../data/release/maven/Repository.java | 4 +- .../data/release/model/ArtifactVersion.java | 54 +++++-- .../data/release/model/Iteration.java | 12 +- .../data/release/model/Module.java | 12 +- .../data/release/model/ModuleIteration.java | 2 +- .../data/release/model/Projects.java | 9 +- .../data/release/model/ReleaseTrains.java | 7 +- .../data/release/model/Train.java | 111 +++++++------- .../data/release/model/Version.java | 6 +- .../data/release/utils/CommandUtils.java | 57 -------- .../data/release/utils/ExecutionUtils.java | 82 +++++++++++ .../data/release/utils/Logger.java | 5 + .../META-INF/spring/spring-shell-plugin.xml | 10 -- src/main/resources/application.properties | 1 + src/main/resources/logback.xml | 16 --- .../release/AbstractIntegrationTests.java | 10 +- .../data/release/ArtifactUnitTests.java | 4 +- .../cli/ReleaseCommandsIntegrationTests.java | 4 +- .../data/release/git/BranchUnitTests.java | 4 +- .../git/GitOperationsIntegrationTests.java | 22 +-- .../data/release/git/GitProjectUnitTests.java | 19 ++- .../git/GitPropertiesIntegrationTests.java | 37 +++++ ...java => IoPropertiesIntegrationTests.java} | 30 +--- .../release/maven/MavenIntegrationTests.java | 6 +- .../model/ArtifactVersionUnitTests.java | 68 ++++++--- .../TestReleaseTrains.java} | 14 +- .../data/release/model/TrainsUnitTest.java | 42 ++++++ .../resources/application-test.properties | 3 + 55 files changed, 900 insertions(+), 500 deletions(-) delete mode 100644 infrastructure.properties create mode 100644 src/main/java/org/springframework/data/release/Application.java create mode 100644 src/main/java/org/springframework/data/release/BootShim.java rename src/main/java/org/springframework/data/release/{io/OsConfiguration.java => CliComponent.java} (59%) rename src/main/java/org/springframework/data/release/cli/{IssueTracerCommands.java => IssueTrackerCommands.java} (76%) create mode 100644 src/main/java/org/springframework/data/release/git/GitProperties.java rename src/main/java/org/springframework/data/release/git/{Tags.java => VersionTags.java} (79%) create mode 100644 src/main/java/org/springframework/data/release/io/IoProperties.java delete mode 100644 src/main/java/org/springframework/data/release/utils/CommandUtils.java create mode 100644 src/main/java/org/springframework/data/release/utils/ExecutionUtils.java delete mode 100644 src/main/resources/META-INF/spring/spring-shell-plugin.xml create mode 100644 src/main/resources/application.properties delete mode 100644 src/main/resources/logback.xml create mode 100644 src/test/java/org/springframework/data/release/git/GitPropertiesIntegrationTests.java rename src/test/java/org/springframework/data/release/io/{CommonsExecOsCommandOperationsIntegegrationTests.java => IoPropertiesIntegrationTests.java} (51%) rename src/test/java/org/springframework/data/release/{TestConfig.java => model/TestReleaseTrains.java} (66%) create mode 100644 src/test/java/org/springframework/data/release/model/TrainsUnitTest.java create mode 100644 src/test/resources/application-test.properties diff --git a/infrastructure.properties b/infrastructure.properties deleted file mode 100644 index 9f5bb8a..0000000 --- a/infrastructure.properties +++ /dev/null @@ -1,5 +0,0 @@ -io.workDir=~/temp/spring-data-shell - -git.username=olivergierke -git.author=Oliver Gierke -git.email=ogierke@pivotal.io \ No newline at end of file diff --git a/pom.xml b/pom.xml index a36135b..bc65060 100644 --- a/pom.xml +++ b/pom.xml @@ -8,16 +8,21 @@ org.springframework.boot spring-boot-starter-parent - 1.2.4.RELEASE + 1.3.0.RELEASE 1.8 - org.springframework.shell.Bootstrap + org.springframework.data.release.Application + + org.springframework.boot + spring-boot-starter + + org.springframework.boot spring-boot-starter-logging @@ -72,13 +77,18 @@ org.springframework.boot spring-boot-starter-test - + org.eclipse.jgit org.eclipse.jgit 4.0.1.201506240215-r + + org.springframework.boot + spring-boot-configuration-processor + true + @@ -93,8 +103,13 @@ - org.springframework.shell.Bootstrap + ${jar.mainclass} spring-data-release-shell + + + git.password=${git.password} + + diff --git a/readme.md b/readme.md index 3e12c42..8d34a9a 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,8 @@ -`mvn package appassembler:assemble && sh target/appassembler/bin/spring-data-release-shell` \ No newline at end of file +1. Add an `application-local.properties` to the project root and add the following properties: + +- `git.username` - Your GitHub username. +- `git.password` - Your GitHub password. +- `git.author` - Your full name (used for preparing commits). +- `git.email` - Your email (used for preparing commits). + +2. Run `mvn package appassembler:assemble && sh target/appassembler/bin/spring-data-release-shell` diff --git a/src/main/java/org/springframework/data/release/Application.java b/src/main/java/org/springframework/data/release/Application.java new file mode 100644 index 0000000..a9e921c --- /dev/null +++ b/src/main/java/org/springframework/data/release/Application.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 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 java.util.logging.Logger; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.shell.support.logging.HandlerUtils; + +/** + * @author Oliver Gierke + */ +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + + SpringApplication application = new SpringApplication(Application.class); + application.setAdditionalProfiles("local"); + + try { + BootShim bs = new BootShim(args, application.run(args)); + bs.run(); + } catch (RuntimeException e) { + throw e; + } finally { + HandlerUtils.flushAllHandlers(Logger.getLogger("")); + } + } +} diff --git a/src/main/java/org/springframework/data/release/BootShim.java b/src/main/java/org/springframework/data/release/BootShim.java new file mode 100644 index 0000000..91b0b60 --- /dev/null +++ b/src/main/java/org/springframework/data/release/BootShim.java @@ -0,0 +1,126 @@ +package org.springframework.data.release; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.shell.CommandLine; +import org.springframework.shell.ShellException; +import org.springframework.shell.SimpleShellCommandLineOptions; +import org.springframework.shell.core.ExitShellRequest; +import org.springframework.shell.core.JLineShellComponent; +import org.springframework.shell.support.logging.HandlerUtils; +import org.springframework.util.StopWatch; + +public class BootShim { + private static BootShim bootstrap; + private static StopWatch sw = new StopWatch("Spring Shell"); + private static CommandLine commandLine; + private ConfigurableApplicationContext ctx; + + public BootShim(String[] args, ConfigurableApplicationContext context) { + this.ctx = context; + + try { + commandLine = SimpleShellCommandLineOptions.parseCommandLine(args); + } catch (IOException var5) { + throw new ShellException(var5.getMessage(), var5); + } + + this.configureApplicationContext(this.ctx); + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) this.ctx); + if (commandLine.getDisableInternalCommands()) { + scanner.scan(new String[] { "org.springframework.shell.converters", "org.springframework.shell.plugin.support" }); + } else { + scanner.scan(new String[] { "org.springframework.shell.commands", "org.springframework.shell.converters", + "org.springframework.shell.plugin.support" }); + } + + } + + public ApplicationContext getApplicationContext() { + return this.ctx; + } + + private void configureApplicationContext(ConfigurableApplicationContext annctx) { + this.createAndRegisterBeanDefinition(annctx, JLineShellComponent.class, "shell"); + annctx.getBeanFactory().registerSingleton("commandLine", commandLine); + } + + protected void createAndRegisterBeanDefinition(GenericApplicationContext annctx, Class clazz) { + this.createAndRegisterBeanDefinition(annctx, clazz, (String) null); + } + + protected void createAndRegisterBeanDefinition(ConfigurableApplicationContext annctx, Class clazz, String name) { + RootBeanDefinition rbd = new RootBeanDefinition(); + rbd.setBeanClass(clazz); + DefaultListableBeanFactory bf = (DefaultListableBeanFactory) annctx.getBeanFactory(); + if (name != null) { + bf.registerBeanDefinition(name, rbd); + } else { + bf.registerBeanDefinition(clazz.getSimpleName(), rbd); + } + + } + + private void setupLogging() { + Logger rootLogger = Logger.getLogger(""); + HandlerUtils.wrapWithDeferredLogHandler(rootLogger, Level.SEVERE); + Logger sfwLogger = Logger.getLogger("org.springframework"); + sfwLogger.setLevel(Level.WARNING); + Logger rooLogger = Logger.getLogger("org.springframework.shell"); + rooLogger.setLevel(Level.FINE); + } + + public ExitShellRequest run() { + sw.start(); + String[] commandsToExecuteAndThenQuit = commandLine.getShellCommandsToExecute(); + JLineShellComponent shell = (JLineShellComponent) this.ctx.getBean("shell", JLineShellComponent.class); + ExitShellRequest exitShellRequest; + if (null != commandsToExecuteAndThenQuit) { + boolean successful = false; + exitShellRequest = ExitShellRequest.FATAL_EXIT; + String[] arr$ = commandsToExecuteAndThenQuit; + int len$ = commandsToExecuteAndThenQuit.length; + + for (int i$ = 0; i$ < len$; ++i$) { + String cmd = arr$[i$]; + successful = shell.executeCommand(cmd).isSuccess(); + if (!successful) { + break; + } + } + + if (successful) { + exitShellRequest = ExitShellRequest.NORMAL_EXIT; + } + } else { + shell.start(); + shell.promptLoop(); + exitShellRequest = shell.getExitShellRequest(); + if (exitShellRequest == null) { + exitShellRequest = ExitShellRequest.NORMAL_EXIT; + } + + shell.waitForComplete(); + } + + sw.stop(); + if (shell.isDevelopmentMode()) { + System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms"); + } + + return exitShellRequest; + } + + public JLineShellComponent getJLineShellComponent() { + return (JLineShellComponent) this.ctx.getBean("shell", JLineShellComponent.class); + } +} diff --git a/src/main/java/org/springframework/data/release/io/OsConfiguration.java b/src/main/java/org/springframework/data/release/CliComponent.java similarity index 59% rename from src/main/java/org/springframework/data/release/io/OsConfiguration.java rename to src/main/java/org/springframework/data/release/CliComponent.java index 77d5416..8e2fa2c 100644 --- a/src/main/java/org/springframework/data/release/io/OsConfiguration.java +++ b/src/main/java/org/springframework/data/release/CliComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2015 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,16 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.io; +package org.springframework.data.release; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; /** * @author Oliver Gierke */ -@Configuration -@PropertySource("file:infrastructure.properties") -class OsConfiguration { +@Component +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface CliComponent { } 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 e504ae5..41c6b47 100644 --- a/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java +++ b/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java @@ -79,6 +79,6 @@ public class AnnouncementOperations { public static void main(String[] args) { AnnouncementOperations operations = new AnnouncementOperations(); - System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.FOWLER, Iteration.SR1))); + System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.GOSLING, Iteration.SR1))); } } diff --git a/src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java b/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java similarity index 76% rename from src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java rename to src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java index 15268dc..f2c0ebd 100644 --- a/src/main/java/org/springframework/data/release/cli/IssueTracerCommands.java +++ b/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java @@ -15,26 +15,30 @@ */ package org.springframework.data.release.cli; +import java.util.stream.Collectors; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; +import org.springframework.data.release.CliComponent; +import org.springframework.data.release.jira.Changelog; 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.ModuleIteration; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.utils.ExecutionUtils; 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; -import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; /** * @author Oliver Gierke */ -@Component -public class IssueTracerCommands implements CommandMarker { +@CliComponent +public class IssueTrackerCommands implements CommandMarker { private final PluginRegistry tracker; private final JiraConnector jira; @@ -45,7 +49,8 @@ public class IssueTracerCommands implements CommandMarker { * @param environment */ @Autowired - public IssueTracerCommands(PluginRegistry tracker, JiraConnector jira, Environment environment) { + public IssueTrackerCommands(PluginRegistry tracker, JiraConnector jira, + Environment environment) { String username = environment.getProperty("jira.username", (String) null); String password = environment.getProperty("jira.password", (String) null); @@ -61,9 +66,9 @@ public class IssueTracerCommands implements CommandMarker { } @CliCommand(value = "jira tickets") - public String jira( - @CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "for-current-user", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") boolean forCurrentUser) { + public String jira(@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!"; @@ -82,14 +87,11 @@ public class IssueTracerCommands implements CommandMarker { return tracker.getPluginFor(module.getProject()).getChangelogFor(module).toString(); } - StringBuilder builder = new StringBuilder(); + return ExecutionUtils.runAndReturn(iteration, this::getChangelog).// + stream().map(it -> it.toString()).collect(Collectors.joining("\n")); + } - for (ModuleIteration module : iteration) { - - IssueTracker issues = tracker.getPluginFor(module.getProject()); - builder.append(issues.getChangelogFor(module)).append("\n"); - } - - return builder.toString(); + private Changelog getChangelog(ModuleIteration module) { + return tracker.getPluginFor(module.getProject()).getChangelogFor(module); } } 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 522e3b1..ebad2ad 100644 --- a/src/main/java/org/springframework/data/release/cli/ModelCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ModelCommands.java @@ -15,36 +15,25 @@ */ package org.springframework.data.release.cli; -import java.util.ArrayList; -import java.util.List; +import java.util.stream.Collectors; +import org.springframework.data.release.CliComponent; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; /** * @author Oliver Gierke */ -@Component +@CliComponent public class ModelCommands implements CommandMarker { @CliCommand("trains") public String train(@CliOption(key = { "", "train" }) Train train) { - if (train != null) { - return train.toString(); - } - - List names = new ArrayList<>(); - - for (Train releaseTrain : ReleaseTrains.TRAINS) { - names.add(releaseTrain.getName()); - } - - return StringUtils.collectionToDelimitedString(names, ", "); + return train != null ? train.toString() + : ReleaseTrains.TRAINS.stream().map(Train::getName).collect(Collectors.joining(", ")); } } 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 ac4b94b..a8b4268 100644 --- a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -16,18 +16,17 @@ package org.springframework.data.release.cli; import static org.springframework.data.release.model.Projects.*; + import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.docs.DocumentationOperations; import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.git.Tags; +import org.springframework.data.release.git.VersionTags; import org.springframework.data.release.gradle.GradleOperations; 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.Module; import org.springframework.data.release.model.Phase; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; @@ -41,7 +40,7 @@ import org.springframework.stereotype.Component; * @author Oliver Gierke */ @Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class ReleaseCommands implements CommandMarker { private final MavenOperations maven; @@ -53,25 +52,16 @@ public class ReleaseCommands implements CommandMarker { @CliCommand("release predict") public String predictTrainAndIteration() throws Exception { - Pom pom = maven.getMavenProject(COMMONS); + return git.getTags(COMMONS).getLatest().toArtifactVersion().// + map(this::getTrainNameForCommonsVersion).// + orElse(null); + } - Tags tags = git.getTags(COMMONS); + public String getTrainNameForCommonsVersion(ArtifactVersion version) { - ArtifactVersion version = tags.getLatest().toArtifactVersion(); - System.out.println(version); - - for (Train train : ReleaseTrains.TRAINS) { - - Module module = train.getModule(COMMONS); - - if (!pom.getVersion().toString().startsWith(module.getVersion().toMajorMinorBugfix())) { - continue; - } - - return train.getName(); - } - - return null; + return ReleaseTrains.TRAINS.stream().// + filter(train -> version.toString().startsWith(train.getModule(COMMONS).getVersion().toString())).// + findFirst().map(Train::getName).orElse(null); } /** diff --git a/src/main/java/org/springframework/data/release/cli/StaticResources.java b/src/main/java/org/springframework/data/release/cli/StaticResources.java index a2bb509..863136f 100644 --- a/src/main/java/org/springframework/data/release/cli/StaticResources.java +++ b/src/main/java/org/springframework/data/release/cli/StaticResources.java @@ -34,7 +34,7 @@ public class StaticResources { public StaticResources(ModuleIteration module) { Project project = module.getProject(); - ArtifactVersion version = ArtifactVersion.from(module); + ArtifactVersion version = ArtifactVersion.of(module); this.baseUrl = String.format(URL_TEMPLATE, project.getName().toLowerCase(), version); } diff --git a/src/main/java/org/springframework/data/release/cli/TrainConverter.java b/src/main/java/org/springframework/data/release/cli/TrainConverter.java index 5ab2deb..b33b3bd 100644 --- a/src/main/java/org/springframework/data/release/cli/TrainConverter.java +++ b/src/main/java/org/springframework/data/release/cli/TrainConverter.java @@ -56,7 +56,6 @@ public class TrainConverter implements Converter { @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/docs/DocumentationOperations.java b/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java index 584cf89..2ac390d 100644 --- a/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java +++ b/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java @@ -22,7 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.git.GitOperations; import org.springframework.data.release.git.GitProject; import org.springframework.data.release.git.Tag; -import org.springframework.data.release.git.Tags; +import org.springframework.data.release.git.VersionTags; import org.springframework.data.release.io.Workspace; import org.springframework.data.release.io.Workspace.LineCallback; import org.springframework.data.release.model.ModuleIteration; @@ -44,7 +44,7 @@ public class DocumentationOperations { public void updateDockbookIncludes(TrainIteration iteration) throws Exception { - Tags tags = git.getTags(COMMONS); + VersionTags tags = git.getTags(COMMONS); ModuleIteration commons = iteration.getModule(COMMONS); ModuleIteration previousIteration = iteration.getPreviousIteration(commons); diff --git a/src/main/java/org/springframework/data/release/git/GitCommands.java b/src/main/java/org/springframework/data/release/git/GitCommands.java index db07d85..62f4cfb 100644 --- a/src/main/java/org/springframework/data/release/git/GitCommands.java +++ b/src/main/java/org/springframework/data/release/git/GitCommands.java @@ -18,6 +18,7 @@ package org.springframework.data.release.git; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.CliComponent; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; @@ -25,14 +26,13 @@ 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; -import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; /** * @author Oliver Gierke */ -@Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@CliComponent +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class GitCommands implements CommandMarker { private final GitOperations git; @@ -43,8 +43,8 @@ public class GitCommands implements CommandMarker { } @CliCommand("git update") - public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception, - InterruptedException { + public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName) + throws Exception, InterruptedException { git.update(ReleaseTrains.getTrainByName(trainName)); } @@ -84,7 +84,7 @@ public class GitCommands implements CommandMarker { public void push(// @CliOption(key = "", mandatory = true) TrainIteration iteration, // @CliOption(key = "tags", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") String tags) - throws Exception { + throws Exception { boolean pushTags = Boolean.parseBoolean(tags); 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 b49ad9f..9db5f6b 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -19,12 +19,8 @@ import lombok.RequiredArgsConstructor; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Future; import java.util.stream.Collectors; - -import javax.annotation.PostConstruct; +import java.util.stream.StreamSupport; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; @@ -36,31 +32,25 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; -import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.TagOpt; -import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.data.release.io.CommandResult; 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.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; -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.data.release.model.TrainIteration; -import org.springframework.data.release.utils.CommandUtils; +import org.springframework.data.release.utils.ExecutionUtils; import org.springframework.data.release.utils.Logger; import org.springframework.plugin.core.PluginRegistry; import org.springframework.stereotype.Component; import org.springframework.util.Assert; /** - * Component to execut Git related operations. + * Component to execute Git related operations. * * @author Oliver Gierke */ @@ -72,15 +62,7 @@ public class GitOperations { private final Workspace workspace; private final Logger logger; private final PluginRegistry issueTracker; - private final Environment environment; - - private CredentialsProvider credentials; - - @PostConstruct - public void init() { - this.credentials = new UsernamePasswordCredentialsProvider(environment.getProperty("git.username"), - environment.getProperty("git.password")); - } + private final GitProperties gitProperties; public GitProject getGitProject(Project project) { return new GitProject(project, server); @@ -96,7 +78,7 @@ public class GitOperations { Assert.notNull(train, "Train must not be null!"); - for (ModuleIteration module : train) { + ExecutionUtils.run(train, module -> { Branch branch = Branch.from(module); @@ -109,24 +91,23 @@ public class GitOperations { setRef("origin/".concat(branch.toString())).// call(); } - } + }); } /** - * Checks out all projects of the given {@link Train} at the tags for the given {@link Iteration}. + * Checks out all projects of the given {@link TrainIteration}. * - * @param train * @param iteration * @throws Exception */ - public void checkout(TrainIteration iteration) throws Exception { + public void checkout(TrainIteration iteration) { update(iteration.getTrain()); - for (ModuleIteration module : iteration) { + ExecutionUtils.run(iteration, module -> { Project project = module.getProject(); - ArtifactVersion artifactVersion = ArtifactVersion.from(module); + ArtifactVersion artifactVersion = ArtifactVersion.of(module); Tag tag = findTagFor(project, artifactVersion); @@ -138,16 +119,17 @@ public class GitOperations { try (Git git = new Git(getRepository(module.getProject()))) { logger.log(module, "git checkout %s", tag); + git.checkout().setStartPoint(tag.toString()); } - } + }); logger.log(iteration, "Successfully checked out projects."); } public void prepare(TrainIteration iteration) throws Exception { - for (ModuleIteration module : iteration) { + ExecutionUtils.run(iteration, module -> { Branch branch = Branch.from(module); @@ -161,28 +143,18 @@ public class GitOperations { setRebase(true).// call(); } - } + }); } - public void update(Train train) throws Exception { - - List> executions = new ArrayList<>(); - - for (Module module : train) { - update(module.getProject()); - } - - for (Future execution : executions) { - CommandUtils.getCommandResult(execution); - } + public void update(Train train) { + ExecutionUtils.run(train, module -> update(module.getProject())); } - public void push(TrainIteration iteration) throws Exception { + public void push(TrainIteration iteration) { - for (ModuleIteration module : iteration) { + ExecutionUtils.run(iteration, module -> { Branch branch = Branch.from(module); - logger.log(module, "git push origin %s", branch); try (Git git = new Git(getRepository(module.getProject()))) { @@ -192,15 +164,15 @@ public class GitOperations { git.push().// setRemote("origin").// setRefSpecs(new RefSpec(ref.getName())).// - setCredentialsProvider(credentials).// + setCredentialsProvider(gitProperties.getCredentials()).// call(); } - } + }); } - public void pushTags(Train train) throws Exception { + public void pushTags(Train train) { - for (Module module : train) { + ExecutionUtils.run(train.getModules(), module -> { logger.log(module.getProject(), "git push --tags"); @@ -209,10 +181,10 @@ public class GitOperations { git.push().// setRemote("origin").// setPushTags().// - setCredentialsProvider(this.credentials).// + setCredentialsProvider(gitProperties.getCredentials()).// call(); } - } + }); } public void update(Project project) throws Exception { @@ -220,9 +192,7 @@ public class GitOperations { GitProject gitProject = new GitProject(project, server); String repositoryName = gitProject.getRepositoryName(); - Repository repository = getRepository(project); - - try (Git git = new Git(repository)) { + try (Git git = new Git(getRepository(project))) { if (workspace.hasProjectDirectory(project)) { @@ -232,35 +202,34 @@ public class GitOperations { checkout(project, Branch.MASTER); git.reset().setMode(ResetType.HARD).call(); - git.fetch().setTagOpt(TagOpt.FETCH_TAGS); - git.pull().call(); - // return os.executeCommand("git checkout master && git reset --hard && git fetch --tags && git pull origin - // master", - // project); - } else { logger.log(project, "No repository found! Cloning from %s…", gitProject.getProjectUri()); - clone(project); - // return os.executeCommand(command); + clone(project); } } } - public Tags getTags(Project project) throws Exception { + public VersionTags getTags(Project project) { try (Git git = new Git(getRepository(project))) { - return new Tags(git.tagList().call().stream().map(ref -> new Tag(ref.getName())).collect(Collectors.toList())); + + return new VersionTags(git.tagList().call().stream().// + map(ref -> Tag.of(ref.getName())).// + collect(Collectors.toList())); + + } catch (Exception o_O) { + throw new RuntimeException(o_O); } } - public void tagRelease(TrainIteration iteration) throws Exception { + public void tagRelease(TrainIteration iteration) { - for (ModuleIteration module : iteration) { + ExecutionUtils.run(iteration, module -> { Branch branch = Branch.from(module); Project project = module.getProject(); @@ -284,7 +253,7 @@ public class GitOperations { git.tag().setName(tag.toString()).setObjectId(commit).call(); } } - } + }); } /** @@ -301,19 +270,17 @@ public class GitOperations { Assert.notNull(iteration, "Train iteration must not be null!"); Assert.hasText(summary, "Summary must not be null or empty!"); - for (ModuleIteration module : iteration) { - commit(module, expandSummary(summary, module, iteration), details); - } + ExecutionUtils.run(iteration, module -> commit(module, expandSummary(summary, module, iteration), details)); } - private String expandSummary(String summary, ModuleIteration module, TrainIteration iteration) { + private static String expandSummary(String summary, ModuleIteration module, TrainIteration iteration) { if (!summary.contains("%s")) { return summary; } return String.format(summary, - ArtifactVersion.from(module).toString().concat(String.format(" (%s)", iteration.toString()))); + ArtifactVersion.of(module).toString().concat(String.format(" (%s)", iteration.toString()))); } /** @@ -336,8 +303,8 @@ public class GitOperations { Ticket ticket = tracker.getReleaseTicketFor(module); Commit commit = new Commit(ticket, summary, details); - String author = environment.getProperty("git.author"); - String email = environment.getProperty("git.email"); + String author = gitProperties.getAuthor(); + String email = gitProperties.getEmail(); logger.log(module, "git commit -m \"%s\" --author=\"%s <%s>\"", commit, author, email); @@ -382,16 +349,12 @@ public class GitOperations { * @return * @throws IOException */ - private Tag findTagFor(Project project, ArtifactVersion version) throws Exception { + private Tag findTagFor(Project project, ArtifactVersion version) { - for (Tag tag : getTags(project)) { - - if (tag.toArtifactVersion().equals(version)) { - return tag; - } - } - - return null; + return StreamSupport.stream(getTags(project).spliterator(), false).// + filter(tag -> tag.toArtifactVersion().map(it -> it.equals(version)).orElse(false)).// + findFirst().orElseThrow(() -> new IllegalArgumentException( + String.format("No tag found for version %s of project %s!", version, project))); } public void checkout(Project project, Branch branch) throws Exception { @@ -409,10 +372,11 @@ public class GitOperations { } checkout.call(); + } } - private Repository getRepository(Project project) throws Exception { + private Repository getRepository(Project project) throws IOException { return FileRepositoryBuilder.create(workspace.getFile(".git", project)); } diff --git a/src/main/java/org/springframework/data/release/git/GitProperties.java b/src/main/java/org/springframework/data/release/git/GitProperties.java new file mode 100644 index 0000000..fa73c59 --- /dev/null +++ b/src/main/java/org/springframework/data/release/git/GitProperties.java @@ -0,0 +1,60 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.git; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; + +import javax.annotation.PostConstruct; + +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +/** + * Configurable properties for Git. + * + * @author Oliver Gierke + */ +@Data +@Component +@ConfigurationProperties(prefix = "git") +public class GitProperties { + + private @Getter(AccessLevel.PRIVATE) String username, password; + private String author, email; + + @PostConstruct + public void init() { + + Assert.hasText(username, "No GitHub username (git.username) configured!"); + Assert.hasText(username, "No GitHub password (git.password) configured!"); + Assert.hasText(username, "No Git author (git.author) configured!"); + Assert.hasText(username, "No Git email (git.email) configured!"); + } + + /** + * Returns the jGit {@link CredentialsProvider} to be used. + * + * @return + */ + public CredentialsProvider getCredentials() { + return new UsernamePasswordCredentialsProvider(username, password); + } +} diff --git a/src/main/java/org/springframework/data/release/git/Tag.java b/src/main/java/org/springframework/data/release/git/Tag.java index c09210f..c0459b6 100644 --- a/src/main/java/org/springframework/data/release/git/Tag.java +++ b/src/main/java/org/springframework/data/release/git/Tag.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -15,9 +15,12 @@ */ package org.springframework.data.release.git; +import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; +import java.util.Optional; + import org.springframework.data.release.model.ArtifactVersion; /** @@ -25,12 +28,19 @@ import org.springframework.data.release.model.ArtifactVersion; * * @author Oliver Gierke */ -@RequiredArgsConstructor @EqualsAndHashCode +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class Tag implements Comparable { private final String name; + public static Tag of(String source) { + + int slashIndex = source.lastIndexOf('/'); + + return new Tag(source.substring(slashIndex == -1 ? 0 : slashIndex + 1)); + } + /** * Returns the part of the name of the tag that is suitable to derive a version from the tag. Will transparently strip * a {@code v} prefix from the name. @@ -41,8 +51,17 @@ public class Tag implements Comparable { return name.startsWith("v") ? name.substring(1) : name; } - public ArtifactVersion toArtifactVersion() { - return ArtifactVersion.parse(getVersionSource()); + public boolean isVersionTag() { + return toArtifactVersion().isPresent(); + } + + public Optional toArtifactVersion() { + + try { + return Optional.of(ArtifactVersion.of(getVersionSource())); + } catch (IllegalArgumentException o_O) { + return Optional.empty(); + } } /** @@ -70,6 +89,10 @@ public class Tag implements Comparable { */ @Override public int compareTo(Tag that) { - return that.name.compareTo(this.name); + + // Prefer artifact versions but fall back to name comparison + + return toArtifactVersion().map(left -> that.toArtifactVersion().map(right -> left.compareTo(right)).// + orElse(name.compareTo(that.name))).orElse(name.compareTo(that.name)); } } diff --git a/src/main/java/org/springframework/data/release/git/Tags.java b/src/main/java/org/springframework/data/release/git/VersionTags.java similarity index 79% rename from src/main/java/org/springframework/data/release/git/Tags.java rename to src/main/java/org/springframework/data/release/git/VersionTags.java index ec2fde6..635e3a0 100644 --- a/src/main/java/org/springframework/data/release/git/Tags.java +++ b/src/main/java/org/springframework/data/release/git/VersionTags.java @@ -15,12 +15,11 @@ */ package org.springframework.data.release.git; -import java.util.ArrayList; -import java.util.Collections; +import lombok.EqualsAndHashCode; + import java.util.Iterator; import java.util.List; - -import lombok.EqualsAndHashCode; +import java.util.stream.Collectors; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.ModuleIteration; @@ -32,23 +31,22 @@ import org.springframework.util.Assert; * @author Oliver Gierke */ @EqualsAndHashCode -public class Tags implements Iterable { +public class VersionTags implements Iterable { private final List tags; /** - * Creates a new {@link Tags} instance for the given {@link List} of {@link Tag}s. + * Creates a new {@link VersionTags} instance for the given {@link List} of {@link Tag}s. * * @param source must not be {@literal null}. */ - Tags(List source) { + VersionTags(List source) { Assert.notNull(source, "Tags must not be null!"); - List tags = new ArrayList<>(source); - Collections.sort(tags); - - this.tags = Collections.unmodifiableList(tags); + this.tags = source.stream().// + filter(Tag::isVersionTag).// + sorted().collect(Collectors.toList()); } /** @@ -57,11 +55,11 @@ public class Tags implements Iterable { * @return */ public Tag getLatest() { - return tags.get(0); + return tags.get(tags.size() - 1); } public Tag createTag(ModuleIteration iteration) { - return getLatest().createNew(ArtifactVersion.from(iteration)); + return getLatest().createNew(ArtifactVersion.of(iteration)); } /** 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 0f7cb2f..4eed8fa 100644 --- a/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java +++ b/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java @@ -24,8 +24,6 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; -import javax.annotation.PostConstruct; - import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecuteResultHandler; import org.apache.commons.exec.DefaultExecutor; @@ -140,7 +138,7 @@ class CommonsExecOsCommandOperations implements OsCommandOperations { * * @throws Exception */ - @PostConstruct + // @PostConstruct public void initialize() throws Exception { String javaHome = executeCommand("/usr/libexec/java_home -F -v 1.8 -a x86_64 -d64").get().getOutput(); diff --git a/src/main/java/org/springframework/data/release/io/IoProperties.java b/src/main/java/org/springframework/data/release/io/IoProperties.java new file mode 100644 index 0000000..aca10cd --- /dev/null +++ b/src/main/java/org/springframework/data/release/io/IoProperties.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 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.io; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author Oliver Gierke + */ +@Slf4j +@Data +@Component +@ConfigurationProperties(prefix = "io") +public class IoProperties { + + private String workDir; + + public void setWorkDir(String workDir) { + + this.workDir = workDir.replace("~", System.getProperty("user.home")); + log.info(String.format("Using %s as working directory!", workDir)); + } + + public File getWorkDir() { + return new File(workDir); + } +} diff --git a/src/main/java/org/springframework/data/release/io/Workspace.java b/src/main/java/org/springframework/data/release/io/Workspace.java index 34366bc..dfdeed3 100644 --- a/src/main/java/org/springframework/data/release/io/Workspace.java +++ b/src/main/java/org/springframework/data/release/io/Workspace.java @@ -15,6 +15,8 @@ */ package org.springframework.data.release.io; +import lombok.RequiredArgsConstructor; + import java.io.File; import java.io.IOException; import java.nio.charset.Charset; @@ -23,10 +25,7 @@ import java.util.Scanner; import javax.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; - import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; import org.springframework.data.release.model.Project; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -39,14 +38,12 @@ import com.google.common.io.Files; * @author Oliver Gierke */ @Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class Workspace { private static final Charset UTF_8 = Charset.forName("UTF-8"); - public static final String WORK_DIR_PROPERTY = "io.workDir"; - - private final Environment environment; + private final IoProperties ioProperties; /** * Returns the current working directory. @@ -54,9 +51,7 @@ public class Workspace { * @return */ public File getWorkingDirectory() { - - String workDir = environment.getProperty("io.workDir"); - return new File(workDir.replace("~", System.getProperty("user.home"))); + return ioProperties.getWorkDir(); } /** 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 facb97b..5c28990 100644 --- a/src/main/java/org/springframework/data/release/jira/Changelog.java +++ b/src/main/java/org/springframework/data/release/jira/Changelog.java @@ -44,7 +44,7 @@ public class Changelog { @Override public String toString() { - ArtifactVersion version = ArtifactVersion.from(module); + ArtifactVersion version = ArtifactVersion.of(module); String headline = String.format("Changes in version %s (%s)", version, new DateFormatter("YYYY-MM-dd").print(new Date(), Locale.US)); diff --git a/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java b/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java index 552ede0..540b1bb 100644 --- a/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java +++ b/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java @@ -15,14 +15,14 @@ */ package org.springframework.data.release.jira; +import lombok.RequiredArgsConstructor; + 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 java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; @@ -48,7 +48,7 @@ import org.springframework.web.util.UriTemplate; * @author Oliver Gierke */ @Component -@RequiredArgsConstructor(onConstructor = @_(@Autowired)) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) class GitHubIssueTracker implements IssueTracker { private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}"; @@ -68,13 +68,12 @@ class GitHubIssueTracker implements IssueTracker { @Cacheable("release-tickets") public Ticket getReleaseTicketFor(ModuleIteration module) { - for (GitHubIssue issue : getIssuesFor(module)) { - if (issue.isReleaseTicket(module)) { - return new Ticket(issue.getId(), issue.getTitle()); - } - } - - throw new IllegalArgumentException(String.format("Could not find a release ticket for %s!", module)); + return getIssuesFor(module).stream().// + filter(issue -> issue.isReleaseTicket(module)).// + findFirst().// + map(issue -> new Ticket(issue.getId(), issue.getTitle())).// + orElseThrow( + () -> new IllegalArgumentException(String.format("Could not find a release ticket for %s!", module))); } /* @@ -85,12 +84,9 @@ class GitHubIssueTracker implements IssueTracker { @Cacheable("changelogs") public Changelog getChangelogFor(ModuleIteration module) { - List issues = getIssuesFor(module); - List tickets = new ArrayList<>(issues.size()); - - for (GitHubIssue issue : issues) { - tickets.add(new Ticket(issue.getId(), issue.getTitle())); - } + List tickets = getIssuesFor(module).stream().// + map(issue -> new Ticket(issue.getId(), issue.getTitle())).// + collect(Collectors.toList()); logger.log(module, "Created changelog with %s entries.", tickets.size()); @@ -131,8 +127,8 @@ class GitHubIssueTracker implements IssueTracker { logger.log(module, "Looking up milestone from %s…", milestoneUri); - List exchange = operations.exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE, - parameters).getBody(); + List exchange = operations + .exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE, parameters).getBody(); GitHubMilestone milestone = null; diff --git a/src/main/java/org/springframework/data/release/jira/Jira.java b/src/main/java/org/springframework/data/release/jira/Jira.java index 6ac1e81..ca50090 100644 --- a/src/main/java/org/springframework/data/release/jira/Jira.java +++ b/src/main/java/org/springframework/data/release/jira/Jira.java @@ -15,6 +15,8 @@ */ package org.springframework.data.release.jira; +import lombok.RequiredArgsConstructor; + import java.net.URI; import java.util.ArrayList; import java.util.Collections; @@ -22,13 +24,12 @@ 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.boot.SpringApplication; 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.Application; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; @@ -47,7 +48,7 @@ import org.springframework.web.util.UriTemplate; * @author Oliver Gierke */ @Component -@RequiredArgsConstructor(onConstructor = @_(@Autowired)) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) class Jira implements JiraConnector { private static final String JIRA_HOST = "https://jira.spring.io"; @@ -64,9 +65,7 @@ class Jira implements JiraConnector { */ @Override @CacheEvict(value = "tickets", allEntries = true) - public void reset() { - - } + public void reset() {} @Cacheable("release-tickets") public Ticket getReleaseTicketFor(ModuleIteration iteration) { @@ -81,6 +80,10 @@ class Jira implements JiraConnector { JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters) .getBody(); + if (issues.getIssues().isEmpty()) { + throw new IllegalStateException(String.format("Did not find a release ticket for %s!", iteration)); + } + JiraIssue issue = issues.getIssues().get(0); return new Ticket(issue.getKey(), issue.getFields().getSummary()); @@ -121,8 +124,8 @@ class Jira implements JiraConnector { parameters.put("fields", "summary,fixVersions"); parameters.put("startAt", startAt); - issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers), JiraIssues.class, - parameters).getBody(); + issues = operations + .exchange(SEARCH_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers), JiraIssues.class, parameters).getBody(); logger.log(iteration, "Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal()); @@ -210,11 +213,10 @@ class Jira implements JiraConnector { public static void main(String[] args) { - try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext( - "META-INF/spring/spring-shell-plugin.xml")) { + try (ConfigurableApplicationContext context = SpringApplication.run(Application.class, args)) { JiraConnector tracker = context.getBean(JiraConnector.class); - TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2); + TrainIteration iteration = new TrainIteration(ReleaseTrains.GOSLING, Iteration.SR1); ModuleIteration module = iteration.getModule("JPA"); // Changelog changelog = tracker.getChangelogFor(module); 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 cd99321..e033592 100644 --- a/src/main/java/org/springframework/data/release/maven/Artifact.java +++ b/src/main/java/org/springframework/data/release/maven/Artifact.java @@ -48,7 +48,7 @@ public class Artifact { this.module = module; this.repository = new Repository(module.getIteration()); - this.version = ArtifactVersion.from(module); + this.version = ArtifactVersion.of(module); } /** 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 eacff6b..7de4df7 100644 --- a/src/main/java/org/springframework/data/release/maven/MavenOperations.java +++ b/src/main/java/org/springframework/data/release/maven/MavenOperations.java @@ -34,6 +34,7 @@ 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.utils.ExecutionUtils; import org.springframework.data.release.utils.Logger; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -47,7 +48,6 @@ import org.xmlbeam.io.XBFileIO; @RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class MavenOperations { - private static final String COMMONS_VERSION_PROPERTY = "springdata.commons"; private static final String POM_XML = "pom.xml"; private final Workspace workspace; @@ -90,9 +90,8 @@ public class MavenOperations { execute(workspace.getFile("parent/pom.xml", BUILD), new PomCallback() { @Override - public ParentPom doWith(ParentPom pom) { + public void doWith(ParentPom pom) { pom.setSharedResourcesVersion(phase.equals(PREPARE) ? buildVersion : nextBuildVersion); - return pom; } }); @@ -108,7 +107,7 @@ public class MavenOperations { execute(workspace.getFile(POM_XML, project), new PomCallback() { @Override - public Pom doWith(Pom pom) { + public void doWith(Pom pom) { for (Project dependency : project.getDependencies()) { @@ -132,8 +131,6 @@ public class MavenOperations { pom.setParentVersion(version); updateRepository(project, pom, repository, phase); - - return pom; } }); } @@ -149,22 +146,22 @@ public class MavenOperations { */ public void triggerDistributionBuild(TrainIteration iteration) throws Exception { - for (ModuleIteration moduleIteration : iteration) { + ExecutionUtils.run(iteration, module -> { - Project project = moduleIteration.getProject(); + Project project = module.getProject(); if (BUILD.equals(project)) { - continue; + return; } if (!isMavenProject(project)) { logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); - continue; + return; } logger.log(project, "Triggering distribution build…"); - ArtifactVersion version = ArtifactVersion.from(moduleIteration); + ArtifactVersion version = ArtifactVersion.of(module); String command = "mvn clean deploy -DskipTests -Pdistribute"; @@ -174,14 +171,14 @@ public class MavenOperations { command = command.concat(",release"); } - CommandResult result = os.executeWithOutput(command, moduleIteration.getProject()).get(); + CommandResult result = os.executeWithOutput(command, module.getProject()).get(); if (result.hasError()) { throw result.getException(); } logger.log(project, "Successfully finished distribution build!"); - } + }); } private boolean isMavenProject(Project project) { @@ -197,7 +194,7 @@ public class MavenOperations { execute(bomPomFile, new PomCallback() { @Override - public Pom doWith(Pom pom) { + public void doWith(Pom pom) { for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { @@ -213,8 +210,6 @@ public class MavenOperations { pom.setDependencyManagementVersion(additionalArtifact, version); } } - - return pom; } }); } @@ -246,13 +241,11 @@ public class MavenOperations { Class typeArgument = GenericTypeResolver.resolveTypeArgument(callback.getClass(), PomCallback.class); T pom = (T) io.read(typeArgument); - pom = callback.doWith(pom); - + callback.doWith(pom); io.write(pom); } private interface PomCallback { - - public T doWith(T pom); + public void doWith(T pom); } } 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 6d1d7eb..c010c31 100644 --- a/src/main/java/org/springframework/data/release/maven/MavenProject.java +++ b/src/main/java/org/springframework/data/release/maven/MavenProject.java @@ -37,6 +37,6 @@ class MavenProject { } public ArtifactVersion getReleaseVersion() { - return new ArtifactVersion(module.getVersion()); + return ArtifactVersion.of(module.getVersion()); } } diff --git a/src/main/java/org/springframework/data/release/maven/Repository.java b/src/main/java/org/springframework/data/release/maven/Repository.java index 1359a7a..d8a8e93 100644 --- a/src/main/java/org/springframework/data/release/maven/Repository.java +++ b/src/main/java/org/springframework/data/release/maven/Repository.java @@ -28,8 +28,8 @@ public class Repository { private final String url; public Repository(Iteration iteration) { - this.id = iteration.isPublicVersion() ? "spring-libs-release" : "spring-libs-milestone"; - this.url = iteration.isPublicVersion() ? BASE.concat("release") : BASE.concat("milestone"); + this.id = iteration.isPublic() ? "spring-libs-release" : "spring-libs-milestone"; + this.url = iteration.isPublic() ? BASE.concat("release") : BASE.concat("milestone"); } public String getId() { 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 2c0ff68..57a507c 100644 --- a/src/main/java/org/springframework/data/release/model/ArtifactVersion.java +++ b/src/main/java/org/springframework/data/release/model/ArtifactVersion.java @@ -16,7 +16,6 @@ package org.springframework.data.release.model; import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; import org.springframework.util.Assert; @@ -25,7 +24,6 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -@RequiredArgsConstructor @EqualsAndHashCode public class ArtifactVersion implements Comparable { @@ -43,13 +41,20 @@ public class ArtifactVersion implements Comparable { * Creates a new {@link ArtifactVersion} from the given logical {@link Version}. * * @param version must not be {@literal null}. + * @param suffix must not be {@literal null} or empty. */ - public ArtifactVersion(Version version) { + private ArtifactVersion(Version version, String suffix) { Assert.notNull(version, "Version must not be null!"); + Assert.hasText(suffix, "Suffix must not be null or empty!"); + ; this.version = version; - this.suffix = RELEASE_SUFFIX; + this.suffix = suffix; + } + + public static ArtifactVersion of(Version version) { + return new ArtifactVersion(version, RELEASE_SUFFIX); } /** @@ -58,7 +63,7 @@ public class ArtifactVersion implements Comparable { * @param source must not be {@literal null} or empty. * @return */ - public static ArtifactVersion parse(String source) { + public static ArtifactVersion of(String source) { Assert.hasText(source, "Version source must not be null or empty!"); @@ -67,7 +72,7 @@ public class ArtifactVersion implements Comparable { Version version = Version.parse(source.substring(0, suffixStart)); String suffix = source.substring(suffixStart + 1); - Assert.isTrue(suffix.matches(VALID_SUFFIX), "Invalid version suffix!"); + Assert.isTrue(suffix.matches(VALID_SUFFIX), String.format("Invalid version suffix: %s!", source)); return new ArtifactVersion(version, suffix); } @@ -78,14 +83,14 @@ public class ArtifactVersion implements Comparable { * @param iterationVersion must not be {@literal null}. * @return */ - public static ArtifactVersion from(IterationVersion iterationVersion) { + public static ArtifactVersion of(IterationVersion iterationVersion) { Assert.notNull(iterationVersion, "IterationVersion must not be null!"); Version version = iterationVersion.getVersion(); Iteration iteration = iterationVersion.getIteration(); - if (iteration.isGAVersion()) { + if (iteration.isGAIteration()) { return new ArtifactVersion(version, RELEASE_SUFFIX); } @@ -133,17 +138,38 @@ public class ArtifactVersion implements Comparable { return suffix.matches(MILESTONE_SUFFIX); } + /** + * Returns the next development version to be used for the current release version, which means next minor for GA + * versions and next bug fix for service releases. Will return the current version as snapshot otherwise. + * + * @return + */ public ArtifactVersion getNextDevelopmentVersion() { - if (suffix.equals(SNAPSHOT_SUFFIX)) { - return this; + if (suffix.equals(RELEASE_SUFFIX)) { + + boolean isGaVersion = version.withBugfix(0).equals(version); + Version nextVersion = isGaVersion ? version.nextMinor() : version.nextBugfix(); + + return new ArtifactVersion(nextVersion, SNAPSHOT_SUFFIX); } + return suffix.equals(SNAPSHOT_SUFFIX) ? this : new ArtifactVersion(version, SNAPSHOT_SUFFIX); + } + + /** + * Returns the next bug fix version for the current version if it's a release version or the snapshot version of the + * current one otherwise. + * + * @return + */ + public ArtifactVersion getNextBugfixVersion() { + if (suffix.equals(RELEASE_SUFFIX)) { return new ArtifactVersion(version.nextBugfix(), SNAPSHOT_SUFFIX); } - return new ArtifactVersion(version, SNAPSHOT_SUFFIX); + return suffix.equals(SNAPSHOT_SUFFIX) ? this : new ArtifactVersion(version, SNAPSHOT_SUFFIX); } /* @@ -152,7 +178,9 @@ public class ArtifactVersion implements Comparable { */ @Override public int compareTo(ArtifactVersion that) { - return this.toString().compareTo(that.toString()); + + int versionsEqual = this.version.compareTo(that.version); + return versionsEqual != 0 ? versionsEqual : this.suffix.compareTo(that.suffix); } /* @@ -165,7 +193,7 @@ public class ArtifactVersion implements Comparable { } /** - * Returns the {@link String} of the plain version (read: x.y.z, ommitting trailing bugfix zeros). + * Returns the {@link String} of the plain version (read: x.y.z, omitting trailing bug fix zeros). * * @return */ 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 236fdfa..e7fe64a 100644 --- a/src/main/java/org/springframework/data/release/model/Iteration.java +++ b/src/main/java/org/springframework/data/release/model/Iteration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -52,11 +52,17 @@ public class Iteration { this.next = next; } - public boolean isGAVersion() { + public boolean isGAIteration() { return this.equals(GA); } - public boolean isPublicVersion() { + /** + * Returns whether the {@link Iteration} is considered public, i.e. the artifacts produced by the iteration are + * supposed to be published to Maven Central. + * + * @return + */ + public boolean isPublic() { return isServiceIteration() || this.equals(GA); } 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 35d320b..73bc537 100644 --- a/src/main/java/org/springframework/data/release/model/Module.java +++ b/src/main/java/org/springframework/data/release/model/Module.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -57,6 +57,16 @@ public class Module { return new Module(project, nextVersion.toString()); } + /** + * Returns whether the current {@link Module} refers to the same project as the given one. + * + * @param module must not be {@literal null}. + * @return + */ + public boolean hasSameProjectAs(Module module) { + return this.project.equals(module.project); + } + @Override public String toString() { return String.format("Spring Data %s %s - %s", project.getName(), version, project.getKey()); 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 91d605a..815be00 100644 --- a/src/main/java/org/springframework/data/release/model/ModuleIteration.java +++ b/src/main/java/org/springframework/data/release/model/ModuleIteration.java @@ -64,7 +64,7 @@ public class ModuleIteration implements IterationVersion { public String getVersionString() { StringBuilder builder = new StringBuilder(); - builder.append(ArtifactVersion.from(this).toShortString()); + builder.append(ArtifactVersion.of(this).toShortString()); if (!iteration.isServiceIteration()) { builder.append(" ").append(iteration.getName()); diff --git a/src/main/java/org/springframework/data/release/model/Projects.java b/src/main/java/org/springframework/data/release/model/Projects.java index 264fe3d..351cfcd 100644 --- a/src/main/java/org/springframework/data/release/model/Projects.java +++ b/src/main/java/org/springframework/data/release/model/Projects.java @@ -25,7 +25,7 @@ import java.util.List; public class Projects { public static final Project COMMONS, BUILD, REST, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, - REDIS, GEMFIRE; + REDIS, GEMFIRE, KEY_VALUE, ENVERS; public static final List PROJECTS; static { @@ -44,9 +44,12 @@ public class Projects { GEMFIRE = new Project("SGF", "Gemfire", Arrays.asList(COMMONS)); REST = new Project("DATAREST", "REST", Arrays.asList(COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE, SOLR, CASSANDRA), - Arrays.asList("spring-data-rest-core", "spring-data-rest-core")); + Arrays.asList("spring-data-rest-core", "spring-data-rest-core", "spring-data-rest-hal-browser")); + + KEY_VALUE = new Project("DATAKV", "KeyValue", Arrays.asList(COMMONS)); + ENVERS = new Project("DATAENV", "Envers", Arrays.asList(JPA, COMMONS)); PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, - GEMFIRE, REST); + GEMFIRE, REST, KEY_VALUE, ENVERS); } } 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 49611ef..21489f7 100644 --- a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java +++ b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java @@ -27,7 +27,7 @@ import java.util.List; public class ReleaseTrains { public static final List TRAINS; - public static final Train CODD, DIJKSTRA, EVANS, FOWLER; + public static final Train CODD, DIJKSTRA, EVANS, FOWLER, GOSLING, HOPPER; static { @@ -35,10 +35,13 @@ public class ReleaseTrains { DIJKSTRA = dijkstra(); EVANS = DIJKSTRA.next("Evans", Transition.MINOR); FOWLER = EVANS.next("Fowler", Transition.MINOR); + GOSLING = FOWLER.next("Gosling", Transition.MINOR, new Module(KEY_VALUE, "1.0")); + HOPPER = GOSLING.next("Hopper", Transition.MINOR, new Module(NEO4J, "4.1"), new Module(COUCHBASE, "2.1"), + new Module(ENVERS, "1.0")); // Trains - TRAINS = Arrays.asList(CODD, DIJKSTRA, EVANS, FOWLER); + TRAINS = Arrays.asList(CODD, DIJKSTRA, EVANS, FOWLER, GOSLING, HOPPER); // Train names 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 ab42e45..788a724 100644 --- a/src/main/java/org/springframework/data/release/model/Train.java +++ b/src/main/java/org/springframework/data/release/model/Train.java @@ -21,15 +21,17 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.Value; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.shell.support.util.OsUtils; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** * @author Oliver Gierke @@ -38,7 +40,7 @@ import org.springframework.util.StringUtils; public class Train implements Iterable { private final String name;; - private final List modules; + private final Collection modules; private final Iterations iterations; public Train(String name, Module... modules) { @@ -48,10 +50,10 @@ public class Train implements Iterable { this.iterations = Iterations.DEFAULT; } - public Train(String name, List modules) { + public Train(String name, Collection modules) { this.name = name; - this.modules = Collections.unmodifiableList(modules); + this.modules = Collections.unmodifiableCollection(modules); this.iterations = Iterations.DEFAULT; } @@ -66,46 +68,39 @@ public class Train implements Iterable { public Module getModule(String name) { - for (Module module : this) { - if (module.getProject().getName().equals(name)) { - return module; - } - } - - return null; + return modules.stream().// + filter(module -> module.getProject().getName().equals(name)).// + findFirst().// + orElseThrow(() -> new IllegalArgumentException(String.format("No Module found with name %s!", name))); } public Module getModule(Project project) { - for (Module module : this) { - if (module.getProject().equals(project)) { - return module; - } - } - - return null; + return modules.stream().// + filter(module -> module.getProject().equals(project)).// + findFirst().orElseThrow( + () -> new IllegalArgumentException(String.format("No module found for project %s!", project.getName()))); } - public Train next(String name, Transition transition) { + public Train next(String name, Transition transition, Module... additionalModules) { - List nextModules = new ArrayList<>(); + Set collect = Stream.concat(modules.stream(), Stream.of(additionalModules)).// + map(module -> Arrays.stream(additionalModules).// + reduce(module.next(transition), + (it, additionalModule) -> it.hasSameProjectAs(additionalModule) ? additionalModule : it)) + .collect(Collectors.toSet()); - for (Module module : modules) { - nextModules.add(module.next(transition)); - } - - return new Train(name, nextModules); + return new Train(name, collect); } public ModuleIteration getModuleIteration(Iteration iteration, String moduleName) { - for (Module module : this) { - if (module.hasName(moduleName)) { - return new ModuleIteration(module, iteration, this); - } - } - - return null; + return modules.stream().// + filter(module -> module.hasName(moduleName)).// + findFirst().// + map(module -> new ModuleIteration(module, iteration, this)).// + orElseThrow( + () -> new IllegalArgumentException(String.format("No module found with module name %s!", moduleName))); } public Iterable getModuleIterations(Iteration iteration) { @@ -114,19 +109,12 @@ public class Train implements Iterable { List getModuleIterations(Iteration iteration, Project... exclusions) { - List iterations = new ArrayList<>(modules.size()); List exclusionList = Arrays.asList(exclusions); - for (Module module : this) { - - if (exclusionList.contains(module.getProject())) { - continue; - } - - iterations.add(new ModuleIteration(module, iteration, this)); - } - - return iterations; + return modules.stream().// + filter(module -> !exclusionList.contains(module.getProject())).// + map(module -> new ModuleIteration(module, iteration, this)).// + collect(Collectors.toList()); } public Iteration getIteration(String name) { @@ -136,7 +124,8 @@ public class Train implements Iterable { public ArtifactVersion getModuleVersion(Project project, Iteration iteration) { Module module = getModule(project); - return ArtifactVersion.from(new ModuleIteration(module, iteration, this)); + + return ArtifactVersion.of(new ModuleIteration(module, iteration, this)); } /* @@ -148,8 +137,14 @@ public class Train implements Iterable { StringBuilder builder = new StringBuilder(); - builder.append(name).append(OsUtils.LINE_SEPARATOR).append(OsUtils.LINE_SEPARATOR); - builder.append(StringUtils.collectionToDelimitedString(modules, OsUtils.LINE_SEPARATOR)); + builder.append(name).// + append(OsUtils.LINE_SEPARATOR).// + append(OsUtils.LINE_SEPARATOR); + + builder.append(modules.stream().// + map(Module::toString).// + sorted().// + collect(Collectors.joining(OsUtils.LINE_SEPARATOR))); return builder.toString(); } @@ -186,24 +181,18 @@ public class Train implements Iterable { Assert.hasText(name, "Name must not be null or empty!"); - for (Iteration iteration : this) { - if (iteration.getName().equalsIgnoreCase(name)) { - return iteration; - } - } - - return null; + return iterations.stream().// + filter(iteration -> iteration.getName().equalsIgnoreCase(name)).// + findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format("No iteration found with name %s!", name))); } Iteration getPreviousIteration(Iteration iteration) { - for (Iteration candidate : iterations) { - if (candidate.isNext(iteration)) { - return candidate; - } - } - - throw new IllegalArgumentException(String.format("Could not find previous iteration for %s!", iteration)); + return iterations.stream().// + filter(candidate -> candidate.isNext(iteration)).// + findFirst().orElseThrow(() -> new IllegalArgumentException( + String.format("Could not find previous iteration for %s!", iteration))); } /* diff --git a/src/main/java/org/springframework/data/release/model/Version.java b/src/main/java/org/springframework/data/release/model/Version.java index f62d073..f1ff92b 100644 --- a/src/main/java/org/springframework/data/release/model/Version.java +++ b/src/main/java/org/springframework/data/release/model/Version.java @@ -23,7 +23,7 @@ public class Version implements Comparable { * * @param parts must not be {@literal null} or empty. */ - public Version(int... parts) { + private Version(int... parts) { Assert.notNull(parts); Assert.isTrue(parts.length > 0 && parts.length < 5); @@ -39,6 +39,10 @@ public class Version implements Comparable { Assert.isTrue(build >= 0, "Build version must be greater or equal zero!"); } + public static Version of(int... parts) { + return new Version(parts); + } + /** * Parses the given string representation of a version into a {@link Version} object. * diff --git a/src/main/java/org/springframework/data/release/utils/CommandUtils.java b/src/main/java/org/springframework/data/release/utils/CommandUtils.java deleted file mode 100644 index 2aae3b0..0000000 --- a/src/main/java/org/springframework/data/release/utils/CommandUtils.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2015 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 java.util.concurrent.Future; - -import org.springframework.data.release.io.CommandResult; - -/** - * @author Oliver Gierke - */ -public class CommandUtils { - - public static CommandResult getCommandResult(Future future) throws Exception { - - CommandResult result = future.get(); - - if (result.hasError()) { - throw new CommandException(result); - } - - return result; - } - - public static class CommandException extends RuntimeException { - - private final CommandResult result; - - public CommandException(CommandResult result) { - - super(result.getException()); - this.result = result; - } - - /* - * (non-Javadoc) - * @see java.lang.Throwable#getMessage() - */ - @Override - public String getMessage() { - return String.format("Command execution failed: %s.", result); - } - } -} diff --git a/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java b/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java new file mode 100644 index 0000000..840e182 --- /dev/null +++ b/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright 2015 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 java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.springframework.util.Assert; + +/** + * Utility method to easily execute functionality in parallel. + * + * @author Oliver Gierke + */ +public class ExecutionUtils { + + /** + * Runs the given {@link ConsumerWithException} for each element in the given {@link Iterable} in parallel waiting for + * all executions to complete before returning. Exceptions being thrown in the {@link ConsumerWithException} will be + * converted into {@link RuntimeException}s. + * + * @param iterable must not be {@literal null}. + * @param consumer must not be {@literal null}. + */ + public static void run(Iterable iterable, ConsumerWithException consumer) { + + Assert.notNull(iterable, "Iterable must not be null!"); + Assert.notNull(consumer, "Consumer must not be null!"); + + StreamSupport.stream(iterable.spliterator(), false).// + map(it -> CompletableFuture.runAsync(() -> { + try { + consumer.accept(it); + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + })).collect(Collectors.toList()).forEach(future -> future.join()); + + } + + /** + * Runs the given {@link Function} for each element in the given {@link Iterable} in parallel waiting for all + * executions to complete before returning the results. + * + * @param iterable must not be {@literal null}. + * @param function must not be {@literal null}. + * @return + */ + public static Collection runAndReturn(Iterable iterable, Function function) { + + Assert.notNull(iterable, "Iterable must not be null!"); + Assert.notNull(function, "Consumer must not be null!"); + + return StreamSupport.stream(iterable.spliterator(), false).// + map(it -> CompletableFuture.supplyAsync(() -> function.apply(it))).// + collect(Collectors.toList()).// + stream().// + map(future -> future.join()).// + collect(Collectors.toList()); + } + + public static interface ConsumerWithException { + + void accept(T t) throws Exception; + } +} diff --git a/src/main/java/org/springframework/data/release/utils/Logger.java b/src/main/java/org/springframework/data/release/utils/Logger.java index 94c854c..cd357b3 100644 --- a/src/main/java/org/springframework/data/release/utils/Logger.java +++ b/src/main/java/org/springframework/data/release/utils/Logger.java @@ -17,6 +17,7 @@ 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.Train; import org.springframework.data.release.model.TrainIteration; import org.springframework.shell.support.logging.HandlerUtils; import org.springframework.stereotype.Component; @@ -43,6 +44,10 @@ public class Logger { log(iteration.toString(), template, args); } + public void log(Train train, String template, Object... args) { + log(train.getName(), 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/main/resources/META-INF/spring/spring-shell-plugin.xml b/src/main/resources/META-INF/spring/spring-shell-plugin.xml deleted file mode 100644 index c61e0b2..0000000 --- a/src/main/resources/META-INF/spring/spring-shell-plugin.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..f759759 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +io.work-dir=~/temp/spring-data-shell diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index f1b93eb..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - %d %5p %40.40c:%4L - %m%n - - - - - - - - - - \ No newline at end of file diff --git a/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java b/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java index 0268b99..9faa6c4 100644 --- a/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java @@ -16,14 +16,14 @@ package org.springframework.data.release; import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Oliver Gierke */ +@ActiveProfiles({ "test", "local" }) @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = TestConfig.class) -public abstract class AbstractIntegrationTests { - -} +@SpringApplicationConfiguration(classes = Application.class) +public abstract class AbstractIntegrationTests {} diff --git a/src/test/java/org/springframework/data/release/ArtifactUnitTests.java b/src/test/java/org/springframework/data/release/ArtifactUnitTests.java index 32af713..13dda41 100644 --- a/src/test/java/org/springframework/data/release/ArtifactUnitTests.java +++ b/src/test/java/org/springframework/data/release/ArtifactUnitTests.java @@ -35,7 +35,7 @@ public class ArtifactUnitTests { 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"))); + assertThat(artifact.getVersion(), is(ArtifactVersion.of("1.6.0.M1"))); + assertThat(artifact.getNextDevelopmentVersion(), is(ArtifactVersion.of("1.6.0.BUILD-SNAPSHOT"))); } } diff --git a/src/test/java/org/springframework/data/release/cli/ReleaseCommandsIntegrationTests.java b/src/test/java/org/springframework/data/release/cli/ReleaseCommandsIntegrationTests.java index dc41076..875ca9b 100644 --- a/src/test/java/org/springframework/data/release/cli/ReleaseCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/cli/ReleaseCommandsIntegrationTests.java @@ -35,8 +35,8 @@ public class ReleaseCommandsIntegrationTests extends AbstractIntegrationTests { @Test public void predictsReleaseTrainCorrectly() throws Exception { - git.update(ReleaseTrains.DIJKSTRA); + git.update(ReleaseTrains.GOSLING); - assertThat(releaseCommands.predictTrainAndIteration(), is("Dijkstra")); + assertThat(releaseCommands.predictTrainAndIteration(), is("Gosling")); } } diff --git a/src/test/java/org/springframework/data/release/git/BranchUnitTests.java b/src/test/java/org/springframework/data/release/git/BranchUnitTests.java index 9b6e9dd..fe92646 100644 --- a/src/test/java/org/springframework/data/release/git/BranchUnitTests.java +++ b/src/test/java/org/springframework/data/release/git/BranchUnitTests.java @@ -32,14 +32,14 @@ public class BranchUnitTests { @Test public void testname() { - IterationVersion iterationVersion = new SimpleIterationVersion(new Version(1, 4), Iteration.RC1); + IterationVersion iterationVersion = new SimpleIterationVersion(Version.of(1, 4), Iteration.RC1); assertThat(Branch.from(iterationVersion).toString(), is("master")); } @Test public void createsBugfixBranchForServiceRelease() { - IterationVersion iterationVersion = new SimpleIterationVersion(new Version(1, 4), Iteration.SR1); + IterationVersion iterationVersion = new SimpleIterationVersion(Version.of(1, 4), Iteration.SR1); assertThat(Branch.from(iterationVersion).toString(), is("1.4.x")); } } 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 e87726b..320c127 100644 --- a/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/git/GitOperationsIntegrationTests.java @@ -15,14 +15,15 @@ */ package org.springframework.data.release.git; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import static org.springframework.data.release.model.Projects.*; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.AbstractIntegrationTests; import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.util.StringUtils; +import org.springframework.data.release.model.TestReleaseTrains; /** * @author Oliver Gierke @@ -32,22 +33,25 @@ public class GitOperationsIntegrationTests extends AbstractIntegrationTests { @Autowired GitOperations gitOperations; @Test - @Ignore - public void testname() throws Exception { - gitOperations.update(ReleaseTrains.CODD); + public void updatesGitRepositories() throws Exception { + gitOperations.update(ReleaseTrains.GOSLING); } @Test - @Ignore public void showTags() throws Exception { - Tags tags = gitOperations.getTags(COMMONS); - System.out.println(StringUtils.collectionToDelimitedString(tags.asList(), "\n")); + gitOperations.update(TestReleaseTrains.SAMPLE); + + assertThat(gitOperations.getTags(BUILD).asList(), is(not(emptyIterable()))); } @Test public void foo() throws Exception { + gitOperations.update(TestReleaseTrains.SAMPLE); + } - gitOperations.update(ReleaseTrains.EVANS); + @Test + public void obtainsVersionTagsForRepoThatAlsoHasOtherTags() { + gitOperations.getTags(MONGO_DB); } } diff --git a/src/test/java/org/springframework/data/release/git/GitProjectUnitTests.java b/src/test/java/org/springframework/data/release/git/GitProjectUnitTests.java index d9abd50..d7b7786 100644 --- a/src/test/java/org/springframework/data/release/git/GitProjectUnitTests.java +++ b/src/test/java/org/springframework/data/release/git/GitProjectUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -19,20 +19,31 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import org.junit.Test; +import org.springframework.data.release.model.Module; +import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; /** + * Unit tests for {@link GitProject}. + * * @author Oliver Gierke */ public class GitProjectUnitTests { @Test - public void testname() { + public void buildsGitHubRepositoryUriCorrectly() { Train codd = ReleaseTrains.CODD; - GitProject project = new GitProject(codd.getModule("Commons").getProject(), new GitServer()); + GitServer server = new GitServer(); + Module module = codd.getModule("Commons"); + Project project = module.getProject(); - assertThat(project.getProjectUri(), is("https://www.github.com/spring-projects/spring-data-commons")); + GitProject gitProject = new GitProject(project, server); + + String projectUri = gitProject.getProjectUri(); + + assertThat(projectUri, startsWith(server.getUri())); + assertThat(projectUri, endsWith("spring-data-commons")); } } diff --git a/src/test/java/org/springframework/data/release/git/GitPropertiesIntegrationTests.java b/src/test/java/org/springframework/data/release/git/GitPropertiesIntegrationTests.java new file mode 100644 index 0000000..b7d8503 --- /dev/null +++ b/src/test/java/org/springframework/data/release/git/GitPropertiesIntegrationTests.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.git; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.AbstractIntegrationTests; + +/** + * @author Oliver Gierke + */ +public class GitPropertiesIntegrationTests extends AbstractIntegrationTests { + + @Autowired GitProperties gitProperties; + + @Test + public void hasBasicPropertiesConfigured() { + assertThat(gitProperties.getAuthor(), is(notNullValue())); + assertThat(gitProperties.getEmail(), is(notNullValue())); + } +} diff --git a/src/test/java/org/springframework/data/release/io/CommonsExecOsCommandOperationsIntegegrationTests.java b/src/test/java/org/springframework/data/release/io/IoPropertiesIntegrationTests.java similarity index 51% rename from src/test/java/org/springframework/data/release/io/CommonsExecOsCommandOperationsIntegegrationTests.java rename to src/test/java/org/springframework/data/release/io/IoPropertiesIntegrationTests.java index 94e2b40..7b4b2ee 100644 --- a/src/test/java/org/springframework/data/release/io/CommonsExecOsCommandOperationsIntegegrationTests.java +++ b/src/test/java/org/springframework/data/release/io/IoPropertiesIntegrationTests.java @@ -21,38 +21,16 @@ import static org.junit.Assert.*; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.AbstractIntegrationTests; -import org.springframework.data.release.model.Projects; /** * @author Oliver Gierke */ -public class CommonsExecOsCommandOperationsIntegegrationTests extends AbstractIntegrationTests { +public class IoPropertiesIntegrationTests extends AbstractIntegrationTests { - @Autowired OsCommandOperations operations; + @Autowired IoProperties ioProperties; @Test - public void testname() throws Exception { - - // CommandResult result = operations - // .executeCommand("export GIT_TRACE=1 && git clone https://github.com/spring-projects/spring-data-build").get(); - - // CommandResult result = operations.executeCommand("git pull", Projects.BUILD).get(); - - CommandResult result = operations.executeCommand("git remote -v", Projects.BUILD).get(); - // .get(); - - if (result.hasError()) { - - System.out.println(result.getStatus()); - System.out.println(result.getOutput()); - System.out.println(result.getException().getMessage()); - - throw result.getException(); - } else { - System.out.println(result.getOutput()); - } - - assertThat(result.hasError(), is(false)); - + public void configuresWorkingDirectoryFromApplicationProperties() { + assertThat(ioProperties.getWorkDir(), is(notNullValue())); } } diff --git a/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java b/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java index 98df45e..d85930d 100644 --- a/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java @@ -17,9 +17,7 @@ package org.springframework.data.release.maven; import java.io.IOException; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.data.release.AbstractIntegrationTests; @@ -36,15 +34,13 @@ public class MavenIntegrationTests extends AbstractIntegrationTests { @Autowired Workspace workspace; @Autowired ProjectionFactory projection; - public @Rule TemporaryFolder folder = new TemporaryFolder(); - @Test public void modifiesParentPomCorrectly() throws IOException { XBFileIO io = projection.io().file(new ClassPathResource("parent-pom.xml").getFile()); ParentPom pom = io.read(ParentPom.class); - pom.setSharedResourcesVersion(ArtifactVersion.parse("1.2.0.RELEASE")); + pom.setSharedResourcesVersion(ArtifactVersion.of("1.2.0.RELEASE")); // System.out.println(projection.asString(pom)); } diff --git a/src/test/java/org/springframework/data/release/model/ArtifactVersionUnitTests.java b/src/test/java/org/springframework/data/release/model/ArtifactVersionUnitTests.java index f910cea..b606a2a 100644 --- a/src/test/java/org/springframework/data/release/model/ArtifactVersionUnitTests.java +++ b/src/test/java/org/springframework/data/release/model/ArtifactVersionUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -15,14 +15,10 @@ */ package org.springframework.data.release.model; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import org.junit.Test; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.IterationVersion; -import org.springframework.data.release.model.Version; /** * @author Oliver Gierke @@ -31,22 +27,22 @@ public class ArtifactVersionUnitTests { @Test(expected = IllegalArgumentException.class) public void rejectsInvalidVersionSuffix() { - ArtifactVersion.parse("1.4.5.GA"); + ArtifactVersion.of("1.4.5.GA"); } @Test public void parsesReleaseVersionCorrectly() { - ArtifactVersion version = ArtifactVersion.parse("1.4.5.RELEASE"); + ArtifactVersion version = ArtifactVersion.of("1.4.5.RELEASE"); assertThat(version.isReleaseVersion(), is(true)); - assertThat(version.getNextDevelopmentVersion(), is(ArtifactVersion.parse("1.4.6.BUILD-SNAPSHOT"))); + assertThat(version.getNextDevelopmentVersion(), is(ArtifactVersion.of("1.4.6.BUILD-SNAPSHOT"))); } @Test public void createsMilestoneVersionCorrectly() { - ArtifactVersion version = ArtifactVersion.parse("1.4.5.M1"); + ArtifactVersion version = ArtifactVersion.of("1.4.5.M1"); assertThat(version.isReleaseVersion(), is(false)); assertThat(version.isMilestoneVersion(), is(true)); @@ -55,7 +51,7 @@ public class ArtifactVersionUnitTests { @Test public void createsReleaseVersionByDefault() { - ArtifactVersion version = new ArtifactVersion(new Version(1, 4, 5)); + ArtifactVersion version = ArtifactVersion.of(Version.of(1, 4, 5)); assertThat(version.isReleaseVersion(), is(true)); assertThat(version.toString(), is("1.4.5.RELEASE")); @@ -64,8 +60,8 @@ public class ArtifactVersionUnitTests { @Test public void createsMilestoneVersionFromIteration() { - IterationVersion oneFourMilestoneOne = new SimpleIterationVersion(new Version(1, 4), Iteration.M1); - ArtifactVersion version = ArtifactVersion.from(oneFourMilestoneOne); + IterationVersion oneFourMilestoneOne = new SimpleIterationVersion(Version.of(1, 4), Iteration.M1); + ArtifactVersion version = ArtifactVersion.of(oneFourMilestoneOne); assertThat(version.isMilestoneVersion(), is(true)); assertThat(version.toString(), is("1.4.0.M1")); @@ -74,8 +70,8 @@ public class ArtifactVersionUnitTests { @Test public void createsReleaseVersionFromIteration() { - IterationVersion oneFourGA = new SimpleIterationVersion(new Version(1, 4), Iteration.GA); - ArtifactVersion version = ArtifactVersion.from(oneFourGA); + IterationVersion oneFourGA = new SimpleIterationVersion(Version.of(1, 4), Iteration.GA); + ArtifactVersion version = ArtifactVersion.of(oneFourGA); assertThat(version.isReleaseVersion(), is(true)); assertThat(version.toString(), is("1.4.0.RELEASE")); @@ -84,10 +80,48 @@ public class ArtifactVersionUnitTests { @Test public void createsServiceReleaseVersionFromIteration() { - IterationVersion oneFourServiceReleaseTwo = new SimpleIterationVersion(new Version(1, 4), Iteration.SR2); - ArtifactVersion version = ArtifactVersion.from(oneFourServiceReleaseTwo); + IterationVersion oneFourServiceReleaseTwo = new SimpleIterationVersion(Version.of(1, 4), Iteration.SR2); + ArtifactVersion version = ArtifactVersion.of(oneFourServiceReleaseTwo); assertThat(version.isReleaseVersion(), is(true)); assertThat(version.toString(), is("1.4.2.RELEASE")); } + + @Test + public void returnsNextMinorSnapshotVersionForGARelease() { + + ArtifactVersion version = ArtifactVersion.of("1.5.0.RELEASE").getNextDevelopmentVersion(); + + assertThat(version.isMilestoneVersion(), is(false)); + assertThat(version.isReleaseVersion(), is(false)); + assertThat(version, is(ArtifactVersion.of("1.6.0.BUILD-SNAPSHOT"))); + } + + @Test + public void ordersCorrectly() { + + ArtifactVersion oneNine = ArtifactVersion.of("1.9.0.RELEASE"); + ArtifactVersion oneTen = ArtifactVersion.of("1.10.0.RELEASE"); + + assertThat(oneNine.compareTo(oneTen), is(lessThan(0))); + } + + @Test + public void ordersSnapshotsOfSameVersionSmaller() { + + ArtifactVersion oneTenRelease = ArtifactVersion.of("1.10.0.RELEASE"); + ArtifactVersion oneTenSnapshot = ArtifactVersion.of("1.10.0.BUILD-SNAPSHOT"); + + assertThat(oneTenRelease.compareTo(oneTenSnapshot), is(greaterThan(0))); + } + + @Test + public void returnsCorrectBugfixVersions() { + + assertThat(ArtifactVersion.of("1.0.0.RELEASE").getNextBugfixVersion(), + is(ArtifactVersion.of("1.0.1.BUILD-SNAPSHOT"))); + assertThat(ArtifactVersion.of("1.0.0.M1").getNextBugfixVersion(), is(ArtifactVersion.of("1.0.0.BUILD-SNAPSHOT"))); + assertThat(ArtifactVersion.of("1.0.1.RELEASE").getNextBugfixVersion(), + is(ArtifactVersion.of("1.0.2.BUILD-SNAPSHOT"))); + } } diff --git a/src/test/java/org/springframework/data/release/TestConfig.java b/src/test/java/org/springframework/data/release/model/TestReleaseTrains.java similarity index 66% rename from src/test/java/org/springframework/data/release/TestConfig.java rename to src/test/java/org/springframework/data/release/model/TestReleaseTrains.java index 4ad5fa5..93d09a9 100644 --- a/src/test/java/org/springframework/data/release/TestConfig.java +++ b/src/test/java/org/springframework/data/release/model/TestReleaseTrains.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2015 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,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release; +package org.springframework.data.release.model; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; +import java.util.Arrays; /** + * Sample release {@link Train} definitions. + * * @author Oliver Gierke */ -@Configuration -@ComponentScan -public class TestConfig { +public class TestReleaseTrains { + public static Train SAMPLE = new Train("SAMPLE", Arrays.asList(new Module(Projects.BUILD, "1.0"))); } diff --git a/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java b/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java new file mode 100644 index 0000000..f52650e --- /dev/null +++ b/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 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; + +/** + * Unit tests for release {@link Train}s. + * + * @author Oliver Gierke + */ +public class TrainsUnitTest { + + @Test + public void prefersNewVersionOfAdditionalModule() { + + Module module = ReleaseTrains.HOPPER.getModule(Projects.NEO4J); + + assertThat(module.getVersion(), is(Version.parse("4.1"))); + } + + @Test + public void addsNewlyAddedModule() { + assertThat(ReleaseTrains.HOPPER.getModule(Projects.ENVERS), is(notNullValue())); + } +} diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties new file mode 100644 index 0000000..4982dd4 --- /dev/null +++ b/src/test/resources/application-test.properties @@ -0,0 +1,3 @@ +io.work-dir=~/temp/shell-test +logging.level.org.springframework=WARN +logging.level.org.springframework.data.release=INFO