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
This commit is contained in:
Oliver Gierke
2015-11-20 12:34:15 +01:00
parent f52d57da5c
commit 243561571b
56 changed files with 902 additions and 501 deletions

3
.gitignore vendored
View File

@@ -2,6 +2,7 @@ target/
.settings/
.project
.classpath
.factorypath
.springBeans
jira.properties
application-local.properties
spring-shell.log

View File

@@ -1,5 +0,0 @@
io.workDir=~/temp/spring-data-shell
git.username=olivergierke
git.author=Oliver Gierke
git.email=ogierke@pivotal.io

View File

@@ -8,16 +8,21 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.4.RELEASE</version>
<version>1.3.0.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<jar.mainclass>org.springframework.shell.Bootstrap</jar.mainclass>
<jar.mainclass>org.springframework.data.release.Application</jar.mainclass>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
@@ -72,13 +77,18 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.0.1.201506240215-r</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
@@ -93,8 +103,13 @@
<configuration>
<programs>
<program>
<mainClass>org.springframework.shell.Bootstrap</mainClass>
<mainClass>${jar.mainclass}</mainClass>
<id>spring-data-release-shell</id>
<jvmSettings>
<systemProperties>
<systemProperty>git.password=${git.password}</systemProperty>
</systemProperties>
</jvmSettings>
</program>
</programs>
</configuration>

View File

@@ -1 +1,8 @@
`mvn package appassembler:assemble && sh target/appassembler/bin/spring-data-release-shell`
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`

View File

@@ -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(""));
}
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View File

@@ -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)));
}
}

View File

@@ -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<IssueTracker, Project> tracker;
private final JiraConnector jira;
@@ -45,7 +49,8 @@ public class IssueTracerCommands implements CommandMarker {
* @param environment
*/
@Autowired
public IssueTracerCommands(PluginRegistry<IssueTracker, Project> tracker, JiraConnector jira, Environment environment) {
public IssueTrackerCommands(PluginRegistry<IssueTracker, Project> 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);
}
}

View File

@@ -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<String> 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(", "));
}
}

View File

@@ -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);
}
/**

View File

@@ -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);
}

View File

@@ -56,7 +56,6 @@ public class TrainConverter implements Converter<Train> {
@Override
public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData,
String optionContext, MethodTarget target) {
return false;
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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, Project> 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<Future<CommandResult>> executions = new ArrayList<>();
for (Module module : train) {
update(module.getProject());
}
for (Future<CommandResult> 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));
}

View File

@@ -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);
}
}

View File

@@ -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<Tag> {
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<Tag> {
return name.startsWith("v") ? name.substring(1) : name;
}
public ArtifactVersion toArtifactVersion() {
return ArtifactVersion.parse(getVersionSource());
public boolean isVersionTag() {
return toArtifactVersion().isPresent();
}
public Optional<ArtifactVersion> toArtifactVersion() {
try {
return Optional.of(ArtifactVersion.of(getVersionSource()));
} catch (IllegalArgumentException o_O) {
return Optional.empty();
}
}
/**
@@ -70,6 +89,10 @@ public class Tag implements Comparable<Tag> {
*/
@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));
}
}

View File

@@ -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<Tag> {
public class VersionTags implements Iterable<Tag> {
private final List<Tag> 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<Tag> source) {
VersionTags(List<Tag> source) {
Assert.notNull(source, "Tags must not be null!");
List<Tag> 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<Tag> {
* @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));
}
/**

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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();
}
/**

View File

@@ -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));

View File

@@ -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<GitHubIssue> issues = getIssuesFor(module);
List<Ticket> tickets = new ArrayList<>(issues.size());
for (GitHubIssue issue : issues) {
tickets.add(new Ticket(issue.getId(), issue.getTitle()));
}
List<Ticket> 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<GitHubMilestone> exchange = operations.exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE,
parameters).getBody();
List<GitHubMilestone> exchange = operations
.exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE, parameters).getBody();
GitHubMilestone milestone = null;

View File

@@ -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);

View File

@@ -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);
}
/**

View File

@@ -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<ParentPom>() {
@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<Pom>() {
@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<Pom>() {
@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<T extends Pom> {
public T doWith(T pom);
public void doWith(T pom);
}
}

View File

@@ -37,6 +37,6 @@ class MavenProject {
}
public ArtifactVersion getReleaseVersion() {
return new ArtifactVersion(module.getVersion());
return ArtifactVersion.of(module.getVersion());
}
}

View File

@@ -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() {

View File

@@ -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<ArtifactVersion> {
@@ -43,13 +41,20 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
* 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<ArtifactVersion> {
* @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<ArtifactVersion> {
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<ArtifactVersion> {
* @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<ArtifactVersion> {
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<ArtifactVersion> {
*/
@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<ArtifactVersion> {
}
/**
* 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
*/

View File

@@ -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);
}

View File

@@ -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());

View File

@@ -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());

View File

@@ -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<Project> 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);
}
}

View File

@@ -27,7 +27,7 @@ import java.util.List;
public class ReleaseTrains {
public static final List<Train> 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

View File

@@ -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<Module> {
private final String name;;
private final List<Module> modules;
private final Collection<Module> modules;
private final Iterations iterations;
public Train(String name, Module... modules) {
@@ -48,10 +50,10 @@ public class Train implements Iterable<Module> {
this.iterations = Iterations.DEFAULT;
}
public Train(String name, List<Module> modules) {
public Train(String name, Collection<Module> 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<Module> {
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<Module> nextModules = new ArrayList<>();
Set<Module> 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<ModuleIteration> getModuleIterations(Iteration iteration) {
@@ -114,19 +109,12 @@ public class Train implements Iterable<Module> {
List<ModuleIteration> getModuleIterations(Iteration iteration, Project... exclusions) {
List<ModuleIteration> iterations = new ArrayList<>(modules.size());
List<Project> 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<Module> {
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<Module> {
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<Module> {
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)));
}
/*

View File

@@ -23,7 +23,7 @@ public class Version implements Comparable<Version> {
*
* @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<Version> {
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.
*

View File

@@ -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<CommandResult> 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);
}
}
}

View File

@@ -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 <T> void run(Iterable<T> iterable, ConsumerWithException<T> 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 <T, S> Collection<S> runAndReturn(Iterable<T> iterable, Function<T, S> 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<T> {
void accept(T t) throws Exception;
}
}

View File

@@ -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)));
}

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.springframework.data.release" />
</beans>

View File

@@ -0,0 +1 @@
io.work-dir=~/temp/spring-data-shell

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.data" level="warn" />
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -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 {}

View File

@@ -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")));
}
}

View File

@@ -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"));
}
}

View File

@@ -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"));
}
}

View File

@@ -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);
}
}

View File

@@ -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"));
}
}

View File

@@ -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()));
}
}

View File

@@ -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()));
}
}

View File

@@ -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));
}

View File

@@ -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")));
}
}

View File

@@ -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")));
}

View File

@@ -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()));
}
}

View File

@@ -0,0 +1,3 @@
io.work-dir=~/temp/shell-test
logging.level.org.springframework=WARN
logging.level.org.springframework.data.release=INFO