Further new features.

Added support for creating changelogs from GitHub (for Spring Data Build project). A lot of internal refactorings, strengthening the domain model. Moved project declarations into Projects class. Made types package protected where possible.

Added dedicated TrainIteration type as well as a Shell Converter implementation to allow this type to be handed into the command implementations.

Unified logging into separate component to make sure we have consistent output.
This commit is contained in:
Oliver Gierke
2014-04-16 20:18:37 +02:00
parent 45c86d2b42
commit 5e09fda269
43 changed files with 1069 additions and 292 deletions

View File

@@ -61,6 +61,12 @@
<version>1.12.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -15,14 +15,16 @@
*/
package org.springframework.data.release.announcement;
import static org.springframework.data.release.model.Projects.*;
import org.springframework.data.release.cli.StaticResources;
import org.springframework.data.release.maven.Artifact;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.util.Assert;
/**
@@ -38,25 +40,19 @@ public class AnnouncementOperations {
* @param iteration must not be {@literal null}.
* @return
*/
public String getProjectBulletpoints(Train train, Iteration iteration) {
public String getProjectBulletpoints(TrainIteration iteration) {
Assert.notNull(train, "Train must not be null!");
Assert.notNull(iteration, "Iteration must not be null!");
StringBuilder builder = new StringBuilder();
for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) {
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
Project project = module.getProject();
builder.append("* ");
builder.append(project.getFullName()).append(" ");
builder.append(ArtifactVersion.from(module).toShortString());
if (!iteration.isServiceIteration()) {
builder.append(" ").append(module.getIteration().getName());
}
builder.append(module.getVersionString());
builder.append(" - ");
Artifact artifact = new Artifact(module);
@@ -83,6 +79,6 @@ public class AnnouncementOperations {
public static void main(String[] args) {
AnnouncementOperations operations = new AnnouncementOperations();
System.out.println(operations.getProjectBulletpoints(ReleaseTrains.CODD, Iteration.SR2));
System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.CODD, Iteration.SR2)));
}
}

View File

