Moved release tool into dedicated sub-folder.

This commit is contained in:
Oliver Gierke
2015-07-07 08:30:23 +02:00
commit d2beb0bd7a
88 changed files with 7066 additions and 0 deletions

View File

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

113
pom.xml Normal file
View File

@@ -0,0 +1,113 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-release-cli</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.4.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<jar.mainclass>org.springframework.shell.Bootstrap</jar.mainclass>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.xmlbeam</groupId>
<artifactId>xmlprojector</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
<dependency>
<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>
</dependencies>
<build>
<plugins>
<!-- Shell packaging -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<programs>
<program>
<mainClass>org.springframework.shell.Bootstrap</mainClass>
<id>spring-data-release-shell</id>
</program>
</programs>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</project>

1
readme.md Normal file
View File

@@ -0,0 +1 @@
`mvn package appassembler:assemble && sh target/appassembler/bin/spring-data-release-shell`

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.announcement;
import static org.springframework.data.release.model.Projects.*;
import org.springframework.data.release.cli.StaticResources;
import org.springframework.data.release.maven.Artifact;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.util.Assert;
/**
* @author Oliver Gierke
*/
public class AnnouncementOperations {
/**
* Returns the project list and links to be included in the release announcement for the given {@link Train} and
* {@link Iteration}.
*
* @param train must not be {@literal null}.
* @param iteration must not be {@literal null}.
* @return
*/
public String getProjectBulletpoints(TrainIteration iteration) {
Assert.notNull(iteration, "Iteration must not be null!");
StringBuilder builder = new StringBuilder();
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
Project project = module.getProject();
builder.append("* ");
builder.append(project.getFullName()).append(" ");
builder.append(module.getVersionString());
builder.append(" - ");
Artifact artifact = new Artifact(module);
builder.append(getMarkDownLink("Artifacts", artifact.getRootUrl()));
builder.append(" - ");
StaticResources resources = new StaticResources(module);
builder.append(getMarkDownLink("JavaDocs", resources.getJavaDocUrl())).append(" - ");
builder.append(getMarkDownLink("Documentation", resources.getDocumentationUrl())).append(" - ");
builder.append(getMarkDownLink("Changelog", resources.getChangelogUrl()));
builder.append("\n");
}
return builder.toString();
}
private String getMarkDownLink(String name, String url) {
return String.format("[%s](%s)", name, url);
}
public static void main(String[] args) {
AnnouncementOperations operations = new AnnouncementOperations();
System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.FOWLER, Iteration.SR1)));
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.release.jira.Credentials;
import org.springframework.data.release.jira.IssueTracker;
import org.springframework.data.release.jira.JiraConnector;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author Oliver Gierke
*/
@Component
public class IssueTracerCommands implements CommandMarker {
private final PluginRegistry<IssueTracker, Project> tracker;
private final JiraConnector jira;
private final Credentials credentials;
/**
* @param tracker
* @param environment
*/
@Autowired
public IssueTracerCommands(PluginRegistry<IssueTracker, Project> tracker, JiraConnector jira, Environment environment) {
String username = environment.getProperty("jira.username", (String) null);
String password = environment.getProperty("jira.password", (String) null);
this.tracker = tracker;
this.jira = jira;
this.credentials = StringUtils.hasText(username) ? new Credentials(username, password) : null;
}
@CliCommand("jira evict")
public void jiraEvict() {
jira.reset();
}
@CliCommand(value = "jira tickets")
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!";
}
return jira.getTicketsFor(iteration, forCurrentUser ? credentials : null).toString();
}
@CliCommand("tracker changelog")
public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration, //
@CliOption(key = "module") String moduleName) {
if (StringUtils.hasText(moduleName)) {
ModuleIteration module = iteration.getModule(moduleName);
return tracker.getPluginFor(module.getProject()).getChangelogFor(module).toString();
}
StringBuilder builder = new StringBuilder();
for (ModuleIteration module : iteration) {
IssueTracker issues = tracker.getPluginFor(module.getProject());
builder.append(issues.getChangelogFor(module)).append("\n");
}
return builder.toString();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import java.util.ArrayList;
import java.util.List;
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
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, ", ");
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import 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.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;
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;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ReleaseCommands implements CommandMarker {
private final MavenOperations maven;
private final GradleOperations gradle;
private final GitOperations git;
private final ReleaseOperations misc;
private final DocumentationOperations docs;
@CliCommand("release predict")
public String predictTrainAndIteration() throws Exception {
Pom pom = maven.getMavenProject(COMMONS);
Tags tags = git.getTags(COMMONS);
ArtifactVersion version = tags.getLatest().toArtifactVersion();
System.out.println(version);
for (Train train : ReleaseTrains.TRAINS) {
Module module = train.getModule(COMMONS);
if (!pom.getVersion().toString().startsWith(module.getVersion().toMajorMinorBugfix())) {
continue;
}
return train.getName();
}
return null;
}
/**
* Triggers the distribution of release artifacts for all projects.
*
* @param trainName
* @param iterationName
* @throws Exception
*/
@CliCommand("release distribute")
public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.checkout(iteration);
maven.triggerDistributionBuild(iteration);
}
/**
* Prepares the release of the given iteration of the given train.
*
* @param trainName the name of the release train (ignoring case).
* @param iterationName the name of the iteration.
* @throws Exception
*/
@CliCommand(value = "release prepare", help = "Prepares the release of the iteration of the given train.")
public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.prepare(iteration);
misc.prepareChangelogs(iteration);
misc.updateResources(iteration);
docs.updateDockbookIncludes(iteration);
maven.updatePom(iteration, Phase.PREPARE);
gradle.updateProject(iteration, Phase.PREPARE);
git.commit(iteration, "Prepare %s.", null);
}
@CliCommand(value = "release conclude")
public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.tagRelease(iteration);
maven.updatePom(iteration, Phase.CLEANUP);
gradle.updateProject(iteration, Phase.CLEANUP);
git.commit(iteration, "After release cleanups.", null);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.shell.plugin.BannerProvider;
import org.springframework.shell.support.util.FileUtils;
import org.springframework.shell.support.util.OsUtils;
import org.springframework.stereotype.Component;
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
class SpringDataReleaseCliBannerProvider implements BannerProvider {
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.NamedProvider#getProviderName()
*/
@Override
public String getProviderName() {
return "Spring Data Release Shell";
}
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.BannerProvider#getBanner()
*/
@Override
public String getBanner() {
StringBuilder builder = new StringBuilder();
builder.append(FileUtils.readBanner(SpringDataReleaseCliBannerProvider.class, "banner.txt"));
builder.append(getVersion()).append(OsUtils.LINE_SEPARATOR);
builder.append(OsUtils.LINE_SEPARATOR);
return builder.toString();
}
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.BannerProvider#getVersion()
*/
@Override
public String getVersion() {
return "1.0";
}
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.BannerProvider#getWelcomeMessage()
*/
@Override
public String getWelcomeMessage() {
return "Welcome to the Spring Data Release Shell!";
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.shell.plugin.PromptProvider;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class SpringDataReleaseCliPromptProvider implements PromptProvider {
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.PromptProvider#getPrompt()
*/
@Override
public String getPrompt() {
return "$ ";
}
/*
* (non-Javadoc)
* @see org.springframework.shell.plugin.NamedProvider#getProviderName()
*/
@Override
public String getProviderName() {
return "spring-data-release-cli";
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
/**
* @author Oliver Gierke
*/
@RequiredArgsConstructor
public class StaticResources {
private static final String URL_TEMPLATE = "http://docs.spring.io/spring-data/%s/docs/%s";
private final String baseUrl;
public StaticResources(ModuleIteration module) {
Project project = module.getProject();
ArtifactVersion version = ArtifactVersion.from(module);
this.baseUrl = String.format(URL_TEMPLATE, project.getName().toLowerCase(), version);
}
public String getDocumentationUrl() {
return baseUrl.concat("/reference/html");
}
public String getJavaDocUrl() {
return baseUrl.concat("/api");
}
public String getChangelogUrl() {
return baseUrl.concat("/changelog.txt");
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import java.util.List;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.shell.core.Completion;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.MethodTarget;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author Oliver Gierke
*/
@Component
public class TrainConverter implements Converter<Train> {
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String)
*/
@Override
public boolean supports(Class<?> type, String optionContext) {
return Train.class.isAssignableFrom(type);
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String)
*/
@Override
public Train convertFromText(String value, Class<?> targetType, String optionContext) {
return StringUtils.hasText(value) ? ReleaseTrains.getTrainByName(value) : null;
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget)
*/
@Override
public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData,
String optionContext, MethodTarget target) {
return false;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import java.util.List;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.core.Completion;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.MethodTarget;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
public class TrainIterationConverter implements Converter<TrainIteration> {
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String)
*/
@Override
public boolean supports(Class<?> type, String optionContext) {
return TrainIteration.class.equals(type);
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String)
*/
@Override
public TrainIteration convertFromText(String value, Class<?> targetType, String optionContext) {
String[] parts = value.split(" ");
Train train = ReleaseTrains.getTrainByName(parts[0].trim());
Iteration iteration = train.getIteration(parts[1].trim());
return new TrainIteration(train, iteration);
}
/*
* (non-Javadoc)
* @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget)
*/
@Override
public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData,
String optionContext, MethodTarget target) {
for (Train train : ReleaseTrains.TRAINS) {
for (Iteration iteration : train.getIterations()) {
completions.add(new Completion(new TrainIteration(train, iteration).toString()));
}
}
return false;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.docs;
import static org.springframework.data.release.model.Projects.*;
import lombok.RequiredArgsConstructor;
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.io.Workspace;
import org.springframework.data.release.io.Workspace.LineCallback;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DocumentationOperations {
private static final String INDEX_LOCATION = "/src/docbkx/index.xml";
private final Workspace workspace;
private final GitOperations git;
public void updateDockbookIncludes(TrainIteration iteration) throws Exception {
Tags tags = git.getTags(COMMONS);
ModuleIteration commons = iteration.getModule(COMMONS);
ModuleIteration previousIteration = iteration.getPreviousIteration(commons);
final GitProject gitProject = git.getGitProject(COMMONS);
final Tag previousTag = tags.createTag(previousIteration);
final Tag newTag = tags.createTag(commons);
for (ModuleIteration module : iteration) {
Project project = module.getProject();
if (!project.dependsOn(COMMONS)) {
continue;
}
workspace.processFile(INDEX_LOCATION, project, new LineCallback() {
@Override
public String doWith(String line, long number) {
boolean isInclude = line.contains("xi:include");
boolean containsGitRepo = line.contains(gitProject.getRepositoryName());
return isInclude && containsGitRepo ? line.replace(previousTag.toString(), newTag.toString()) : line;
}
});
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.model.IterationVersion;
import org.springframework.data.release.model.Version;
import org.springframework.util.Assert;
/**
* Value type to represent an SCM branch.
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
@EqualsAndHashCode
class Branch {
public static final Branch MASTER = new Branch("master");
private final String name;
/**
* Creates a new {@link Branch} from the given {@link IterationVersion}.
*
* @param iterationVersion must not be {@literal null}.
* @return
*/
public static Branch from(IterationVersion iterationVersion) {
Assert.notNull(iterationVersion, "Iteration versoin must not be null!");
Version version = iterationVersion.getVersion();
if (iterationVersion.getIteration().isServiceIteration()) {
return new Branch(version.toString().concat(".x"));
}
return MASTER;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.jira.Ticket;
/**
* @author Oliver Gierke
*/
@EqualsAndHashCode
@RequiredArgsConstructor
public class Commit {
private final Ticket ticket;
private final String summary;
private final String details;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(ticket.getId()).append(" - ").append(summary).append("\n");
if (details != null) {
builder.append("\n");
builder.append(details).append("\n");
}
return builder.toString();
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GitCommands implements CommandMarker {
private final GitOperations git;
@CliCommand("git checkout")
public void checkout(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.checkout(iteration);
}
@CliCommand("git update")
public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception,
InterruptedException {
git.update(ReleaseTrains.getTrainByName(trainName));
}
@CliCommand("git tags")
public String tags(@CliOption(key = { "project" }, mandatory = true) String projectName) throws Exception {
Project project = ReleaseTrains.getProjectByName(projectName);
return StringUtils.collectionToDelimitedString(git.getTags(project).asList(), "\n");
}
/**
* Resets all projects contained in the given {@link Train}.
*
* @param trainName
* @throws Exception
*/
@CliCommand("git reset")
public void reset(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.reset(iteration);
}
@CliCommand("git prepare")
public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.prepare(iteration);
}
/**
* Pushes all changes of all modules of the given {@link TrainIteration} to the remote server. If {@code tags} is
* given, only the tags are pushed.
*
* @param iteration
* @param tags
* @throws Exception
*/
@CliCommand("git push")
public void push(//
@CliOption(key = "", mandatory = true) TrainIteration iteration, //
@CliOption(key = "tags", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") String tags)
throws Exception {
boolean pushTags = Boolean.parseBoolean(tags);
if (pushTags) {
git.pushTags(iteration.getTrain());
} else {
git.push(iteration);
}
}
}

View File

@@ -0,0 +1,429 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
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 org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
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.Logger;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
/**
* Component to execut Git related operations.
*
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class GitOperations {
private final GitServer server = new GitServer();
private final Workspace workspace;
private final Logger logger;
private final PluginRegistry<IssueTracker, Project> issueTracker;
private final Environment environment;
private CredentialsProvider credentials;
@PostConstruct
public void init() {
this.credentials = new UsernamePasswordCredentialsProvider(environment.getProperty("git.username"),
environment.getProperty("git.password"));
}
public GitProject getGitProject(Project project) {
return new GitProject(project, server);
}
/**
* Resets the repositories for all modules of the given {@link Train}.
*
* @param train must not be {@literal null}.
* @throws Exception
*/
public void reset(TrainIteration train) throws Exception {
Assert.notNull(train, "Train must not be null!");
for (ModuleIteration module : train) {
Branch branch = Branch.from(module);
try (Git git = new Git(getRepository(module.getProject()))) {
logger.log(module, "git reset --hard origin/%s", branch);
git.reset().//
setMode(ResetType.HARD).//
setRef("origin/".concat(branch.toString())).//
call();
}
}
}
/**
* Checks out all projects of the given {@link Train} at the tags for the given {@link Iteration}.
*
* @param train
* @param iteration
* @throws Exception
*/
public void checkout(TrainIteration iteration) throws Exception {
update(iteration.getTrain());
for (ModuleIteration module : iteration) {
Project project = module.getProject();
ArtifactVersion artifactVersion = ArtifactVersion.from(module);
Tag tag = findTagFor(project, artifactVersion);
if (tag == null) {
throw new IllegalStateException(
String.format("No tag found for version %s of project %s, aborting.", artifactVersion, project));
}
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) {
Branch branch = Branch.from(module);
update(module.getProject());
logger.log(module.getProject(), "git checkout %s && git pull origin %s", branch, branch);
checkout(module.getProject(), branch);
try (Git git = new Git(getRepository(module.getProject()))) {
git.pull().//
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 push(TrainIteration iteration) throws Exception {
for (ModuleIteration module : iteration) {
Branch branch = Branch.from(module);
logger.log(module, "git push origin %s", branch);
try (Git git = new Git(getRepository(module.getProject()))) {
Ref ref = git.getRepository().getRef(branch.toString());
git.push().//
setRemote("origin").//
setRefSpecs(new RefSpec(ref.getName())).//
setCredentialsProvider(credentials).//
call();
}
}
}
public void pushTags(Train train) throws Exception {
for (Module module : train) {
logger.log(module.getProject(), "git push --tags");
try (Git git = new Git(getRepository(module.getProject()))) {
git.push().//
setRemote("origin").//
setPushTags().//
setCredentialsProvider(this.credentials).//
call();
}
}
}
public void update(Project project) throws Exception {
GitProject gitProject = new GitProject(project, server);
String repositoryName = gitProject.getRepositoryName();
Repository repository = getRepository(project);
try (Git git = new Git(repository)) {
if (workspace.hasProjectDirectory(project)) {
logger.log(project, "Found existing repository %s. Obtaining latest changes…", repositoryName);
logger.log(project, "git checkout master && git reset --hard && git fetch --tags && git pull origin master");
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);
}
}
}
public Tags getTags(Project project) throws Exception {
try (Git git = new Git(getRepository(project))) {
return new Tags(git.tagList().call().stream().map(ref -> new Tag(ref.getName())).collect(Collectors.toList()));
}
}
public void tagRelease(TrainIteration iteration) throws Exception {
for (ModuleIteration module : iteration) {
Branch branch = Branch.from(module);
Project project = module.getProject();
try (Git git = new Git(getRepository(module.getProject()))) {
logger.log(module, "git checkout %s", branch);
checkout(project, branch);
logger.log(module, "git pull origin %s", branch);
git.pull().call();
ObjectId hash = getReleaseHash(module);
Tag tag = getTags(project).createTag(module);
try (RevWalk walk = new RevWalk(git.getRepository())) {
RevCommit commit = walk.parseCommit(hash);
logger.log(module, "git tag %s %s", tag, hash.getName());
git.tag().setName(tag.toString()).setObjectId(commit).call();
}
}
}
}
/**
* Commits all changes currently made to all modules of the given {@link TrainIteration}. The summary can contain a
* single {@code %s} placeholder which the version of the current module will get replace into.
*
* @param iteration must not be {@literal null}.
* @param summary must not be {@literal null} or empty.
* @param details can be {@literal null} or empty.
* @throws Exception
*/
public void commit(TrainIteration iteration, String summary, String details) throws Exception {
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);
}
}
private 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())));
}
/**
* Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit
* message. If no files are given, all pending changes are commited.
*
* @param module must not be {@literal null}.
* @param summary must not be {@literal null} or empty.
* @param details can be {@literal null} or empty.
* @param files can be empty.
* @throws Exception
*/
public void commit(ModuleIteration module, String summary, String details, File... files) throws Exception {
Assert.notNull(module, "Module iteration must not be null!");
Assert.hasText(summary, "Summary must not be null or empty!");
Project project = module.getProject();
IssueTracker tracker = issueTracker.getPluginFor(project);
Ticket ticket = tracker.getReleaseTicketFor(module);
Commit commit = new Commit(ticket, summary, details);
String author = environment.getProperty("git.author");
String email = environment.getProperty("git.email");
logger.log(module, "git commit -m \"%s\" --author=\"%s <%s>\"", commit, author, email);
try (Git git = new Git(getRepository(module.getProject()))) {
git.commit().//
setMessage(commit.toString()).//
setAuthor(author, email).//
setAll(true).//
call();
}
}
private ObjectId getReleaseHash(ModuleIteration module) throws Exception {
Project project = module.getProject();
Ticket releaseTicket = issueTracker.getPluginFor(project).getReleaseTicketFor(module);
String trigger = String.format("%s - Release", releaseTicket.getId());
try (Git git = new Git(getRepository(module.getProject()))) {
for (RevCommit commit : git.log().setMaxCount(50).call()) {
String summary = commit.getShortMessage();
if (summary.startsWith(trigger)) {
return commit.getId();
}
}
}
throw new IllegalStateException(
String.format("Did not find a release commit for project %s (ticket id %s)", project, releaseTicket.getId()));
}
/**
* Returns the {@link Tag} that represents the {@link ArtifactVersion} of the given {@link Project}.
*
* @param project
* @param version
* @return
* @throws IOException
*/
private Tag findTagFor(Project project, ArtifactVersion version) throws Exception {
for (Tag tag : getTags(project)) {
if (tag.toArtifactVersion().equals(version)) {
return tag;
}
}
return null;
}
public void checkout(Project project, Branch branch) throws Exception {
try (Git git = new Git(getRepository(project))) {
Ref ref = git.getRepository().getRef(branch.toString());
CheckoutCommand checkout = git.checkout().setName(branch.toString());
if (ref == null) {
checkout.setCreateBranch(true).//
setUpstreamMode(SetupUpstreamMode.TRACK).//
setStartPoint("origin/".concat(branch.toString()));
}
checkout.call();
}
}
private Repository getRepository(Project project) throws Exception {
return FileRepositoryBuilder.create(workspace.getFile(".git", project));
}
public void clone(Project project) throws Exception {
Git git = Git.cloneRepository().//
setURI(getGitProject(project).getProjectUri()).//
setDirectory(workspace.getProjectDirectory(project)).//
call();
git.checkout().setName(Branch.MASTER.toString()).//
call();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.model.Project;
/**
* @author Oliver Gierke
*/
@EqualsAndHashCode
@RequiredArgsConstructor
public class GitProject {
private static final String PROJECT_PREFIX = "spring-data";
private final Project project;
private final GitServer server;
/**
* Returns the name of the repository the project is using.
*
* @return
*/
public String getRepositoryName() {
return String.format("%s-%s", PROJECT_PREFIX, project.getName().toLowerCase());
}
/**
* Returns the URI of the {@link Project}'s repository.
*
* @return
*/
public String getProjectUri() {
return server.getUri() + getRepositoryName();
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
/**
* @author Oliver Gierke
*/
public class GitServer {
private static final String SERVER_URI = "https://github.com/spring-projects/";
public String getUri() {
return SERVER_URI;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.model.ArtifactVersion;
/**
* Value object to represent an SCM tag.
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
@EqualsAndHashCode
public class Tag implements Comparable<Tag> {
private final String name;
/**
* 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.
*
* @return
*/
private String getVersionSource() {
return name.startsWith("v") ? name.substring(1) : name;
}
public ArtifactVersion toArtifactVersion() {
return ArtifactVersion.parse(getVersionSource());
}
/**
* Creates a new {@link Tag} for the given {@link ArtifactVersion} based on the format of the current one.
*
* @param version
* @return
*/
public Tag createNew(ArtifactVersion version) {
return new Tag(name.startsWith("v") ? "v".concat(version.toString()) : version.toString());
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return name;
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Tag that) {
return that.name.compareTo(this.name);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import lombok.EqualsAndHashCode;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.util.Assert;
/**
* Value object to represent a collection of {@link Tag}s.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
public class Tags implements Iterable<Tag> {
private final List<Tag> tags;
/**
* Creates a new {@link Tags} instance for the given {@link List} of {@link Tag}s.
*
* @param source must not be {@literal null}.
*/
Tags(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);
}
/**
* Returns the latest {@link Tag}.
*
* @return
*/
public Tag getLatest() {
return tags.get(0);
}
public Tag createTag(ModuleIteration iteration) {
return getLatest().createNew(ArtifactVersion.from(iteration));
}
/**
* Returns all {@link Tag}s as {@link List}.
*
* @return
*/
public List<Tag> asList() {
return tags;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Tag> iterator() {
return tags.iterator();
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.gradle;
import static org.springframework.data.release.model.Projects.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.io.Workspace.LineCallback;
import org.springframework.data.release.maven.Repository;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.stereotype.Component;
/**
* Gradle specific operations.
*
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class GradleOperations {
private static final String BUILD_GRADLE = "build.gradle";
private static final String GRADLE_PROPERTIES = "gradle.properties";
private static final String COMMONS_PROPERTY = "springDataCommonsVersion";
private static final String BUILD_PROPERTY = "springDataBuildVersion";
private final Workspace workspace;
private final Logger logger;
/**
* Updates all Gradle projects contained in the release.
*
* @param iteration
* @param phase
* @throws Exception
*/
public void updateProject(TrainIteration iteration, final Phase phase) throws Exception {
final Repository repository = new Repository(iteration.getIteration());
final ArtifactVersion commonsVersion = iteration.getModuleVersion(COMMONS);
final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD);
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
final Project project = module.getProject();
if (!isGradleProject(project)) {
continue;
}
workspace.processFile(GRADLE_PROPERTIES, project, new LineCallback() {
/*
* (non-Javadoc)
* @see org.springframework.data.release.io.Workspace.LineCallback#doWith(java.lang.String, long)
*/
@Override
public String doWith(String line, long number) {
if (line.contains(COMMONS_PROPERTY)) {
ArtifactVersion version = phase.equals(Phase.PREPARE) ? commonsVersion
: commonsVersion.getNextDevelopmentVersion();
logger.log(project, "Setting Spring Data Commons version in %s to %s.", GRADLE_PROPERTIES, version);
return String.format("%s=%s", COMMONS_PROPERTY, version);
}
if (line.contains(BUILD_PROPERTY)) {
ArtifactVersion version = phase.equals(Phase.PREPARE) ? buildVersion
: buildVersion.getNextDevelopmentVersion();
logger.log(project, "Setting Spring Data Build version in %s to %s.", GRADLE_PROPERTIES, version);
return String.format("%s=%s", BUILD_PROPERTY, version);
}
return line;
}
});
workspace.processFile(BUILD_GRADLE, project, new LineCallback() {
/*
* (non-Javadoc)
* @see org.springframework.data.release.io.Workspace.LineCallback#doWith(java.lang.String, long)
*/
@Override
public String doWith(String line, long number) {
String snapshotUrl = repository.getSnapshotUrl();
String releaseUrl = repository.getUrl();
String message = "Switching to Spring repository %s";
switch (phase) {
case CLEANUP:
logger.log(project, message, snapshotUrl);
return line.contains(releaseUrl) ? line.replace(releaseUrl, snapshotUrl) : line;
case PREPARE:
default:
logger.log(project, message, releaseUrl);
return line.contains(snapshotUrl) ? line.replace(snapshotUrl, releaseUrl) : line;
}
}
});
}
}
/**
* Returns whether the given project is a Gradle project (checks for the presence of a build.gradle file).
*
* @param project
* @return
*/
private boolean isGradleProject(Project project) {
return workspace.getFile(BUILD_GRADLE, project).exists();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.io;
import lombok.Value;
/**
* @author Oliver Gierke
*/
@Value
public class CommandResult {
private final int status;
private final String output;
private final Exception exception;
public boolean hasError() {
return status != 0;
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2011-2012 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.RequiredArgsConstructor;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
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;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.output.WriterOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.utils.Logger;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
/**
* Implementation of {@link OsCommandOperations} interface.
*
* @author Stefan Schmidt
* @author Oliver Gierke
* @since 1.2.0
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
class CommonsExecOsCommandOperations implements OsCommandOperations {
private static final Map<String, String> ENVIRONMENT = new HashMap<>();
private final Workspace workspace;
private final Logger logger;
/*
* (non-Javadoc)
* @see org.springframework.shell.commands.OsOperations#executeCommand(java.lang.String)
*/
@Async
@Override
public Future<CommandResult> executeCommand(String command) throws IOException {
return executeCommand((String) null, command);
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.io.OsCommandOperations#executeCommand(java.lang.String, org.springframework.data.release.model.Project)
*/
@Async
@Override
public Future<CommandResult> executeCommand(String command, Project project) throws IOException {
logger.log(project, command);
return executeCommand(command, workspace.getProjectDirectory(project), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.io.OsCommandOperations#executeAndListen(org.springframework.data.release.model.Project, java.lang.String)
*/
@Override
public Future<CommandResult> executeWithOutput(String command, Project project) throws IOException {
logger.log(project, command);
return executeCommand(command, workspace.getProjectDirectory(project), false);
}
private Future<CommandResult> executeCommand(String subfolder, String command) throws IOException {
File workingDirectory = workspace.getWorkingDirectory();
File executionDirectory = subfolder == null ? workingDirectory : new File(workingDirectory, subfolder);
return executeCommand(command, executionDirectory, true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.io.OsCommandOperations#executeForResult(java.lang.String, org.springframework.data.release.model.Project)
*/
@Async
@Override
public String executeForResult(String command, Project project) throws Exception {
return executeCommand(command, workspace.getProjectDirectory(project), true).get().getOutput();
}
private Future<CommandResult> executeCommand(String command, File executionDirectory, boolean silent)
throws IOException {
StringWriter writer = new StringWriter();
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
try (WriterOutputStream outputStream = new WriterOutputStream(writer)) {
String outerCommand = "/bin/bash -lc";
CommandLine outer = CommandLine.parse(outerCommand);
outer.addArgument(command, false);
DefaultExecutor executor = new DefaultExecutor();
executor.setWorkingDirectory(executionDirectory);
executor.setStreamHandler(new PumpStreamHandler(silent ? outputStream : System.out, null));
executor.execute(outer, ENVIRONMENT, resultHandler);
resultHandler.waitFor();
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return new AsyncResult<CommandResult>(
new CommandResult(resultHandler.getExitValue(), writer.toString(), resultHandler.getException()));
}
/**
* Adds {@code JAVA_HOME} to the ENVIRONMENT variables looking up the path to a Java 7.
*
* @throws Exception
*/
@PostConstruct
public void initialize() throws Exception {
String javaHome = executeCommand("/usr/libexec/java_home -F -v 1.8 -a x86_64 -d64").get().getOutput();
if (javaHome.endsWith("\n")) {
javaHome = javaHome.substring(0, javaHome.length() - 1);
}
ENVIRONMENT.put("JAVA_HOME", javaHome);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2011-2012 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 java.io.IOException;
import java.util.concurrent.Future;
import org.springframework.data.release.model.Project;
/**
* Operations type to allow execution of native OS commands from the Spring Roo shell.
*
* @author Stefan Schmidt
* @since 1.2.0
*/
public interface OsCommandOperations {
/**
* Attempts the execution of a commands and delegates the output to the standard logger.
*
* @param command the command to execute
* @throws IOException if an error occurs
*/
Future<CommandResult> executeCommand(String command) throws IOException;
Future<CommandResult> executeCommand(String command, Project project) throws IOException;
Future<CommandResult> executeWithOutput(String command, Project project) throws IOException;
String executeForResult(String command, Project project) throws Exception;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.io;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* @author Oliver Gierke
*/
@Configuration
@PropertySource("file:infrastructure.properties")
class OsConfiguration {
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.io;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
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;
import com.google.common.io.Files;
/**
* Abstraction of the workspace that is used to work with the {@link Project}'s repositories, execute builds, etc.
*
* @author Oliver Gierke
*/
@Component
@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;
/**
* Returns the current working directory.
*
* @return
*/
public File getWorkingDirectory() {
String workDir = environment.getProperty("io.workDir");
return new File(workDir.replace("~", System.getProperty("user.home")));
}
/**
* Returns the directory for the given {@link Project}.
*
* @param project must not be {@literal null}.
* @return
*/
public File getProjectDirectory(Project project) {
Assert.notNull(project, "Project must not be null!");
return new File(getWorkingDirectory(), project.getName());
}
/**
* Returns whether the project directory for the given project already exists.
*
* @param project must not be {@literal null}.
* @return
*/
public boolean hasProjectDirectory(Project project) {
Assert.notNull(project, "Project must not be null!");
return getProjectDirectory(project).exists();
}
/**
* Returns a file with the given name relative to the working directory for the given {@link Project}.
*
* @param name must not be {@literal null} or empty.
* @param project must not be {@literal null}.
* @return
*/
public File getFile(String name, Project project) {
Assert.hasText(name, "Filename must not be null or empty!");
Assert.notNull(project, "Project must not be null!");
return new File(getProjectDirectory(project), name);
}
public boolean processFile(String filename, Project project, LineCallback callback) throws Exception {
File file = getFile(filename, project);
if (!file.exists()) {
return false;
}
StringBuilder builder = new StringBuilder();
try (Scanner scanner = new Scanner(file)) {
long number = 0;
while (scanner.hasNextLine()) {
String result = callback.doWith(scanner.nextLine(), number++);
if (result != null) {
builder.append(result).append("\n");
}
}
}
writeContentToFile(filename, project, builder.toString());
return true;
}
private void writeContentToFile(String name, Project project, String content) throws IOException {
File file = getFile(name, project);
Files.write(content, file, UTF_8);
}
/**
* Initializes the working directory and creates the folders if necessary.
*
* @throws IOException
*/
@PostConstruct
public void setUp() throws IOException {
Path path = getWorkingDirectory().toPath();
if (!java.nio.file.Files.exists(path)) {
java.nio.file.Files.createDirectories(path);
}
}
public interface LineCallback {
String doWith(String line, long number);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.util.Date;
import java.util.Locale;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.shell.support.util.OsUtils;
/**
* @author Oliver Gierke
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
@EqualsAndHashCode
public class Changelog {
private final ModuleIteration module;
private final Tickets tickets;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
ArtifactVersion version = ArtifactVersion.from(module);
String headline = String.format("Changes in version %s (%s)", version,
new DateFormatter("YYYY-MM-dd").print(new Date(), Locale.US));
StringBuilder builder = new StringBuilder(headline).append(OsUtils.LINE_SEPARATOR);
for (int i = 0; i < headline.length(); i++) {
builder.append("-");
}
builder.append(OsUtils.LINE_SEPARATOR);
for (Ticket ticket : tickets) {
String summary = ticket.getSummary();
builder.append("* ").append(ticket.getId()).append(" - ").append(summary.trim());
if (!summary.endsWith(".")) {
builder.append(".");
}
builder.append(OsUtils.LINE_SEPARATOR);
}
return builder.toString();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import javax.xml.bind.DatatypeConverter;
import lombok.Value;
/**
* @author Oliver Gierke
*/
@Value
public class Credentials {
private final String username, password;
public String asBase64() {
return DatatypeConverter.printBase64Binary(String.format("%s:%s", username, password).getBytes());
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import lombok.Data;
import org.springframework.data.release.model.ModuleIteration;
/**
* @author Oliver Gierke
*/
@Data
class GitHubIssue {
private String number;
private String title;
public String getId() {
return "#".concat(number);
}
public boolean isReleaseTicket(ModuleIteration module) {
return title.contains("Release") && title.contains(module.getVersionString());
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.release.git.GitProject;
import org.springframework.data.release.git.GitServer;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Projects;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Tracker;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.UriTemplate;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
class GitHubIssueTracker implements IssueTracker {
private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}";
private static final String URI_TEMPLATE = "https://api.github.com/repos/spring-projects/{repoName}/issues?milestone={id}&state=all";
private static final ParameterizedTypeReference<List<GitHubMilestone>> MILESTONES_TYPE = new ParameterizedTypeReference<List<GitHubMilestone>>() {};
private static final ParameterizedTypeReference<List<GitHubIssue>> ISSUES_TYPE = new ParameterizedTypeReference<List<GitHubIssue>>() {};
private final RestOperations operations;
private final Logger logger;
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.IssueTracker#getReleaseTicketFor(org.springframework.data.release.model.ModuleIteration)
*/
@Override
@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));
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.IssueTracker#getChangelogFor(org.springframework.data.release.model.ModuleIteration)
*/
@Override
@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()));
}
logger.log(module, "Created changelog with %s entries.", tickets.size());
return new Changelog(module, new Tickets(tickets));
}
/*
* (non-Javadoc)
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
*/
@Override
public boolean supports(Project project) {
return project.uses(Tracker.GITHUB);
}
private List<GitHubIssue> getIssuesFor(ModuleIteration module) {
String repositoryName = new GitProject(module.getProject(), new GitServer()).getRepositoryName();
GitHubMilestone milestone = findMilestone(module, repositoryName);
Map<String, Object> parameters = new HashMap<>();
parameters.put("repoName", repositoryName);
parameters.put("id", milestone.getNumber());
return operations.exchange(URI_TEMPLATE, HttpMethod.GET, null, ISSUES_TYPE, parameters).getBody();
}
private GitHubMilestone findMilestone(ModuleIteration module, String repositoryName) {
for (String state : Arrays.asList("close", "open")) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("repoName", repositoryName);
parameters.put("state", state);
URI milestoneUri = new UriTemplate(MILESTONE_URI).expand(parameters);
logger.log(module, "Looking up milestone from %s…", milestoneUri);
List<GitHubMilestone> exchange = operations.exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE,
parameters).getBody();
GitHubMilestone milestone = null;
for (GitHubMilestone candidate : exchange) {
if (candidate.getTitle().contains(module.getVersionString())) {
milestone = candidate;
}
}
if (milestone != null) {
logger.log(module, "Found milestone %s.", milestone);
return milestone;
}
}
throw new IllegalStateException(String.format("No milestone found containing %s!", module.getVersionString()));
}
public static void main(String[] args) {
try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"META-INF/spring/spring-shell-plugin.xml")) {
IssueTracker tracker = context.getBean("gitHubIssueTracker", IssueTracker.class);
TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2);
ModuleIteration module = iteration.getModule(Projects.BUILD);
Changelog changelog = tracker.getChangelogFor(module);
System.out.println(changelog);
System.out.println(tracker.getReleaseTicketFor(module));
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import lombok.Data;
/**
* @author Oliver Gierke
*/
@Data
class GitHubMilestone {
private String title;
private Long number;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.plugin.core.Plugin;
/**
* @author Oliver Gierke
*/
public interface IssueTracker extends Plugin<Project> {
/**
* Returns the {@link Ticket} that tracks modifications in the context of a release.
*
* @param module the module to lookup the {@link Ticket} for.
* @return
*/
Ticket getReleaseTicketFor(ModuleIteration module);
Changelog getChangelogFor(ModuleIteration iteration);
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
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.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Tracker;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.UriTemplate;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
class Jira implements JiraConnector {
private static final String JIRA_HOST = "https://jira.spring.io";
private static final String BASE_URI = "/rest/api/2";
private static final String SEARCH_TEMPLATE = JIRA_HOST + BASE_URI
+ "/search?jql={jql}&fields={fields}&startAt={startAt}";
private final RestOperations operations;
private final Logger logger;
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.JiraConnector#flushTickets()
*/
@Override
@CacheEvict(value = "tickets", allEntries = true)
public void reset() {
}
@Cacheable("release-tickets")
public Ticket getReleaseTicketFor(ModuleIteration iteration) {
JqlQuery query = JqlQuery.from(iteration).and("summary ~ \"Release\"");
Map<String, Object> parameters = new HashMap<>();
parameters.put("jql", query);
parameters.put("fields", "summary");
parameters.put("startAt", 0);
JiraIssues issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, null, JiraIssues.class, parameters)
.getBody();
JiraIssue issue = issues.getIssues().get(0);
return new Ticket(issue.getKey(), issue.getFields().getSummary());
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.JiraConnector#getTicketsFor(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration, org.springframework.data.release.jira.Credentials)
*/
@Override
@Cacheable("tickets")
public Tickets getTicketsFor(TrainIteration iteration, Credentials credentials) {
JqlQuery query = JqlQuery.from(iteration);
HttpHeaders headers = new HttpHeaders();
int startAt = 0;
List<Ticket> tickets = new ArrayList<>();
JiraIssues issues = null;
if (credentials != null) {
query = query.and("assignee = currentUser()");
headers.set("Authorization", String.format("Basic %s", credentials.asBase64()));
logger.log(iteration, "Retrieving tickets (for user %s)…", credentials.getUsername());
} else {
logger.log(iteration, "Retrieving tickets…");
}
query = query.orderBy("updatedDate DESC");
do {
Map<String, Object> parameters = new HashMap<>();
parameters.put("jql", query);
parameters.put("fields", "summary,fixVersions");
parameters.put("startAt", startAt);
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());
for (JiraIssue issue : issues) {
if (!issue.wasBackportedFrom(iteration.getTrain())) {
tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary()));
}
}
startAt = issues.getNextStartAt();
} while (issues.hasMoreResults());
return new Tickets(Collections.unmodifiableList(tickets), issues.getTotal());
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
*/
@Override
public void verifyBeforeRelease(TrainIteration iteration) {
// for each module
// - make sure only one ticket is open
}
/*
*
* (non-Javadoc)
* @see org.springframework.data.release.jira.JiraConnector#closeIteration(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration, org.springframework.data.release.jira.Credentials)
*/
@Override
public void closeIteration(TrainIteration iteration, Credentials credentials) {
// for each module
// - close all tickets
// -- make sure only one ticket is open
// -- resolve open ticket
// -- close tickets
// - mark version as releases
// - if no next version exists, create
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.jira.JiraConnector#getChangelogFor(org.springframework.data.release.model.Module, org.springframework.data.release.model.Iteration)
*/
@Override
@Cacheable("changelogs")
public Changelog getChangelogFor(ModuleIteration module) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("jql", JqlQuery.from(module));
parameters.put("fields", "summary,fixVersions");
parameters.put("startAt", 0);
URI searchUri = new UriTemplate(SEARCH_TEMPLATE).expand(parameters);
logger.log(module, "Looking up JIRA issues from %s…", searchUri);
JiraIssues issues = operations.getForObject(searchUri, JiraIssues.class);
List<Ticket> tickets = new ArrayList<>();
for (JiraIssue issue : issues) {
tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary()));
}
logger.log(module, "Created changelog with %s entries.", tickets.size());
return new Changelog(module, new Tickets(tickets, tickets.size()));
}
/*
* (non-Javadoc)
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
*/
@Override
public boolean supports(Project project) {
return project.uses(Tracker.JIRA);
}
public static void main(String[] args) {
try (ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
"META-INF/spring/spring-shell-plugin.xml")) {
JiraConnector tracker = context.getBean(JiraConnector.class);
TrainIteration iteration = new TrainIteration(ReleaseTrains.CODD, Iteration.SR2);
ModuleIteration module = iteration.getModule("JPA");
// Changelog changelog = tracker.getChangelogFor(module);
// System.out.println(changelog);
System.out.println(tracker.getReleaseTicketFor(module));
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.util.ArrayList;
import java.util.List;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.plugin.core.config.EnablePluginRegistries;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Oliver Gierke
*/
@Configuration
@EnableCaching
@PropertySource(value = "file:jira.properties", ignoreResourceNotFound = true)
@EnablePluginRegistries({ IssueTracker.class })
class JiraConfiguration {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
@Bean
public ObjectMapper jacksonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
@Bean
public RestTemplate restTemplate() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(jacksonObjectMapper());
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(converter);
RestTemplate template = new RestTemplate();
template.setMessageConverters(converters);
return template;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
/**
* @author Oliver Gierke
*/
public interface JiraConnector extends IssueTracker {
void reset();
/**
* Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}.
*
* @param train must not be {@literal null}.
* @param iteration must not be {@literal null}.
* @return
*/
Tickets getTicketsFor(TrainIteration iteration, Credentials credentials);
void verifyBeforeRelease(TrainIteration iteration);
void closeIteration(TrainIteration iteration, Credentials credentials);
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.release.model.Train;
/**
* Value object to bind REST responses to.
*
* @author Oliver Gierke
*/
@Data
class JiraIssue {
private String key;
private Fields fields;
/**
* Returns whether the ticket is only fixed in the given {@link Train}. {@literal false} indicates the {@link Ticket}
* has been resolved for
*
* @param train
* @return
*/
public boolean wasBackportedFrom(Train train) {
List<FixVersions> fixVersions = fields.getFixVersions();
return !(fixVersions.size() == 1 && fixVersions.get(0).name.contains(train.getName()));
}
@Data
static class Fields {
String summary;
List<FixVersions> fixVersions;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
static class FixVersions {
String name;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.util.Iterator;
import java.util.List;
import lombok.Data;
/**
* @author Oliver Gierke
*/
@Data
class JiraIssues implements Iterable<JiraIssue> {
private int startAt;
private int maxResults;
private int total;
private List<JiraIssue> issues;
public int getNextStartAt() {
return startAt + issues.size();
}
public boolean hasMoreResults() {
return startAt + issues.size() < total;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<JiraIssue> iterator() {
return issues.iterator();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import lombok.Value;
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.Train;
/**
* @author Oliver Gierke
*/
@Value
class JiraVersion {
private final Module module;
private final Train train;
private final Iteration iteration;
public JiraVersion(ModuleIteration moduleIteration) {
this.module = moduleIteration.getModule();
this.iteration = moduleIteration.getIteration();
this.train = moduleIteration.getTrain();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
Iteration iteration = this.iteration.isInitialIteration() && module.hasCustomFirstIteration() ? module
.getCustomFirstIteration() : this.iteration;
if (iteration.isServiceIteration()) {
return String.format("%s.%s (%s %s)", module.getVersion(), iteration.getBugfixValue(), train.getName(),
iteration.getName());
}
return String.format("%s %s (%s)", module.getVersion(), iteration.getName(), train.getName());
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import static org.springframework.data.release.model.Projects.*;
import java.util.ArrayList;
import java.util.List;
import lombok.Value;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.util.StringUtils;
/**
* @author Oliver Gierke
*/
@Value
class JqlQuery {
private static final String PROJECT_VERSION_TEMPLATE = "project = %s AND fixVersion = \"%s\"";
private final String query;
public JqlQuery and(String clause) {
return new JqlQuery(String.format("(%s) AND %s", query, clause));
}
public JqlQuery orderBy(String orderBy) {
return new JqlQuery(String.format("%s ORDER BY %s", query, orderBy));
}
public static JqlQuery from(ModuleIteration iteration) {
JiraVersion version = new JiraVersion(iteration);
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
}
public static JqlQuery from(TrainIteration iteration) {
List<String> parts = new ArrayList<>();
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
JiraVersion version = new JiraVersion(module);
parts.add(String.format(PROJECT_VERSION_TEMPLATE, module.getProjectKey(), version));
}
return new JqlQuery(StringUtils.collectionToDelimitedString(parts, " OR "));
}
@Override
public String toString() {
return query;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import lombok.Value;
/**
* Value object to represent a {@link Ticket}.
*
* @author Oliver Gierke
*/
@Value
public class Ticket {
private final String id;
private final String summary;
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%13s - %s", id, summary);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import java.util.Iterator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.springframework.util.StringUtils;
/**
* Value object to represent a list of {@link Ticket}s.
*
* @author Oliver Gierke
*/
@Value
@RequiredArgsConstructor
public class Tickets implements Iterable<Ticket> {
private final List<Ticket> tickets;
private final int overallTotal;
public Tickets(List<Ticket> tickets) {
this(tickets, tickets.size());
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Ticket> iterator() {
return tickets.iterator();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(String.format("Train only tickets: %s of %s", tickets.size(), overallTotal));
builder.append("\n");
builder.append(StringUtils.collectionToDelimitedString(tickets, "\n"));
return builder.toString();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import static org.springframework.data.release.model.Projects.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.util.Assert;
/**
* Value object to represent a Maven {@link Artifact}.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
public class Artifact {
private static final GroupId GROUP_ID = new GroupId("org.springframework.data");
private final ModuleIteration module;
private final Repository repository;
private final @Getter ArtifactVersion version;
/**
* Creates a new {@link Artifact} for the given {@link ModuleIteration}.
*
* @param module must not be {@literal null}.
*/
public Artifact(ModuleIteration module) {
Assert.notNull(module, "Module iteration must not be null!");
this.module = module;
this.repository = new Repository(module.getIteration());
this.version = ArtifactVersion.from(module);
}
/**
* Returns the Maven artifact identifier.
*
* @return
*/
public String getArtifactId() {
String artifactId = String.format("spring-data-%s", module.getProject().getName().toLowerCase());
return REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId;
}
public ArtifactVersion getNextDevelopmentVersion() {
return version.getNextDevelopmentVersion();
}
/**
* Returns the URL pointing to the artifacts.
*
* @return
*/
public String getRootUrl() {
return String.format("%s/%s/%s/%s", repository.getUrl(), GROUP_ID.asPath(), getArtifactId(), version);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import lombok.Value;
/**
* Value object to represent an artifacts group identifier.
*
* @author Oliver Gierke
*/
@Value
class GroupId {
private final String value;
public String asPath() {
return value.replace('.', '/');
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.xmlbeam.ProjectionFactory;
import org.xmlbeam.XBProjector;
import org.xmlbeam.XBProjector.Flags;
import org.xmlbeam.config.DefaultXMLFactoriesConfig;
import org.xmlbeam.config.DefaultXMLFactoriesConfig.NamespacePhilosophy;
/**
* @author Oliver Gierke
*/
@Configuration
class MavenConfig {
@Bean
public ProjectionFactory projectionFactory() {
DefaultXMLFactoriesConfig config = new DefaultXMLFactoriesConfig();
config.setNamespacePhilosophy(NamespacePhilosophy.AGNOSTIC);
config.setOmitXMLDeclaration(false);
XBProjector projector = new XBProjector(config, Flags.TO_STRING_RENDERS_XML);
return projector;
}
}

View File

@@ -0,0 +1,258 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import static org.springframework.data.release.model.Phase.*;
import static org.springframework.data.release.model.Projects.*;
import lombok.RequiredArgsConstructor;
import java.io.File;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.release.io.CommandResult;
import org.springframework.data.release.io.OsCommandOperations;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
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.Logger;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.xmlbeam.ProjectionFactory;
import org.xmlbeam.io.XBFileIO;
/**
* @author Oliver Gierke
*/
@Component
@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;
private final ProjectionFactory projectionFactory;
private final OsCommandOperations os;
private final Logger logger;
public Pom getMavenProject(Project project) throws IOException {
File file = workspace.getFile(POM_XML, project);
return projectionFactory.io().file(file).read(Pom.class);
}
/**
* Updates the POM files for all Maven projects contained in the iteration:
* <ol>
* <li>Updates the BOM POM.</li>
* <li>Updates the dependency version to Spring Data Commons to the current release version for all projects depending
* on it.</li>
* <li>Switches to the Spring release Maven repository.</li>
* </ol>
* If {@link Phase} is {@link Phase#CLEANUP} the changes will be rolled back.
*
* @param iteration must not be {@literal null}.
* @param phase must not be {@literal null}.
* @throws Exception
*/
public void updatePom(TrainIteration iteration, final Phase phase) throws Exception {
Assert.notNull(iteration, "Train iteration must not be null!");
Assert.notNull(phase, "Phase must not be null!");
updateBomPom(iteration, phase);
final Repository repository = new Repository(iteration.getIteration());
final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD);
final ArtifactVersion nextBuildVersion = buildVersion.getNextDevelopmentVersion();
// Fix version of shared resources to to-be-released version.
execute(workspace.getFile("parent/pom.xml", BUILD), new PomCallback<ParentPom>() {
@Override
public ParentPom doWith(ParentPom pom) {
pom.setSharedResourcesVersion(phase.equals(PREPARE) ? buildVersion : nextBuildVersion);
return pom;
}
});
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
final Project project = module.getProject();
if (!isMavenProject(project)) {
logger.log(module, "No pom.xml file found, skipping project.");
continue;
}
execute(workspace.getFile(POM_XML, project), new PomCallback<Pom>() {
@Override
public Pom doWith(Pom pom) {
for (Project dependency : project.getDependencies()) {
String dependencyProperty = dependency.getDependencyProperty();
if (pom.getProperty(dependencyProperty) == null) {
continue;
}
ArtifactVersion dependencyVersion = iteration.getModuleVersion(dependency);
ArtifactVersion version = CLEANUP.equals(phase) ? dependencyVersion.getNextDevelopmentVersion()
: dependencyVersion;
logger.log(project, "Updating %s dependency version property %s to %s.", dependency.getFullName(),
dependencyProperty, version);
pom.setProperty(dependencyProperty, version);
}
ArtifactVersion version = CLEANUP.equals(phase) ? nextBuildVersion : buildVersion;
logger.log(project, "Updating Spring Data Build Parent version to %s.", version);
pom.setParentVersion(version);
updateRepository(project, pom, repository, phase);
return pom;
}
});
}
}
/**
* Triggers building the distribution artifacts for all Maven projects of the given {@link Train}.
*
* @param train
* @param iteration
* @throws IOException
* @throws InterruptedException
*/
public void triggerDistributionBuild(TrainIteration iteration) throws Exception {
for (ModuleIteration moduleIteration : iteration) {
Project project = moduleIteration.getProject();
if (BUILD.equals(project)) {
continue;
}
if (!isMavenProject(project)) {
logger.log(project, "Skipping project as no pom.xml could be found in the working directory!");
continue;
}
logger.log(project, "Triggering distribution build…");
ArtifactVersion version = ArtifactVersion.from(moduleIteration);
String command = "mvn clean deploy -DskipTests -Pdistribute";
if (version.isMilestoneVersion()) {
command = command.concat(",milestone");
} else if (version.isReleaseVersion()) {
command = command.concat(",release");
}
CommandResult result = os.executeWithOutput(command, moduleIteration.getProject()).get();
if (result.hasError()) {
throw result.getException();
}
logger.log(project, "Successfully finished distribution build!");
}
}
private boolean isMavenProject(Project project) {
return workspace.getFile(POM_XML, project).exists();
}
private void updateBomPom(final TrainIteration iteration, final Phase phase) throws Exception {
File bomPomFile = workspace.getFile("bom/pom.xml", BUILD);
logger.log(BUILD, "Updating BOM pom.xml…");
execute(bomPomFile, new PomCallback<Pom>() {
@Override
public Pom doWith(Pom pom) {
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
Artifact artifact = new Artifact(module);
ArtifactVersion version = artifact.getVersion();
version = PREPARE.equals(phase) ? version : version.getNextDevelopmentVersion();
logger.log(BUILD, "%s", module);
pom.setDependencyManagementVersion(artifact.getArtifactId(), version);
for (String additionalArtifact : module.getProject().getAdditionalArtifacts()) {
pom.setDependencyManagementVersion(additionalArtifact, version);
}
}
return pom;
}
});
}
private void updateRepository(Project project, Pom pom, Repository repository, Phase phase) {
String message = "Switching to Spring repository %s (%s).";
if (PREPARE.equals(phase)) {
logger.log(project, message, repository.getId(), repository.getUrl());
pom.setRepositoryId(repository.getSnapshotId(), repository.getId());
pom.setRepositoryUrl(repository.getId(), repository.getUrl());
} else {
logger.log(project, message, repository.getSnapshotId(), repository.getSnapshotUrl());
pom.setRepositoryId(repository.getId(), repository.getSnapshotId());
pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl());
}
}
@SuppressWarnings("unchecked")
private <T extends Pom> void execute(File file, PomCallback<T> callback) throws Exception {
XBFileIO io = projectionFactory.io().file(file);
Class<?> typeArgument = GenericTypeResolver.resolveTypeArgument(callback.getClass(), PomCallback.class);
T pom = (T) io.read(typeArgument);
pom = callback.doWith(pom);
io.write(pom);
}
private interface PomCallback<T extends Pom> {
public T doWith(T pom);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import lombok.Value;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Module;
/**
* @author Oliver Gierke
*/
@Value
class MavenProject {
private final Module module;
public String getGroupId() {
return "org.springframework.data";
}
public String getArtifactId() {
return String.format("spring-data-%s", module.getProject().getName().toLowerCase());
}
public ArtifactVersion getReleaseVersion() {
return new ArtifactVersion(module.getVersion());
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import org.springframework.data.release.model.ArtifactVersion;
import org.xmlbeam.annotation.XBValue;
import org.xmlbeam.annotation.XBWrite;
/**
* @author Oliver Gierke
*/
public interface ParentPom extends Pom {
@XBWrite("/project/profiles/profile[id=\"distribute\"]/dependencies/dependency/version")
void setSharedResourcesVersion(@XBValue ArtifactVersion value);
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import org.springframework.data.release.model.ArtifactVersion;
import org.xmlbeam.annotation.XBRead;
import org.xmlbeam.annotation.XBValue;
import org.xmlbeam.annotation.XBWrite;
/**
* @author Oliver Gierke
*/
public interface Pom {
@XBRead("/project")
Artifact getArtifactId();
@XBRead("/project/version")
ArtifactVersion getVersion();
@XBWrite("/project/version")
void setVersion(String version);
@XBWrite("/project/parent/version")
void setParentVersion(ArtifactVersion version);
@XBRead("/project/properties/{0}")
String getProperty(String property);
@XBWrite("/project/properties/{0}")
void setProperty(String property, @XBValue ArtifactVersion value);
@XBWrite("/project/repositories/repository[id=\"{0}\"]/id")
void setRepositoryId(String oldId, @XBValue String newId);
@XBWrite("/project/repositories/repository[id=\"{0}\"]/url")
void setRepositoryUrl(String id, @XBValue String url);
/**
* Sets the version of the dependency with the given artifact identifier to the given {@link ArtifactVersion}.
*
* @param artifactId
* @param version
*/
@XBWrite("/project/dependencies/dependency[artifactId=\"{0}\"]/version")
Pom setDependencyVersion(String artifactId, @XBValue ArtifactVersion version);
@XBWrite("/project/dependencyManagement/dependencies/dependency[artifactId=\"{0}\"]/version")
Pom setDependencyManagementVersion(String artifactId, @XBValue ArtifactVersion version);
public interface Repository {
@XBRead("child::id")
String getId();
@XBRead("child::url")
String getUrl();
}
public interface Artifact {
@XBRead("child::groupId")
String getGroupId();
@XBRead("child::artifactId")
String getArtifactId();
@XBRead("child::version")
String getVersion();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.maven;
import org.springframework.data.release.model.Iteration;
/**
* @author Oliver Gierke
*/
public class Repository {
private static final String BASE = "https://repo.spring.io/libs-";
private final String id;
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");
}
public String getId() {
return id;
}
public String getSnapshotId() {
return "spring-libs-snapshot";
}
public String getUrl() {
return url;
}
public String getSnapshotUrl() {
return BASE.concat("snapshot");
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.misc;
import lombok.RequiredArgsConstructor;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.git.GitOperations;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.io.Workspace.LineCallback;
import org.springframework.data.release.jira.Changelog;
import org.springframework.data.release.jira.IssueTracker;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class ReleaseOperations {
private static final Set<String> CHANGELOG_LOCATIONS;
static {
Set<String> locations = new HashSet<>();
locations.add("src/main/resources/changelog.txt"); // for Maven projects
locations.add("docs/src/info/changelog.txt"); // for Gradle projects
CHANGELOG_LOCATIONS = Collections.unmodifiableSet(locations);
}
private final PluginRegistry<IssueTracker, Project> trackers;
private final Workspace workspace;
private final GitOperations git;
private final Logger logger;
/**
* Creates {@link Changelog} instances for all modules of the given {@link Train} and {@link Iteration}.
*
* @param train must not be {@literal null}.
* @param iteration must not be {@literal null}.
* @throws Exception
*/
public void prepareChangelogs(TrainIteration iteration) throws Exception {
Assert.notNull(iteration, "Iteration must not be null!");
for (ModuleIteration module : iteration) {
final Changelog changelog = trackers.getPluginFor(module.getProject()).getChangelogFor(module);
for (String location : CHANGELOG_LOCATIONS) {
boolean processed = workspace.processFile(location, module.getProject(), new LineCallback() {
@Override
public String doWith(String line, long number) {
if (line.startsWith("=")) {
StringBuilder builder = new StringBuilder();
builder.append(line).append("\n\n");
builder.append(changelog.toString());
return builder.toString();
} else {
return line;
}
}
});
if (processed) {
git.commit(module, "Updated changelog.", null);
logger.log(module.getProject(), "Updated changelog %s.", location);
}
}
}
}
public void updateResources(TrainIteration iteration) throws Exception {
for (final ModuleIteration module : iteration) {
boolean processed = workspace.processFile("src/main/resources/notice.txt", module.getProject(),
new LineCallback() {
@Override
public String doWith(String line, long number) {
if (number != 0) {
return line;
}
return module.toString();
}
});
if (processed) {
logger.log(module, "Updated notice.txt.");
}
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.springframework.util.Assert;
/**
* Value object to represent version of a particular artifact.
*
* @author Oliver Gierke
*/
@RequiredArgsConstructor
@EqualsAndHashCode
public class ArtifactVersion implements Comparable<ArtifactVersion> {
private static final String RELEASE_SUFFIX = "RELEASE";
private static final String MILESTONE_SUFFIX = "M\\d|RC\\d";
private static final String SNAPSHOT_SUFFIX = "BUILD-SNAPSHOT";
private static final String VALID_SUFFIX = String.format("%s|%s|%s", RELEASE_SUFFIX, MILESTONE_SUFFIX,
SNAPSHOT_SUFFIX);
private final Version version;
private final String suffix;
/**
* Creates a new {@link ArtifactVersion} from the given logical {@link Version}.
*
* @param version must not be {@literal null}.
*/
public ArtifactVersion(Version version) {
Assert.notNull(version, "Version must not be null!");
this.version = version;
this.suffix = RELEASE_SUFFIX;
}
/**
* Parses the given {@link String} into an {@link ArtifactVersion}.
*
* @param source must not be {@literal null} or empty.
* @return
*/
public static ArtifactVersion parse(String source) {
Assert.hasText(source, "Version source must not be null or empty!");
int suffixStart = source.lastIndexOf('.');
Version version = Version.parse(source.substring(0, suffixStart));
String suffix = source.substring(suffixStart + 1);
Assert.isTrue(suffix.matches(VALID_SUFFIX), "Invalid version suffix!");
return new ArtifactVersion(version, suffix);
}
/**
* Creates a new {@link ArtifactVersion} from the given {@link IterationVersion}.
*
* @param iterationVersion must not be {@literal null}.
* @return
*/
public static ArtifactVersion from(IterationVersion iterationVersion) {
Assert.notNull(iterationVersion, "IterationVersion must not be null!");
Version version = iterationVersion.getVersion();
Iteration iteration = iterationVersion.getIteration();
if (iteration.isGAVersion()) {
return new ArtifactVersion(version, RELEASE_SUFFIX);
}
if (iteration.isServiceIteration()) {
Version bugfixVersion = version.withBugfix(iteration.getBugfixValue());
return new ArtifactVersion(bugfixVersion, RELEASE_SUFFIX);
}
return new ArtifactVersion(version, iteration.getName());
}
/**
* Returns the release version for the current one.
*
* @return
*/
public ArtifactVersion getReleaseVersion() {
return new ArtifactVersion(version, RELEASE_SUFFIX);
}
/**
* Returns the snapshot version of the current one.
*
* @return
*/
public ArtifactVersion getSnapshotVersion() {
return new ArtifactVersion(version, SNAPSHOT_SUFFIX);
}
/**
* Returns whether the version is a release version.
*
* @return
*/
public boolean isReleaseVersion() {
return suffix.equals(RELEASE_SUFFIX);
}
/**
* Returns whether the version is a milestone version.
*
* @return
*/
public boolean isMilestoneVersion() {
return suffix.matches(MILESTONE_SUFFIX);
}
public ArtifactVersion getNextDevelopmentVersion() {
if (suffix.equals(SNAPSHOT_SUFFIX)) {
return this;
}
if (suffix.equals(RELEASE_SUFFIX)) {
return new ArtifactVersion(version.nextBugfix(), SNAPSHOT_SUFFIX);
}
return new ArtifactVersion(version, SNAPSHOT_SUFFIX);
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(ArtifactVersion that) {
return this.toString().compareTo(that.toString());
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s.%s", version.toMajorMinorBugfix(), suffix);
}
/**
* Returns the {@link String} of the plain version (read: x.y.z, ommitting trailing bugfix zeros).
*
* @return
*/
public String toShortString() {
return version.toString();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.NonNull;
import lombok.Value;
import org.springframework.util.Assert;
/**
* Value object to represent an individual release train iteration.
*
* @author Oliver Gierke
*/
@Value
public class Iteration {
public static final Iteration SR6 = new Iteration("SR6", null);
public static final Iteration SR5 = new Iteration("SR6", SR6);
public static final Iteration SR4 = new Iteration("SR4", SR5);
public static final Iteration SR3 = new Iteration("SR3", SR4);
public static final Iteration SR2 = new Iteration("SR2", SR3);
public static final Iteration SR1 = new Iteration("SR1", SR2);
public static final Iteration GA = new Iteration("GA", SR1);
public static final Iteration RC1 = new Iteration("RC1", GA);
public static final Iteration M1 = new Iteration("M1", RC1);
/**
* The name of the iteration.
*/
private final @NonNull String name;
private final Iteration next;
Iteration(String name, Iteration next) {
Assert.hasText(name, "Name must not be null or empty!");
this.name = name;
this.next = next;
}
public boolean isGAVersion() {
return this.equals(GA);
}
public boolean isPublicVersion() {
return isServiceIteration() || this.equals(GA);
}
public boolean isServiceIteration() {
return name.startsWith("SR");
}
public boolean isNext(Iteration iteration) {
return next.equals(iteration);
}
public boolean isInitialIteration() {
return this.equals(M1);
}
public int getBugfixValue() {
return name.startsWith("SR") ? Integer.parseInt(name.substring(2)) : 0;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
/**
* A {@link Version} tied to an {@link Iteration}.
*
* @author Oliver Gierke
*/
public interface IterationVersion {
Version getVersion();
Iteration getIteration();
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.Value;
import org.springframework.util.Assert;
/**
* @author Oliver Gierke
*/
@Value
public class Module {
private final Project project;
private final Version version;
private final Iteration customFirstIteration;
Module(Project project, String version) {
this(project, version, null);
}
Module(Project project, String version, String customFirstIteration) {
Assert.notNull(project, "Project must not be null!");
this.project = project;
this.version = Version.parse(version);
this.customFirstIteration = customFirstIteration == null ? null
: new Iteration(customFirstIteration, Iteration.RC1);
}
public boolean hasName(String name) {
return project.getName().equalsIgnoreCase(name);
}
public boolean hasCustomFirstIteration() {
return customFirstIteration != null;
}
public Module next(Transition transition) {
Version nextVersion = Transition.MAJOR.equals(transition) ? version.nextMajor() : version.nextMinor();
return new Module(project, nextVersion.toString());
}
@Override
public String toString() {
return String.format("Spring Data %s %s - %s", project.getName(), version, project.getKey());
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @author Oliver Gierke
*/
@RequiredArgsConstructor
@EqualsAndHashCode
public class ModuleIteration implements IterationVersion {
private final @Getter Module module;
private final Iteration iteration;
private final @Getter Train train;
public ProjectKey getProjectKey() {
return module.getProject().getKey();
}
public Project getProject() {
return module.getProject();
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.model.IterationVersion#getVersion()
*/
@Override
public Version getVersion() {
return module.getVersion();
}
/*
* (non-Javadoc)
* @see org.springframework.data.release.model.IterationVersion#getIteration()
*/
public Iteration getIteration() {
return this.iteration.isInitialIteration() && this.module.hasCustomFirstIteration() ? module
.getCustomFirstIteration() : this.iteration;
}
/**
* Returns the {@link String} representation of the logical version of the {@link ModuleIteration}.
*
* @return
*/
public String getVersionString() {
StringBuilder builder = new StringBuilder();
builder.append(ArtifactVersion.from(this).toShortString());
if (!iteration.isServiceIteration()) {
builder.append(" ").append(iteration.getName());
}
return builder.toString();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s %s", module.getProject().getFullName(), getVersionString());
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
/**
* @author Oliver Gierke
*/
public enum Phase {
PREPARE, CLEANUP;
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
/**
* @author Oliver Gierke
*/
@ToString
@EqualsAndHashCode
public class Project {
private final @Getter ProjectKey key;
private final @Getter String name;
private final @Getter List<Project> dependencies;
private final Tracker tracker;
private final @Getter List<String> additionalArtifacts;
Project(String key, String name, List<Project> dependencies) {
this(key, name, Tracker.JIRA, dependencies, Collections.emptyList());
}
Project(String key, String name, List<Project> dependencies, List<String> additionalArtifacts) {
this(key, name, Tracker.JIRA, dependencies, additionalArtifacts);
}
Project(String key, String name, Tracker tracker, List<Project> dependencies, List<String> additionalArtifacts) {
this.key = new ProjectKey(key);
this.name = name;
this.dependencies = dependencies;
this.tracker = tracker;
this.additionalArtifacts = additionalArtifacts;
}
public boolean uses(Tracker tracker) {
return this.tracker.equals(tracker);
}
public String getFullName() {
return "Spring Data ".concat(name);
}
public String getDependencyProperty() {
return "springdata.".concat(name.toLowerCase());
}
/**
* Returns whether the current project depends on the given one.
*
* @param project must not be {@literal null}.
* @return
*/
public boolean dependsOn(Project project) {
Assert.notNull(project, "Project must not be null!");
return dependencies.contains(project);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013 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 lombok.NonNull;
import lombok.Value;
/**
* @author Oliver Gierke
*/
@Value
public class ProjectKey {
private final @NonNull String key;
@Override
public String toString() {
return key;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author Oliver Gierke
*/
public class Projects {
public static final Project COMMONS, BUILD, REST, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH,
REDIS, GEMFIRE;
public static final List<Project> PROJECTS;
static {
BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB, Collections.emptyList(), Collections.emptyList());
COMMONS = new Project("DATACMNS", "Commons", Arrays.asList(BUILD));
JPA = new Project("DATAJPA", "JPA", Arrays.asList(COMMONS));
MONGO_DB = new Project("DATAMONGO", "MongoDB", Arrays.asList(COMMONS),
Arrays.asList("spring-data-mongodb-cross-store", "spring-data-mongodb-log4j"));
NEO4J = new Project("DATAGRAPH", "Neo4j", Arrays.asList(COMMONS));
SOLR = new Project("DATASOLR", "Solr", Arrays.asList(COMMONS));
COUCHBASE = new Project("DATACOUCH", "Couchbase", Arrays.asList(COMMONS));
CASSANDRA = new Project("DATACASS", "Cassandra", Arrays.asList(COMMONS), Arrays.asList("spring-cql"));
ELASTICSEARCH = new Project("DATAES", "Elasticsearch", Arrays.asList(COMMONS));
REDIS = new Project("DATAREDIS", "Redis", Collections.emptyList());
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"));
PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS,
GEMFIRE, REST);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013 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 lombok.Value;
import org.joda.time.LocalDate;
/**
* @author Oliver Gierke
*/
@Value
public class Release {
private final String id;
private final ProjectKey projectKey;
private final String name;
private final String description;
private final LocalDate date;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import static org.springframework.data.release.model.Projects.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Oliver Gierke
*/
public class ReleaseTrains {
public static final List<Train> TRAINS;
public static final Train CODD, DIJKSTRA, EVANS, FOWLER;
static {
CODD = codd();
DIJKSTRA = dijkstra();
EVANS = DIJKSTRA.next("Evans", Transition.MINOR);
FOWLER = EVANS.next("Fowler", Transition.MINOR);
// Trains
TRAINS = Arrays.asList(CODD, DIJKSTRA, EVANS, FOWLER);
// Train names
List<String> names = new ArrayList<>(TRAINS.size());
for (Train train : TRAINS) {
names.add(train.getName());
}
}
private static Train codd() {
Module build = new Module(BUILD, "1.3");
Module commons = new Module(COMMONS, "1.7");
Module jpa = new Module(JPA, "1.5");
Module mongoDb = new Module(MONGO_DB, "1.4");
Module neo4j = new Module(NEO4J, "3.0");
Module solr = new Module(SOLR, "1.1");
Module rest = new Module(REST, "2.0");
return new Train("Codd", build, commons, jpa, mongoDb, neo4j, solr, rest);
}
private static Train dijkstra() {
Module build = new Module(BUILD, "1.4");
Module commons = new Module(COMMONS, "1.8");
Module jpa = new Module(JPA, "1.6");
Module mongoDb = new Module(MONGO_DB, "1.5");
Module neo4j = new Module(NEO4J, "3.1");
Module solr = new Module(SOLR, "1.2");
Module couchbase = new Module(COUCHBASE, "1.1");
Module cassandra = new Module(CASSANDRA, "1.0");
Module elasticsearch = new Module(ELASTICSEARCH, "1.0", "M2");
Module gemfire = new Module(GEMFIRE, "1.4");
Module redis = new Module(REDIS, "1.3");
Module rest = new Module(REST, "2.1");
return new Train("Dijkstra", build, commons, jpa, mongoDb, neo4j, solr, couchbase, cassandra, elasticsearch,
gemfire, redis, rest);
}
public static Train getTrainByName(String name) {
for (Train train : TRAINS) {
if (train.getName().equalsIgnoreCase(name)) {
return train;
}
}
return null;
}
public static Project getProjectByName(String name) {
for (Project project : PROJECTS) {
if (project.getName().equalsIgnoreCase(name)) {
return project;
}
}
return null;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
/**
* @author Oliver Gierke
*/
public enum Tracker {
JIRA, GITHUB;
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import static org.springframework.data.release.model.Iteration.*;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.shell.support.util.OsUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Oliver Gierke
*/
@Value
public class Train implements Iterable<Module> {
private final String name;;
private final List<Module> modules;
private final Iterations iterations;
public Train(String name, Module... modules) {
this.name = name;
this.modules = Arrays.asList(modules);
this.iterations = Iterations.DEFAULT;
}
public Train(String name, List<Module> modules) {
this.name = name;
this.modules = Collections.unmodifiableList(modules);
this.iterations = Iterations.DEFAULT;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Module> iterator() {
return modules.iterator();
}
public Module getModule(String name) {
for (Module module : this) {
if (module.getProject().getName().equals(name)) {
return module;
}
}
return null;
}
public Module getModule(Project project) {
for (Module module : this) {
if (module.getProject().equals(project)) {
return module;
}
}
return null;
}
public Train next(String name, Transition transition) {
List<Module> nextModules = new ArrayList<>();
for (Module module : modules) {
nextModules.add(module.next(transition));
}
return new Train(name, nextModules);
}
public ModuleIteration getModuleIteration(Iteration iteration, String moduleName) {
for (Module module : this) {
if (module.hasName(moduleName)) {
return new ModuleIteration(module, iteration, this);
}
}
return null;
}
public Iterable<ModuleIteration> getModuleIterations(Iteration iteration) {
return getModuleIterations(iteration, new Project[0]);
}
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;
}
public Iteration getIteration(String name) {
return iterations.getIterationByName(name);
}
public ArtifactVersion getModuleVersion(Project project, Iteration iteration) {
Module module = getModule(project);
return ArtifactVersion.from(new ModuleIteration(module, iteration, this));
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(name).append(OsUtils.LINE_SEPARATOR).append(OsUtils.LINE_SEPARATOR);
builder.append(StringUtils.collectionToDelimitedString(modules, OsUtils.LINE_SEPARATOR));
return builder.toString();
}
/**
* Value object to represent a set of {@link Iteration}s.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
@ToString
public static class Iterations implements Iterable<Iteration> {
public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4, SR5, SR6);
private final List<Iteration> iterations;
/**
* Creates a new {@link Iterations} from the given {@link Iteration}.
*
* @param iterations
*/
Iterations(Iteration... iterations) {
this.iterations = Arrays.asList(iterations);
}
/**
* Returns the iteration with the given name.
*
* @param name must not be {@literal null} or empty.
* @return
*/
Iteration getIterationByName(String name) {
Assert.hasText(name, "Name must not be null or empty!");
for (Iteration iteration : this) {
if (iteration.getName().equalsIgnoreCase(name)) {
return iteration;
}
}
return null;
}
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));
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Iteration> iterator() {
return iterations.iterator();
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.Value;
import java.util.Iterator;
/**
* @author Oliver Gierke
*/
@Value
public class TrainIteration implements Iterable<ModuleIteration> {
private final Train train;
private final Iteration iteration;
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<ModuleIteration> iterator() {
return train.getModuleIterations(iteration).iterator();
}
public ArtifactVersion getModuleVersion(Project project) {
return train.getModuleVersion(project, iteration);
}
public ModuleIteration getModule(String name) {
return train.getModuleIteration(iteration, name);
}
public ModuleIteration getModule(Project project) {
return train.getModuleIteration(iteration, project.getName());
}
public Iterable<ModuleIteration> getModulesExcept(Project... exclusions) {
return train.getModuleIterations(iteration, exclusions);
}
public ModuleIteration getPreviousIteration(ModuleIteration module) {
Iteration previousIteration = train.getIterations().getPreviousIteration(iteration);
return train.getModuleIteration(previousIteration, module.getProject().getName());
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s %s", train.getName(), iteration.getName());
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
/**
* @author Oliver Gierke
*/
public enum Transition {
MAJOR, MINOR;
}

View File

@@ -0,0 +1,218 @@
package org.springframework.data.release.model;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Value object to represent a Version consisting of major, minor and bugfix part.
*
* @author Oliver Gierke
*/
public class Version implements Comparable<Version> {
private final int major;
private final int minor;
private final int bugfix;
private final int build;
/**
* Creates a new {@link Version} from the given integer values. At least one value has to be given but a maximum of 4.
*
* @param parts must not be {@literal null} or empty.
*/
public Version(int... parts) {
Assert.notNull(parts);
Assert.isTrue(parts.length > 0 && parts.length < 5);
this.major = parts[0];
this.minor = parts.length > 1 ? parts[1] : 0;
this.bugfix = parts.length > 2 ? parts[2] : 0;
this.build = parts.length > 3 ? parts[3] : 0;
Assert.isTrue(major >= 0, "Major version must be greater or equal zero!");
Assert.isTrue(minor >= 0, "Minor version must be greater or equal zero!");
Assert.isTrue(bugfix >= 0, "Bugfix version must be greater or equal zero!");
Assert.isTrue(build >= 0, "Build version must be greater or equal zero!");
}
/**
* Parses the given string representation of a version into a {@link Version} object.
*
* @param version must not be {@literal null} or empty.
* @return
*/
public static Version parse(String version) {
Assert.hasText(version);
String[] parts = version.trim().split("\\.");
int[] intParts = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
intParts[i] = Integer.parseInt(parts[i]);
}
return new Version(intParts);
}
/**
* Returns whether the current {@link Version} is greater (newer) than the given one.
*
* @param version
* @return
*/
public boolean isGreaterThan(Version version) {
return compareTo(version) > 0;
}
/**
* Returns whether the current {@link Version} is greater (newer) or the same as the given one.
*
* @param version
* @return
*/
public boolean isGreaterThanOrEqualTo(Version version) {
return compareTo(version) >= 0;
}
/**
* Returns whether the current {@link Version} is the same as the given one.
*
* @param version
* @return
*/
public boolean is(Version version) {
return equals(version);
}
/**
* Returns whether the current {@link Version} is less (older) than the given one.
*
* @param version
* @return
*/
public boolean isLessThan(Version version) {
return compareTo(version) < 0;
}
/**
* Returns whether the current {@link Version} is less (older) or equal to the current one.
*
* @param version
* @return
*/
public boolean isLessThanOrEqualTo(Version version) {
return compareTo(version) <= 0;
}
public Version nextMajor() {
return new Version(this.major + 1);
}
public Version nextMinor() {
return new Version(this.major, this.minor + 1);
}
public Version nextBugfix() {
return new Version(this.major, this.minor, this.bugfix + 1);
}
public Version withBugfix(int bugfix) {
return new Version(this.major, this.minor, bugfix);
}
public String toMajorMinorBugfix() {
return String.format("%s.%s.%s", major, minor, bugfix);
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Version that) {
if (that == null) {
return 1;
}
if (major != that.major) {
return major - that.major;
}
if (minor != that.minor) {
return minor - that.minor;
}
if (bugfix != that.bugfix) {
return bugfix - that.bugfix;
}
if (build != that.build) {
return build - that.build;
}
return 0;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Version)) {
return false;
}
Version that = (Version) obj;
return this.major == that.major && this.minor == that.minor && this.bugfix == that.bugfix
&& this.build == that.build;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * major;
result += 31 * minor;
result += 31 * bugfix;
result += 31 * build;
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
List<Integer> digits = new ArrayList<Integer>();
digits.add(major);
digits.add(minor);
if (build != 0 || bugfix != 0) {
digits.add(bugfix);
}
if (build != 0) {
digits.add(build);
}
return StringUtils.collectionToDelimitedString(digits, ".");
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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,49 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.utils;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.support.logging.HandlerUtils;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
public class Logger {
private static final String PREFIX_TEMPLATE = "%s > %s";
private final java.util.logging.Logger LOGGER = HandlerUtils.getLogger(getClass());
public void log(ModuleIteration module, String template, Object... args) {
log(module.getProject(), template, args);
}
public void log(Project project, String template, Object... args) {
log(project.getName(), template, args);
}
public void log(TrainIteration iteration, String template, Object... args) {
log(iteration.toString(), template, args);
}
private void log(String context, String template, Object... args) {
LOGGER.info(String.format(PREFIX_TEMPLATE, context, String.format(template, args)));
}
}

View File

@@ -0,0 +1,10 @@
<?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,16 @@
<?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

@@ -0,0 +1,8 @@
_____ _ _____ _ _____ _ _____ _ _ _
/ ____| (_) | __ \ | | | __ \ | | / ____| | | | |
| (___ _ __ _ __ _ _ __ __ _ | | | | __ _| |_ __ _ | |__) |___| | ___ __ _ ___ ___ | (___ | |__ ___| | |
\___ \| '_ \| '__| | '_ \ / _` | | | | |/ _` | __/ _` | | _ // _ \ |/ _ \/ _` / __|/ _ \ \___ \| '_ \ / _ \ | |
____) | |_) | | | | | | | (_| | | |__| | (_| | || (_| | | | \ \ __/ | __/ (_| \__ \ __/ ____) | | | | __/ | |
|_____/| .__/|_| |_|_| |_|\__, | |_____/ \__,_|\__\__,_| |_| \_\___|_|\___|\__,_|___/\___| |_____/|_| |_|\___|_|_|
| | __/ |
|_| |___/

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public abstract class AbstractIntegrationTests {
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.release.maven.Artifact;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ReleaseTrains;
/**
* @author Oliver Gierke
*/
public class ArtifactUnitTests {
@Test
public void testname() {
Artifact artifact = new Artifact(ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.M1, "JPA"));
assertThat(artifact.getArtifactId(), is("spring-data-jpa"));
assertThat(artifact.getVersion(), is(ArtifactVersion.parse("1.6.0.M1")));
assertThat(artifact.getNextDevelopmentVersion(), is(ArtifactVersion.parse("1.6.0.BUILD-SNAPSHOT")));
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author Oliver Gierke
*/
@Configuration
@ComponentScan
public class TestConfig {
}

View File

@@ -0,0 +1,42 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import static org.hamcrest.CoreMatchers.*;
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.git.GitOperations;
import org.springframework.data.release.model.ReleaseTrains;
/**
* @author Oliver Gierke
*/
public class ReleaseCommandsIntegrationTests extends AbstractIntegrationTests {
@Autowired ReleaseCommands releaseCommands;
@Autowired GitOperations git;
@Test
public void predictsReleaseTrainCorrectly() throws Exception {
git.update(ReleaseTrains.DIJKSTRA);
assertThat(releaseCommands.predictTrainAndIteration(), is("Dijkstra"));
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.IterationVersion;
import org.springframework.data.release.model.SimpleIterationVersion;
import org.springframework.data.release.model.Version;
/**
* @author Oliver Gierke
*/
public class BranchUnitTests {
@Test
public void testname() {
IterationVersion iterationVersion = new SimpleIterationVersion(new Version(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);
assertThat(Branch.from(iterationVersion).toString(), is("1.4.x"));
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
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;
/**
* @author Oliver Gierke
*/
public class GitOperationsIntegrationTests extends AbstractIntegrationTests {
@Autowired GitOperations gitOperations;
@Test
@Ignore
public void testname() throws Exception {
gitOperations.update(ReleaseTrains.CODD);
}
@Test
@Ignore
public void showTags() throws Exception {
Tags tags = gitOperations.getTags(COMMONS);
System.out.println(StringUtils.collectionToDelimitedString(tags.asList(), "\n"));
}
@Test
public void foo() throws Exception {
gitOperations.update(ReleaseTrains.EVANS);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.git;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
/**
* @author Oliver Gierke
*/
public class GitProjectUnitTests {
@Test
public void testname() {
Train codd = ReleaseTrains.CODD;
GitProject project = new GitProject(codd.getModule("Commons").getProject(), new GitServer());
assertThat(project.getProjectUri(), is("https://www.github.com/spring-projects/spring-data-commons"));
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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 static org.hamcrest.CoreMatchers.*;
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 {
@Autowired OsCommandOperations operations;
@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));
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.jira;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.ReleaseTrains;
/**
* Unit tests for {@link JiraVersion}.
*
* @author Oliver Gierke
*/
public class JiraVersionUnitTests {
@Test
public void rendersJiraGaVersionCorrectly() {
assertIterationVersion(Iteration.M1, "1.8 M1 (Dijkstra)");
assertIterationVersion(Iteration.RC1, "1.8 RC1 (Dijkstra)");
assertIterationVersion(Iteration.GA, "1.8 GA (Dijkstra)");
assertIterationVersion(Iteration.SR1, "1.8.1 (Dijkstra SR1)");
assertIterationVersion(Iteration.SR2, "1.8.2 (Dijkstra SR2)");
assertIterationVersion(Iteration.SR3, "1.8.3 (Dijkstra SR3)");
assertIterationVersion(Iteration.SR4, "1.8.4 (Dijkstra SR4)");
}
@Test
public void usesCustomModuleIterationStartVersion() {
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.M1, "Elasticsearch");
JiraVersion version = new JiraVersion(module);
assertThat(version.toString(), is("1.0 M2 (Dijkstra)"));
}
@Test
public void doesNotUseCustomIterationOnNonFirstiterations() {
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.RC1, "Elasticsearch");
JiraVersion version = new JiraVersion(module);
assertThat(version.toString(), is("1.0 RC1 (Dijkstra)"));
}
private void assertIterationVersion(Iteration iteration, String expected) {
ModuleIteration module = ReleaseTrains.DIJKSTRA.getModuleIteration(iteration, "Commons");
JiraVersion version = new JiraVersion(module);
assertThat(version.toString(), is(expected));
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.
* 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.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;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.model.ArtifactVersion;
import org.xmlbeam.ProjectionFactory;
import org.xmlbeam.io.XBFileIO;
/**
* @author Oliver Gierke
*/
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"));
// System.out.println(projection.asString(pom));
}
@Test
public void updatesRepositoriesCorrectly() throws Exception {
XBFileIO io = projection.io().file(new ClassPathResource("sample-pom.xml").getFile());
Pom pom = io.read(Pom.class);
pom.setRepositoryId("spring-libs-snapshot", "spring-libs-release");
pom.setRepositoryUrl("spring-libs-release", "https://repo.spring.io/libs-release");
// System.out.println(projection.asString(pom));
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
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
*/
public class ArtifactVersionUnitTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsInvalidVersionSuffix() {
ArtifactVersion.parse("1.4.5.GA");
}
@Test
public void parsesReleaseVersionCorrectly() {
ArtifactVersion version = ArtifactVersion.parse("1.4.5.RELEASE");
assertThat(version.isReleaseVersion(), is(true));
assertThat(version.getNextDevelopmentVersion(), is(ArtifactVersion.parse("1.4.6.BUILD-SNAPSHOT")));
}
@Test
public void createsMilestoneVersionCorrectly() {
ArtifactVersion version = ArtifactVersion.parse("1.4.5.M1");
assertThat(version.isReleaseVersion(), is(false));
assertThat(version.isMilestoneVersion(), is(true));
}
@Test
public void createsReleaseVersionByDefault() {
ArtifactVersion version = new ArtifactVersion(new Version(1, 4, 5));
assertThat(version.isReleaseVersion(), is(true));
assertThat(version.toString(), is("1.4.5.RELEASE"));
}
@Test
public void createsMilestoneVersionFromIteration() {
IterationVersion oneFourMilestoneOne = new SimpleIterationVersion(new Version(1, 4), Iteration.M1);
ArtifactVersion version = ArtifactVersion.from(oneFourMilestoneOne);
assertThat(version.isMilestoneVersion(), is(true));
assertThat(version.toString(), is("1.4.0.M1"));
}
@Test
public void createsReleaseVersionFromIteration() {
IterationVersion oneFourGA = new SimpleIterationVersion(new Version(1, 4), Iteration.GA);
ArtifactVersion version = ArtifactVersion.from(oneFourGA);
assertThat(version.isReleaseVersion(), is(true));
assertThat(version.toString(), is("1.4.0.RELEASE"));
}
@Test
public void createsServiceReleaseVersionFromIteration() {
IterationVersion oneFourServiceReleaseTwo = new SimpleIterationVersion(new Version(1, 4), Iteration.SR2);
ArtifactVersion version = ArtifactVersion.from(oneFourServiceReleaseTwo);
assertThat(version.isReleaseVersion(), is(true));
assertThat(version.toString(), is("1.4.2.RELEASE"));
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
/**
*
* @author Oliver Gierke
*/
public class IterationUnitTests {
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* @author Oliver Gierke
*/
public class ModuleIterationUnitTests {
@Test
public void abbreviatesTrailingZerosForNonServiceReleases() {
TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.M1);
ModuleIteration module = iteration.getModule(Projects.JPA);
assertThat(module.getVersionString(), is("1.6 M1"));
}
@Test
public void doesNotListIterationSuffixForServiceReleases() {
TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.SR1);
ModuleIteration module = iteration.getModule(Projects.JPA);
assertThat(module.getVersionString(), is("1.6.1"));
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import lombok.Value;
@Value
public class SimpleIterationVersion implements IterationVersion {
private final Version version;
private final Iteration iteration;
}

View File

@@ -0,0 +1,745 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
Global parent pom.xml for Spring Data modules to inherit from.
- Shared resources are pulled in from the spring-data-build-resources dependency (images, CSS, XSLTs for documentation generation)
- Renders reference documentation from Asciidoctor file named index.adoc within src/main/asciidoc
- In the "distribute" profile, an assembly is generated:
- one to be uploaded to static.springsource.org (incl. javadoc (browsable), reference docs as described before)
Preconfigures the following:
- Logging dependencies: SLF4j + Commons Logging bridge and Logback as test dependency
- Test dependencies: JUnit / Hamcrest / Mockito
- Dependency versions for commonly used dependencies
-->
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-parent</artifactId>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-build</artifactId>
<version>1.6.2.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Spring Data Build - General parent module</name>
<description>Global parent pom.xml to be used by Spring Data modules</description>
<url>http://www.spring.io/spring-data</url>
<inceptionYear>2011-2015</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<developers>
<developer>
<id>ogierke</id>
<name>Oliver Gierke</name>
<email>ogierke at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>http://www.spring.io</organizationUrl>
<roles>
<role>Project lead</role>
</roles>
<timezone>+1</timezone>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<comments>
Copyright 2008-2013 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.
</comments>
</license>
</licenses>
<properties>
<bundlor.enabled>true</bundlor.enabled>
<bundlor.failOnWarnings>true</bundlor.failOnWarnings>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.root>${basedir}</project.root>
<source.level>1.6</source.level>
<dist.id>${project.artifactId}</dist.id>
<apt>1.1.3</apt>
<aspectj>1.8.5</aspectj>
<cdi>1.0</cdi>
<hamcrest>1.3</hamcrest>
<jackson>2.5.1</jackson>
<jodatime>2.7</jodatime>
<junit>4.12</junit>
<logback>1.1.2</logback>
<mockito>1.10.19</mockito>
<querydsl>3.6.3</querydsl>
<slf4j>1.7.10</slf4j>
<spring>4.0.9.RELEASE</spring>
<threetenbp>1.2</threetenbp>
<webbeans>1.2.7</webbeans>
<releasetrain>Fowler-BUILD-SNAPSHOT</releasetrain>
</properties>
<profiles>
<profile>
<!-- Profile to be run on the CI server, JARs JavaDocs -->
<id>ci</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>package-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<!--
Profile to be activated when building the distribution atrifacts.
Generates reference documentation, aggregates JavaDoc etc. Has to be combined with
profiles "release" or "milestone" to deploy artifacts into the appropriate places.
-->
<id>distribute</id>
<properties>
<shared.resources>${project.build.directory}/shared-resources</shared.resources>
<maven.install.skip>true</maven.install.skip>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-build-resources</artifactId>
<version>1.6.2.BUILD-SNAPSHOT</version>
<scope>provided</scope>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<!--
Unpacks the content of spring-data-build-resources into the shared resources folder.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-shared-resources</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
</execution>
</executions>
<configuration>
<includeGroupIds>${project.groupId}</includeGroupIds>
<includeArtifacIds>spring-data-build-resources</includeArtifacIds>
<includeTypes>zip</includeTypes>
<excludeTransitive>true</excludeTransitive>
<outputDirectory>${shared.resources}</outputDirectory>
</configuration>
</plugin>
<!--
Configures JavaDoc generation.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>aggregate-javadoc</id>
<goals>
<goal>aggregate</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<!--
Copies all namespaces schemas to target/schemas flatten the directory structure.
Depended on by the site.xml assembly descriptor.
-->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>copy-documentation-resources</id>
<phase>generate-resources</phase>
<configuration>
<target>
<copy todir="${project.root}/target/site/reference/html">
<fileset dir="${shared.resources}/asciidoc" erroronmissingdir="false">
<include name="**/*.css"/>
</fileset>
<flattenmapper/>
</copy>
<copy todir="${project.root}/target/site/reference/html/images">
<fileset dir="${basedir}/src/main/asciidoc" erroronmissingdir="false">
<include name="**/*.png"/>
<include name="**/*.gif"/>
<include name="**/*.jpg"/>
</fileset>
<flattenmapper/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>collect-schema-files</id>
<phase>process-resources</phase>
<configuration>
<target>
<copy todir="${project.build.directory}/schemas">
<fileset dir="${basedir}" erroronmissingdir="false">
<include name="**/src/main/resources/**/config/spring-*.xsd"/>
</fileset>
<flattenmapper/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>rename-reference-docs</id>
<phase>process-resources</phase>
<configuration>
<target>
<copy failonerror="false" file="${project.build.directory}/generated-docs/index.pdf" tofile="${project.root}/target/site/reference/pdf/${dist.id}-reference.pdf"/>
<copy failonerror="false" file="${project.build.directory}/generated-docs/index.epub" tofile="${project.root}/target/site/reference/epub/${dist.id}-reference.epub"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--
Asciidoctor
-->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.2</version>
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.6</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-epub3</artifactId>
<version>1.5.0-alpha.4</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>html</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html5</backend>
<outputDirectory>${project.root}/target/site/reference/html</outputDirectory>
<sectids>false</sectids>
<sourceHighlighter>prettify</sourceHighlighter>
<attributes>
<linkcss>true</linkcss>
<icons>font</icons>
<sectanchors>true</sectanchors>
<stylesheet>spring.css</stylesheet>
</attributes>
</configuration>
</execution>
<execution>
<id>epub</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>epub3</backend>
<sourceHighlighter>coderay</sourceHighlighter>
</configuration>
</execution>
<execution>
<id>pdf</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>pdf</backend>
<sourceHighlighter>coderay</sourceHighlighter>
</configuration>
</execution>
</executions>
<configuration>
<sourceDirectory>${project.root}/src/main/asciidoc</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<doctype>book</doctype>
<attributes>
<version>${project.version}</version>
<projectName>${project.name}</projectName>
<projectVersion>${project.version}</projectVersion>
<aspectjVersion>${aspectj}</aspectjVersion>
<querydslVersion>${querydsl}</querydslVersion>
<springVersion>${spring}</springVersion>
<releasetrainVersion>${releasetrain}</releasetrainVersion>
<allow-uri-read>true</allow-uri-read>
<toclevels>3</toclevels>
<numbered>true</numbered>
</attributes>
</configuration>
</plugin>
<!--
Creates two zip files for download as well as API and reference documentation distribution.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>static</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>${shared.resources}/assemblies/static-resources.xml</descriptor>
</descriptors>
<finalName>static-resources</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
<version>1.0-beta-5</version>
<configuration>
<fromDir>${project.build.directory}</fromDir>
</configuration>
<executions>
<!-- Upload namespace XSDs -->
<execution>
<id>upload-schema</id>
<phase>deploy</phase>
<goals>
<goal>upload</goal>
</goals>
<configuration>
<fromDir>${project.root}/target/schemas</fromDir>
<includes>*.xsd,.autoschemaln</includes>
<serverId>static-dot-s2</serverId>
<url>scp://static.springsource.org</url>
<toDir>/var/www/domains/springsource.org/www/htdocs/autorepo/schema/${dist.id}/${project.version}</toDir>
<optimize>true</optimize>
</configuration>
</execution>
<!-- Distribute static resources -->
<execution>
<id>upload-static-resources</id>
<phase>deploy</phase>
<goals>
<goal>upload</goal>
</goals>
<configuration>
<fromDir>${project.build.directory}/static-resources</fromDir>
<includes>**</includes>
<serverId>static-dot-s2</serverId>
<url>scp://static.springsource.org</url>
<toDir>/var/www/domains/springsource.org/www/htdocs/autorepo/docs/${dist.id}/${project.version}</toDir>
<optimize>true</optimize>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>spring41</id>
<properties>
<spring>4.1.6.RELEASE</spring>
</properties>
</profile>
<profile>
<id>spring41-next</id>
<properties>
<spring>4.1.7.BUILD-SNAPSHOT</spring>
</properties>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</profile>
<profile>
<id>spring42-next</id>
<properties>
<spring>4.2.0.BUILD-SNAPSHOT</spring>
</properties>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</profile>
<profile>
<id>querydsl-next</id>
<properties>
<querydsl>3.6.2.BUILD-SNAPSHOT</querydsl>
</properties>
<repositories>
<repository>
<id>oss-nexus-snapshots</id>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
</profile>
</profiles>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Test -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>2.5</version>
</extension>
</extensions>
<!--
Default versioned declarations of managed plugins
to be overridden when the distribute profile is active.
If this section was missing, Maven would complain about
missing version numbers for executions without the
profile active.
-->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
<version>1.0-beta-5</version>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>0.1.4</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${source.level}</source>
<target>${source.level}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<useFile>false</useFile>
<includes>
<include>**/*Tests.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<useDefaultManifestFile>true</useDefaultManifestFile>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
<version>1.0.0.RELEASE</version>
<configuration>
<enabled>${bundlor.enabled}</enabled>
<failOnWarnings>${bundlor.failOnWarnings}</failOnWarnings>
</configuration>
<executions>
<execution>
<id>bundlor</id>
<goals>
<goal>bundlor</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<breakiterator>true</breakiterator>
<header>${project.name}</header>
<source>${source.level}</source>
<quiet>true</quiet>
<javadocDirectory>${shared.resources}/javadoc</javadocDirectory>
<overview>${shared.resources}/javadoc/overview.html</overview>
<stylesheetfile>${shared.resources}/javadoc/spring-javadoc.css</stylesheetfile>
<!-- copies doc-files subdirectory which contains image resources -->
<docfilessubdirs>true</docfilessubdirs>
<additionalparam>-Xdoclint:none</additionalparam>
<links>
<link>http://docs.spring.io/spring/docs/3.2.x/javadoc-api/</link>
<link>http://docs.spring.io/spring-data/data-commons/docs/current/api/</link>
<link>http://docs.oracle.com/javase/6/docs/api</link>
</links>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

@@ -0,0 +1,112 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-release-cli</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.0.0.RC4</version>
</parent>
<properties>
<jar.mainclass>org.springframework.shell.Bootstrap</jar.mainclass>
</properties>
<profiles>
<profile>
<id>profile</id>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.xmlbeam</groupId>
<artifactId>xmlprojector</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.12.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<!-- Shell packaging -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<programs>
<program>
<mainClass>org.springframework.shell.Bootstrap</mainClass>
<id>spring-data-release-shell</id>
</program>
</programs>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</project>