@@ -18,10 +18,12 @@ package org.springframework.data.release.cli;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.release.jira.Credentials;
import org.springframework.data.release.jira.IssueTracker;
import org.springframework.data.release.jira.JiraConnector;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
@@ -32,58 +34,62 @@ import org.springframework.util.StringUtils;
* @author Oliver Gierke
*/
@Component
public class JiraCommands implements CommandMarker {
public class IssueTracerCommands implements CommandMarker {
private final JiraConnector connector;
private final PluginRegistry<IssueTracker, Project> tracker;
private final JiraConnector jira;
private final Credentials credentials;
/**
* @param connector
* @param tracker
* @param environment
*/
@Autowired
public JiraCommands(JiraConnector connector, Environment environment) {
public IssueTracerCommands(PluginRegistry<IssueTracker, Project> tracker, JiraConnector jira, Environment environment) {
String username = environment.getProperty("jira.username", (String) null);
String password = environment.getProperty("jira.password", (String) null);
this.connector = connector;
this.tracker = tracker;
this.jira = jira;
this.credentials = StringUtils.hasText(username) ? new Credentials(username, password) : null;
}
@CliCommand("jira evict")
public void jiraEvict() {
connector.reset();
jira.reset();
}
@CliCommand(value = "jira tickets")
public String jira(
@CliOption(key = { "", "train" }, mandatory = true, help = "The name of the release train.") String trainName, //
@CliOption(key = "iteration", mandatory = true, help = "An iteration key (one of M1, RC1, GA).") String iterationName, //
@CliOption(key = "", mandatory = true) TrainIteration iteration, //
@CliOption(key = "for-current-user", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") boolean forCurrentUser) {
if (forCurrentUser && credentials == null) {
return "No authentication specified! Use 'jira authenticate' first!";
}
Train train = ReleaseTrains.getTrainByName(trainName);
Iteration iteration = train.getIterations().getIterationByName(iterationName);
return connector.getTicketsFor(train, iteration, forCurrentUser ? credentials : null).toString();
return jira.getTicketsFor(iteration, forCurrentUser ? credentials : null).toString();
}
@CliCommand("changelog")
public String changelog(@CliOption(key = { "", "train" }, mandatory = true) String trainName, //
@CliOption(key = { "iteration" }, mandatory = true) String iterationName, //
@CliCommand("tracker changelog")
public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration, //
@CliOption(key = "module") String moduleName) {
Train train = ReleaseTrains.getTrainByName(trainName);
Iteration iteration = train.getIteration(iterationName);
if (StringUtils.hasText(moduleName)) {
return connector.getChangelogFor(train.getModuleIteration(iteration, moduleName)).toString();
ModuleIteration module = iteration.getModule(moduleName);
return tracker.getPluginFor(module.getProject()).getChangelogFor(module).toString();
}
return "";
StringBuilder builder = new StringBuilder();
for (ModuleIteration module : iteration) {
IssueTracker issues = tracker.getPluginFor(module.getProject());
builder.append(issues.getChangelogFor(module)).append("\n");
}
return builder.toString();
}
}

View File

@@ -33,16 +33,16 @@ import org.springframework.util.StringUtils;
public class ModelCommands implements CommandMarker {
@CliCommand("trains")
public String train(@CliOption(key = { "", "train" }) String trainName) {
public String train(@CliOption(key = { "", "train" }) Train train) {
if (StringUtils.hasText(trainName)) {
return ReleaseTrains.getTrainByName(trainName).toString();
if (train != null) {
return train.toString();
}
List<String> names = new ArrayList<>();
for (Train train : ReleaseTrains.TRAINS) {
names.add(train.getName());
for (Train releaseTrain : ReleaseTrains.TRAINS) {
names.add(releaseTrain.getName());
}
return StringUtils.collectionToDelimitedString(names, ", ");

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.release.cli;
import static org.springframework.data.release.model.Projects.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,11 +25,11 @@ import org.springframework.data.release.maven.MavenOperations;
import org.springframework.data.release.maven.Pom;
import org.springframework.data.release.misc.ReleaseOperations;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.Module;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
@@ -48,17 +49,16 @@ public class ReleaseCommands implements CommandMarker {
@CliCommand("release predict")
public String predictTrainAndIteration() throws Exception {
Project commons = ReleaseTrains.COMMONS;
Pom pom = maven.getMavenProject(commons);
Pom pom = maven.getMavenProject(COMMONS);
Tags tags = git.getTags(commons);
Tags tags = git.getTags(COMMONS);
ArtifactVersion version = tags.getLatest().toArtifactVersion();
System.out.println(version);
for (Train train : ReleaseTrains.TRAINS) {
Module module = train.getModule(commons);
Module module = train.getModule(COMMONS);
if (!pom.getVersion().toString().startsWith(module.getVersion().toMajorMinorBugfix())) {
continue;
@@ -78,14 +78,10 @@ public class ReleaseCommands implements CommandMarker {
* @throws Exception
*/
@CliCommand("release distribute")
public void distribute(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption(
key = "iteration", mandatory = true) String iterationName) throws Exception {
public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
Train train = ReleaseTrains.getTrainByName(trainName);
Iteration iteration = train.getIteration(iterationName);
git.checkout(train, iteration);
maven.triggerDistributionBuild(train, iteration);
git.checkout(iteration);
maven.triggerDistributionBuild(iteration);
}
/**
@@ -96,17 +92,22 @@ public class ReleaseCommands implements CommandMarker {
* @throws Exception
*/
@CliCommand(value = "release prepare", help = "Prepares the release of the iteration of the given train.")
public void prepare(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption(
key = "iteration", mandatory = true) String iterationName) throws Exception {
public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
Train train = ReleaseTrains.getTrainByName(trainName);
Iteration iteration = train.getIteration(iterationName);
git.prepare(iteration);
misc.prepareChangelogs(iteration);
maven.updatePom(iteration, Phase.PREPARE);
}
git.prepare(train, iteration);
misc.prepareChangelogs(train, iteration);
@CliCommand(value = "release conclude")
public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
for (Module module : train) {
maven.prepareProject(train, iteration, module.getProject());
}
// - pull updates
// - tag release
// - post release pom updates
maven.updatePom(iteration, Phase.CLEANUP);
// - push
}
}

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,80 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.cli;
import java.util.List;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.core.Completion;
import org.springframework.shell.core.Converter;
import org.springframework.shell.core.MethodTarget;
import org.springframework.stereotype.Component;
/**
* @author Oliver Gierke
*/
@Component
public class TrainIterationConverter implements Converter<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) {
// if (!StringUtils.hasText(existingData) &&
// !train.getName().toLowerCase().startsWith(existingData.toLowerCase())) {
// continue;
// }
for (Iteration iteration : train.getIterations()) {
completions.add(new Completion(new TrainIteration(train, iteration).toString()));
}
}
return false;
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.util.Assert;
*/
@RequiredArgsConstructor
@EqualsAndHashCode
public class Branch {
class Branch {
private static final Branch MASTER = new Branch("master");

View File

@@ -18,10 +18,9 @@ package org.springframework.data.release.git;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
@@ -33,24 +32,18 @@ import org.springframework.util.StringUtils;
*/
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GiCommands implements CommandMarker {
class GiCommands implements CommandMarker {
private final GitOperations git;
@CliCommand("git checkout")
public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption(
key = "iteration", mandatory = true) String iterationName) throws Exception {
Train train = ReleaseTrains.getTrainByName(trainName);
Iteration iteration = train.getIteration(iterationName);
git.checkout(train, iteration);
public void checkout(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.checkout(iteration);
}
@CliCommand("git update")
public void checkout(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception,
InterruptedException {
git.update(ReleaseTrains.getTrainByName(trainName));
}
@@ -69,10 +62,7 @@ public class GiCommands implements CommandMarker {
}
@CliCommand("git prepare")
public void prepare(@CliOption(key = { "", "train" }, mandatory = true) String trainName, @CliOption(
key = "iteration", mandatory = true) String iterationName) throws Exception {
Train train = ReleaseTrains.getTrainByName(trainName);
git.prepare(train, train.getIteration(iterationName));
public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception {
git.prepare(iteration);
}
}

View File

@@ -20,7 +20,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import lombok.RequiredArgsConstructor;
@@ -34,7 +33,8 @@ import org.springframework.data.release.model.Module;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Train;
import org.springframework.shell.support.logging.HandlerUtils;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -48,11 +48,10 @@ import org.springframework.util.StringUtils;
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GitOperations {
private static final Logger LOGGER = HandlerUtils.getLogger(GitOperations.class);
private final GitServer server = new GitServer();
private final OsCommandOperations osCommandOperations;
private final Workspace workspace;
private final Logger logger;
public GitProject getGitProject(Project project) {
return new GitProject(project, server);
@@ -80,11 +79,11 @@ public class GitOperations {
* @param iteration
* @throws Exception
*/
public void checkout(Train train, Iteration iteration) throws Exception {
public void checkout(TrainIteration iteration) throws Exception {
update(train);
update(iteration.getTrain());
for (ModuleIteration module : train.getModuleIterations(iteration)) {
for (ModuleIteration module : iteration) {
Project project = module.getProject();
ArtifactVersion artifactVersion = ArtifactVersion.from(module);
@@ -99,13 +98,12 @@ public class GitOperations {
osCommandOperations.executeCommand(String.format("git checkout %s", tag), project).get();
}
LOGGER.info(String.format("Successfully checked out iteration %s for release train %s.", iteration.getName(),
train.getName()));
logger.log(iteration, "Successfully checked out projects.");
}
public void prepare(Train train, Iteration iteration) throws Exception {
public void prepare(TrainIteration iteration) throws Exception {
for (ModuleIteration module : train.getModuleIterations(iteration)) {
for (ModuleIteration module : iteration) {
Branch branch = Branch.from(module);
@@ -139,15 +137,14 @@ public class GitOperations {
if (workspace.hasProjectDirectory(project)) {
LOGGER.info(String.format("Found existing repository %s. Obtaining latest changes…", repositoryName));
logger.log(project, "Found existing repository %s. Obtaining latest changes…", repositoryName);
return osCommandOperations.executeCommand("git checkout master && git fetch --tags && git pull origin master",
project);
} else {
LOGGER.info(String.format("No repository found for project %s. Cloning repository from %s…", repositoryName,
gitProject.getProjectUri()));
logger.log(project, "No repository found! Cloning from %s…", gitProject.getProjectUri());
File projectDirectory = workspace.getProjectDirectory(project);
String command = String.format("git clone %s %s", gitProject.getProjectUri(), projectDirectory.getName());

View File

@@ -21,7 +21,6 @@ import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
@@ -33,11 +32,10 @@ import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.output.WriterOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.git.GitOperations;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.utils.Logger;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.shell.support.logging.HandlerUtils;
import org.springframework.stereotype.Component;
/**
@@ -51,12 +49,10 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class CommonsExecOsCommandOperations implements OsCommandOperations {
private static final Logger LOGGER = HandlerUtils.getLogger(GitOperations.class);
private static final String PREFIX_TEMPLATE = "%s > %s";
private static final Map<String, String> ENVIRONMENT = new HashMap<>();
private final Workspace workspace;
private final Logger logger;
/*
* (non-Javadoc)
@@ -76,7 +72,7 @@ class CommonsExecOsCommandOperations implements OsCommandOperations {
@Override
public Future<CommandResult> executeCommand(String command, Project project) throws IOException {
LOGGER.info(String.format(PREFIX_TEMPLATE, project.getName(), command));
logger.log(project, command);
return executeCommand(command, workspace.getProjectDirectory(project), true);
}
@@ -88,7 +84,7 @@ class CommonsExecOsCommandOperations implements OsCommandOperations {
@Override
public Future<CommandResult> executeWithOutput(String command, Project project) throws IOException {
LOGGER.info(String.format(PREFIX_TEMPLATE, project.getName(), command));
logger.log(project, command);
return executeCommand(command, workspace.getProjectDirectory(project), false);
}

View File

@@ -61,7 +61,7 @@ public class Changelog {
String summary = ticket.getSummary();
builder.append("* ").append(ticket.getId()).append(" - ").append(summary);
builder.append("* ").append(ticket.getId()).append(" - ").append(summary.trim());
if (!summary.endsWith(".")) {
builder.append(".");

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 GitHubIssue {
private String number;
private String title;
}

View File

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

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

View File

@@ -15,35 +15,40 @@
*/
package org.springframework.data.release.jira;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Tracker;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.UriTemplate;
/**
* @author Oliver Gierke
*/
@Component
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
class RestJiraConnector implements JiraConnector {
protected final Logger LOGGER = Logger.getLogger(getClass().getName());
class Jira implements JiraConnector {
private static final String JIRA_HOST = "https://jira.spring.io";
private static final String BASE_URI = "/rest/api/2";
@@ -51,6 +56,7 @@ class RestJiraConnector implements JiraConnector {
+ "/search?jql={jql}&fields={fields}&startAt={startAt}";
private final RestOperations operations;
private final Logger logger;
/*
* (non-Javadoc)
@@ -62,15 +68,33 @@ class RestJiraConnector implements JiraConnector {
}
@Cacheable("release-tickets")
public Ticket getReleaseTicketFor(ModuleIteration iteration) {
JqlQuery query = JqlQuery.from(iteration).and("summary ~ \"Release\"");
Map<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(Train train, Iteration iteration, Credentials credentials) {
public Tickets getTicketsFor(TrainIteration iteration, Credentials credentials) {
JqlQuery query = JqlQuery.from(train, iteration);
JqlQuery query = JqlQuery.from(iteration);
HttpHeaders headers = new HttpHeaders();
int startAt = 0;
@@ -83,10 +107,9 @@ class RestJiraConnector implements JiraConnector {
headers.set("Authorization", String.format("Basic %s", credentials.asBase64()));
LOGGER.info(String.format("Retrieving tickets for %s %s (for user %s).", train.getName(), iteration.getName(),
credentials.getUsername()));
logger.log(iteration, "Retrieving tickets (for user %s)", credentials.getUsername());
} else {
LOGGER.info(String.format("Retrieving tickets for %s %s.", train.getName(), iteration.getName()));
logger.log(iteration, "Retrieving tickets");
}
query = query.orderBy("updatedDate DESC");
@@ -101,10 +124,10 @@ class RestJiraConnector implements JiraConnector {
issues = operations.exchange(SEARCH_TEMPLATE, HttpMethod.GET, new HttpEntity<>(headers), JiraIssues.class,
parameters).getBody();
LOGGER.info(String.format("Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal()));
logger.log(iteration, "Got tickets %s to %s of %s.", startAt, issues.getNextStartAt(), issues.getTotal());
for (JiraIssue issue : issues) {
if (!issue.wasBackportedFrom(train)) {
if (!issue.wasBackportedFrom(iteration.getTrain())) {
tickets.add(new Ticket(issue.getKey(), issue.getFields().getSummary()));
}
}
@@ -121,7 +144,7 @@ class RestJiraConnector implements JiraConnector {
* @see org.springframework.data.release.jira.JiraConnector#verifyBeforeRelease(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration)
*/
@Override
public void verifyBeforeRelease(Train train, Iteration iteration) {
public void verifyBeforeRelease(TrainIteration iteration) {
// for each module
@@ -134,7 +157,7 @@ class RestJiraConnector implements JiraConnector {
* @see org.springframework.data.release.jira.JiraConnector#closeIteration(org.springframework.data.release.model.Train, org.springframework.data.release.model.Iteration, org.springframework.data.release.jira.Credentials)
*/
@Override
public void closeIteration(Train train, Iteration iteration, Credentials credentials) {
public void closeIteration(TrainIteration iteration, Credentials credentials) {
// for each module
@@ -159,13 +182,44 @@ class RestJiraConnector implements JiraConnector {
parameters.put("fields", "summary,fixVersions");
parameters.put("startAt", 0);
JiraIssues issues = operations.getForObject(SEARCH_TEMPLATE, JiraIssues.class, parameters);
URI searchUri = new UriTemplate(SEARCH_TEMPLATE).expand(parameters);
logger.log(module, "Looking up JIRA issues from %s…", searchUri);
JiraIssues issues = operations.getForObject(searchUri, JiraIssues.class);
List<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

@@ -26,6 +26,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.plugin.core.config.EnablePluginRegistries;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -37,6 +38,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
@EnableCaching
@PropertySource(value = "file:jira.properties", ignoreResourceNotFound = true)
@EnablePluginRegistries({ IssueTracker.class })
class JiraConfiguration {
@Bean

View File

@@ -18,14 +18,17 @@ package org.springframework.data.release.jira;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
/**
* @author Oliver Gierke
*/
public interface JiraConnector {
public interface JiraConnector extends IssueTracker {
void reset();
Ticket getReleaseTicketFor(ModuleIteration iteration);
/**
* Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}.
*
@@ -33,11 +36,9 @@ public interface JiraConnector {
* @param iteration must not be {@literal null}.
* @return
*/
Tickets getTicketsFor(Train train, Iteration iteration, Credentials credentials);
Tickets getTicketsFor(TrainIteration iteration, Credentials credentials);
void verifyBeforeRelease(Train train, Iteration iteration);
void verifyBeforeRelease(TrainIteration iteration);
void closeIteration(Train train, Iteration iteration, Credentials credentials);
Changelog getChangelogFor(ModuleIteration iteration);
void closeIteration(TrainIteration iteration, Credentials credentials);
}

View File

@@ -15,15 +15,15 @@
*/
package org.springframework.data.release.jira;
import static org.springframework.data.release.model.Projects.*;
import java.util.ArrayList;
import java.util.List;
import lombok.Value;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.util.StringUtils;
/**
@@ -51,15 +51,11 @@ class JqlQuery {
return new JqlQuery(String.format(PROJECT_VERSION_TEMPLATE, iteration.getProjectKey(), version));
}
public static JqlQuery from(Train train, Iteration iteration) {
public static JqlQuery from(TrainIteration iteration) {
List<String> parts = new ArrayList<>();
for (ModuleIteration module : train.getModuleIterations(iteration)) {
if (ReleaseTrains.BUILD.equals(module.getProject())) {
continue;
}
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
JiraVersion version = new JiraVersion(module);
parts.add(String.format(PROJECT_VERSION_TEMPLATE, module.getProjectKey(), version));

View File

@@ -18,6 +18,7 @@ package org.springframework.data.release.jira;
import java.util.Iterator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.springframework.util.StringUtils;
@@ -28,11 +29,16 @@ import org.springframework.util.StringUtils;
* @author Oliver Gierke
*/
@Value
@RequiredArgsConstructor
public class Tickets implements Iterable<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()

View File

@@ -15,11 +15,12 @@
*/
package org.springframework.data.release.maven;
import static org.springframework.data.release.model.Projects.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.util.Assert;
/**
@@ -27,6 +28,7 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
public class Artifact {
private static final GroupId GROUP_ID = new GroupId("org.springframework.data");
@@ -57,7 +59,11 @@ public class Artifact {
public String getArtifactId() {
String artifactId = String.format("spring-data-%s", module.getProject().getName().toLowerCase());
return ReleaseTrains.REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId;
return REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId;
}
public ArtifactVersion getNextDevelopmentVersion() {
return version.getNextDevelopmentVersion();
}
/**

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.release.maven;
import static org.springframework.data.release.model.Phase.*;
import static org.springframework.data.release.model.Projects.*;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import lombok.RequiredArgsConstructor;
@@ -26,12 +28,12 @@ import org.springframework.data.release.io.CommandResult;
import org.springframework.data.release.io.OsCommandOperations;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Train;
import org.springframework.shell.support.logging.HandlerUtils;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.Logger;
import org.springframework.stereotype.Component;
import org.xmlbeam.ProjectionFactory;
import org.xmlbeam.io.XBFileIO;
@@ -44,12 +46,12 @@ import org.xmlbeam.io.XBFileIO;
public class MavenOperations {
private static final String COMMONS_VERSION_PROPERTY = "springdata.commons";
private static final Logger LOGGER = HandlerUtils.getLogger(MavenOperations.class);
private static final String POM_XML = "pom.xml";
private final Workspace workspace;
private final ProjectionFactory projectionFactory;
private final OsCommandOperations os;
private final Logger logger;
public Pom getMavenProject(Project project) throws IOException {
@@ -57,38 +59,36 @@ public class MavenOperations {
return projectionFactory.io().file(file).read(Pom.class);
}
public void prepareProject(Train train, Iteration iteration, final Project project) throws Exception {
public void updatePom(TrainIteration iteration, final Phase phase) throws Exception {
updateBomPom(train, iteration);
updateBomPom(iteration, phase);
if (ReleaseTrains.BUILD.equals(project)) {
return;
}
final Repository repository = new Repository(iteration.getIteration());
final ArtifactVersion commonsVersion = iteration.getModuleVersion(COMMONS);
final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD);
final ArtifactVersion commonsVersion = train.getModuleVersion(ReleaseTrains.COMMONS, iteration);
final ArtifactVersion buildVersion = train.getModuleVersion(ReleaseTrains.BUILD, iteration);
final Repository repository = new Repository(iteration);
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
File file = workspace.getFile(POM_XML, project);
final Project project = module.getProject();
File pomFile = workspace.getFile(POM_XML, project);
execute(file, new PomCallback() {
execute(pomFile, new PomCallback() {
@Override
public Pom doWith(Pom pom) {
@Override
public Pom doWith(Pom pom) {
if (!project.equals(ReleaseTrains.COMMONS)) {
if (!project.equals(COMMONS)) {
pom.setProperty(COMMONS_VERSION_PROPERTY,
CLEANUP.equals(phase) ? commonsVersion.getNextDevelopmentVersion() : commonsVersion);
}
System.out.println(pom.getProperty(COMMONS_VERSION_PROPERTY));
pom.setProperty(COMMONS_VERSION_PROPERTY, commonsVersion);
pom.setParentVersion(CLEANUP.equals(phase) ? buildVersion.getNextDevelopmentVersion() : buildVersion);
updateRepository(pom, repository, phase);
return pom;
}
pom.setParentVersion(buildVersion);
pom.setRepositoryId("spring-libs-snapshot", "spring-libs-release");
pom.setRepositoryUrl(repository.getId(), repository.getUrl());
return pom;
}
});
});
}
}
/**
@@ -99,23 +99,22 @@ public class MavenOperations {
* @throws IOException
* @throws InterruptedException
*/
public void triggerDistributionBuild(Train train, Iteration iteration) throws Exception {
public void triggerDistributionBuild(TrainIteration iteration) throws Exception {
for (ModuleIteration moduleIteration : train.getModuleIterations(iteration)) {
for (ModuleIteration moduleIteration : iteration) {
Project project = moduleIteration.getProject();
if (ReleaseTrains.BUILD.equals(project)) {
if (BUILD.equals(project)) {
continue;
}
if (!isMavenProject(project)) {
LOGGER.info(String.format("Skipping project %s as no pom.xml could be found in the working directory!",
project.getFullName()));
logger.log(project, "Skipping project as no pom.xml could be found in the working directory!");
continue;
}
LOGGER.info(String.format("Triggering distribution build for %s…", project.getFullName()));
logger.log(project, "Triggering distribution build");
ArtifactVersion version = ArtifactVersion.from(moduleIteration);
@@ -133,7 +132,7 @@ public class MavenOperations {
throw result.getException();
}
LOGGER.info(String.format("Successfully finished distribution build for %s!", project));
logger.log(project, "Successfully finished distribution build!");
}
}
@@ -141,19 +140,22 @@ public class MavenOperations {
return workspace.getFile(POM_XML, project).exists();
}
private void updateBomPom(final Train train, final Iteration iteration) throws Exception {
private void updateBomPom(final TrainIteration iteration, final Phase phase) throws Exception {
File bomPomFile = workspace.getFile("bom/pom.xml", ReleaseTrains.BUILD);
File bomPomFile = workspace.getFile("bom/pom.xml", BUILD);
execute(bomPomFile, new PomCallback() {
@Override
public Pom doWith(Pom pom) {
for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) {
for (ModuleIteration module : iteration.getModulesExcept(BUILD)) {
Artifact artifact = new Artifact(module);
pom.setDependencyVersion(artifact.getArtifactId(), artifact.getVersion());
ArtifactVersion version = artifact.getVersion();
version = PREPARE.equals(phase) ? version : version.getNextDevelopmentVersion();
pom.setDependencyVersion(artifact.getArtifactId(), version);
}
return pom;
@@ -161,6 +163,17 @@ public class MavenOperations {
});
}
private void updateRepository(Pom pom, Repository repository, Phase phase) {
if (PREPARE.equals(phase)) {
pom.setRepositoryId(repository.getSnapshotId(), repository.getId());
pom.setRepositoryUrl(repository.getId(), repository.getUrl());
} else {
pom.setRepositoryId(repository.getId(), repository.getSnapshotId());
pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl());
}
}
private void execute(File file, PomCallback callback) throws Exception {
XBFileIO io = projectionFactory.io().file(file);

View File

@@ -24,7 +24,7 @@ import org.springframework.data.release.model.Module;
* @author Oliver Gierke
*/
@Value
public class MavenProject {
class MavenProject {
private final Module module;

View File

@@ -24,11 +24,13 @@ import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.jira.Changelog;
import org.springframework.data.release.jira.JiraConnector;
import org.springframework.data.release.jira.IssueTracker;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.ModuleIteration;
import org.springframework.data.release.model.ReleaseTrains;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@@ -41,7 +43,7 @@ import com.google.common.io.Files;
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ReleaseOperations {
private final JiraConnector jira;
private final PluginRegistry<IssueTracker, Project> trackers;
private final Workspace workspace;
/**
@@ -51,14 +53,13 @@ public class ReleaseOperations {
* @param iteration must not be {@literal null}.
* @throws Exception
*/
public void prepareChangelogs(Train train, Iteration iteration) throws Exception {
public void prepareChangelogs(TrainIteration iteration) throws Exception {
Assert.notNull(train, "Release train must not be null!");
Assert.notNull(iteration, "Iteration must not be null!");
for (ModuleIteration module : train.getModuleIterations(iteration, ReleaseTrains.BUILD)) {
for (ModuleIteration module : iteration) {
Changelog changelog = jira.getChangelogFor(module);
Changelog changelog = trackers.getPluginFor(module.getProject()).getChangelogFor(module);
File file = workspace.getFile("src/main/resources/changelog.txt", module.getProject());
StringBuilder builder = new StringBuilder();

View File

@@ -83,18 +83,18 @@ public class ArtifactVersion implements Comparable<ArtifactVersion> {
Assert.notNull(iterationVersion, "IterationVersion must not be null!");
Version version = iterationVersion.getVersion();
String iterationName = iterationVersion.getIteration().getName();
Iteration iteration = iterationVersion.getIteration();
if (iterationName.equals("GA")) {
if (iteration.isGAVersion()) {
return new ArtifactVersion(version, RELEASE_SUFFIX);
}
if (iterationName.startsWith("SR")) {
int bugfixDigits = Integer.parseInt(iterationName.substring(2, iterationName.length()));
return new ArtifactVersion(version.withBugfix(bugfixDigits), RELEASE_SUFFIX);
if (iteration.isServiceIteration()) {
Version bugfixVersion = version.withBugfix(iteration.getBugfixValue());
return new ArtifactVersion(bugfixVersion, RELEASE_SUFFIX);
}
return new ArtifactVersion(version, iterationName);
return new ArtifactVersion(version, iteration.getName());
}
/**

View File

@@ -18,6 +18,8 @@ package org.springframework.data.release.model;
import lombok.NonNull;
import lombok.Value;
import org.springframework.util.Assert;
/**
* Value object to represent an individual release train iteration.
*
@@ -40,6 +42,18 @@ public class Iteration {
private final @NonNull String name;
private final Iteration next;
Iteration(String name, Iteration next) {
Assert.hasText(name, "Name must not be null or empty!");
this.name = name;
this.next = next;
}
public boolean isGAVersion() {
return this.equals(GA);
}
public boolean isPublicVersion() {
return isServiceIteration() || this.equals(GA);
}

View File

@@ -16,6 +16,8 @@
package org.springframework.data.release.model;
/**
* A {@link Version} tied to an {@link Iteration}.
*
* @author Oliver Gierke
*/
public interface IterationVersion {

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.release.model;
import static org.springframework.data.release.model.Iteration.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import lombok.Value;
import org.springframework.util.Assert;
/**
* Value object to represent a set of {@link Iteration}s.
*
* @author Oliver Gierke
*/
@Value
public class Iterations implements Iterable<Iteration> {
public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4);
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
*/
public Iteration getIterationByName(String name) {
Assert.hasText(name, "Name must not be null or empty!");
for (Iteration iteration : this) {
if (iteration.getName().equalsIgnoreCase(name)) {
return iteration;
}
}
return null;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Iteration> iterator() {
return iterations.iterator();
}
}

View File

@@ -29,11 +29,11 @@ public class Module {
private final Version version;
private final Iteration customFirstIteration;
public Module(Project project, String version) {
Module(Project project, String version) {
this(project, version, null);
}
public Module(Project project, String version, String customFirstIteration) {
Module(Project project, String version, String customFirstIteration) {
Assert.notNull(project, "Project must not be null!");

View File

@@ -62,4 +62,29 @@ public class ModuleIteration implements IterationVersion {
return String.format("%s %s (%s)", module.getVersion(), iteration.getName(), train.getName());
}
/**
* Returns the {@link String} representation of the logical version of the {@link ModuleIteration}.
*
* @return
*/
public String getVersionString() {
StringBuilder builder = new StringBuilder();
builder.append(ArtifactVersion.from(this).toShortString());
if (!iteration.isServiceIteration()) {
builder.append(" ").append(iteration.getName());
}
return builder.toString();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s %s", module.getProject().getFullName(), getVersionString());
}
}

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

@@ -18,23 +18,36 @@ package org.springframework.data.release.model;
import java.util.Arrays;
import java.util.List;
import lombok.Value;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/**
* @author Oliver Gierke
*/
@Value
@ToString
@EqualsAndHashCode
public class Project {
private final ProjectKey key;
private final String name;
private final @Getter ProjectKey key;
private final @Getter String name;
private final List<Project> dependencies;
private final Tracker tracker;
public Project(String key, String name, Project... dependencies) {
Project(String key, String name, Project... dependencies) {
this(key, name, Tracker.JIRA, dependencies);
}
Project(String key, String name, Tracker tracker, Project... dependencies) {
this.key = new ProjectKey(key);
this.name = name;
this.dependencies = Arrays.asList(dependencies);
this.tracker = tracker;
}
public boolean uses(Tracker tracker) {
return this.tracker.equals(tracker);
}
public String getFullName() {

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.model;
import java.util.Arrays;
import java.util.List;
/**
* @author Oliver Gierke
*/
public class Projects {
public static final Project COMMONS, BUILD, REST, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH,
REDIS, GEMFIRE;
public static final List<Project> PROJECTS;
static {
BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB);
COMMONS = new Project("DATACMNS", "Commons", BUILD);
JPA = new Project("DATAJPA", "JPA", COMMONS);
MONGO_DB = new Project("DATAMONGO", "MongoDB", COMMONS);
NEO4J = new Project("DATAGRAPH", "Neo4j", COMMONS);
SOLR = new Project("DATASOLR", "Solr", COMMONS);
COUCHBASE = new Project("DATACOUCH", "Couchbase", COMMONS);
CASSANDRA = new Project("DATACASS", "Cassandra", COMMONS);
ELASTICSEARCH = new Project("DATAES", "Elasticsearch", COMMONS);
REDIS = new Project("DATAREDIS", "Redis");
GEMFIRE = new Project("SGF", "Gemfire", COMMONS);
REST = new Project("DATAREST", "REST", COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE);
PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS,
GEMFIRE, REST);
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.release.model;
import static org.springframework.data.release.model.Projects.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -26,30 +28,9 @@ public class ReleaseTrains {
public static final List<Train> TRAINS;
public static final Train CODD, DIJKSTRA, EVANS, FOWLER;
public static final Project COMMONS, BUILD, REST;
private static final Project JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, GEMFIRE;
private static final List<Project> PROJECTS;
static {
BUILD = new Project("DATABUILD", "Build");
COMMONS = new Project("DATACMNS", "Commons", BUILD);
JPA = new Project("DATAJPA", "JPA", COMMONS);
MONGO_DB = new Project("DATAMONGO", "MongoDB", COMMONS);
NEO4J = new Project("DATAGRAPH", "Neo4j", COMMONS);
SOLR = new Project("DATASOLR", "Solr", COMMONS);
COUCHBASE = new Project("DATACOUCH", "Couchbase", COMMONS);
CASSANDRA = new Project("DATACASS", "Cassandra", COMMONS);
ELASTICSEARCH = new Project("DATAES", "Elasticsearch", COMMONS);
REDIS = new Project("DATAREDIS", "Redis");
GEMFIRE = new Project("SGF", "Gemfire", COMMONS);
REST = new Project("DATAREST", "REST", COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE);
PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS,
GEMFIRE, REST);
CODD = codd();
DIJKSTRA = dijkstra();
EVANS = DIJKSTRA.next("Evans", Transition.MINOR);

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

@@ -15,15 +15,20 @@
*/
package org.springframework.data.release.model;
import static org.springframework.data.release.model.Iteration.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
import org.springframework.shell.support.util.OsUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -107,7 +112,7 @@ public class Train implements Iterable<Module> {
return getModuleIterations(iteration, new Project[0]);
}
public Iterable<ModuleIteration> getModuleIterations(Iteration iteration, Project... exclusions) {
List<ModuleIteration> getModuleIterations(Iteration iteration, Project... exclusions) {
List<ModuleIteration> iterations = new ArrayList<>(modules.size());
List<Project> exclusionList = Arrays.asList(exclusions);
@@ -144,4 +149,55 @@ public class Train implements Iterable<Module> {
return builder.toString();
}
/**
* Value object to represent a set of {@link Iteration}s.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
@ToString
private static class Iterations implements Iterable<Iteration> {
public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4);
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;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Iteration> iterator() {
return iterations.iterator();
}
}
}

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 java.util.Iterator;
import lombok.Value;
/**
* @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);
}
/*
* (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,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,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

@@ -15,6 +15,8 @@
*/
package org.springframework.data.release.git;
import static org.springframework.data.release.model.Projects.*;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,7 +41,7 @@ public class GitOperationsIntegrationTests extends AbstractIntegrationTests {
@Ignore
public void showTags() throws Exception {
Tags tags = gitOperations.getTags(ReleaseTrains.COMMONS);
Tags tags = gitOperations.getTags(COMMONS);
System.out.println(StringUtils.collectionToDelimitedString(tags.asList(), "\n"));
}
}

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