diff --git a/infrastructure.properties b/infrastructure.properties index 1f36307..c6ec028 100644 --- a/infrastructure.properties +++ b/infrastructure.properties @@ -1 +1,3 @@ -io.workDir=~/temp/spring-data-shell \ No newline at end of file +io.workDir=~/temp/spring-data-shell + +git.author=Oliver Gierke \ No newline at end of file diff --git a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java index 8de7c1e..ac4b94b 100644 --- a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -106,6 +106,8 @@ public class ReleaseCommands implements CommandMarker { maven.updatePom(iteration, Phase.PREPARE); gradle.updateProject(iteration, Phase.PREPARE); + + git.commit(iteration, "Prepare %s.", null); } @CliCommand(value = "release conclude") @@ -113,11 +115,9 @@ public class ReleaseCommands implements CommandMarker { git.tagRelease(iteration); - // - post release pom updates - maven.updatePom(iteration, Phase.CLEANUP); gradle.updateProject(iteration, Phase.CLEANUP); - // - push + git.commit(iteration, "After release cleanups.", null); } } diff --git a/src/main/java/org/springframework/data/release/git/Commit.java b/src/main/java/org/springframework/data/release/git/Commit.java new file mode 100644 index 0000000..d7bfd98 --- /dev/null +++ b/src/main/java/org/springframework/data/release/git/Commit.java @@ -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(); + } +} diff --git a/src/main/java/org/springframework/data/release/git/GitCommands.java b/src/main/java/org/springframework/data/release/git/GitCommands.java index e78077b..db07d85 100644 --- a/src/main/java/org/springframework/data/release/git/GitCommands.java +++ b/src/main/java/org/springframework/data/release/git/GitCommands.java @@ -63,12 +63,35 @@ public class GitCommands implements CommandMarker { * @throws Exception */ @CliCommand("git reset") - public void reset(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception { - git.reset(ReleaseTrains.getTrainByName(trainName)); + 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); + } + } } diff --git a/src/main/java/org/springframework/data/release/git/GitOperations.java b/src/main/java/org/springframework/data/release/git/GitOperations.java index d067486..55f7fb7 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -24,6 +24,7 @@ import java.util.concurrent.Future; import lombok.RequiredArgsConstructor; 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.OsCommandOperations; import org.springframework.data.release.io.Workspace; @@ -52,10 +53,11 @@ import org.springframework.util.StringUtils; public class GitOperations { private final GitServer server = new GitServer(); - private final OsCommandOperations osCommandOperations; + private final OsCommandOperations os; private final Workspace workspace; private final Logger logger; private final PluginRegistry issueTracker; + private final Environment environment; public GitProject getGitProject(Project project) { return new GitProject(project, server); @@ -67,12 +69,15 @@ public class GitOperations { * @param train must not be {@literal null}. * @throws Exception */ - public void reset(Train train) throws Exception { + public void reset(TrainIteration train) throws Exception { Assert.notNull(train, "Train must not be null!"); - for (Module module : train) { - osCommandOperations.executeCommand("git reset --hard", module.getProject()).get(); + for (ModuleIteration module : train) { + + Branch branch = Branch.from(module); + + os.executeCommand(String.format("git reset --hard origin/%s", branch), module.getProject()).get(); } } @@ -99,7 +104,7 @@ public class GitOperations { artifactVersion, project)); } - osCommandOperations.executeCommand(String.format("git checkout %s", tag), project).get(); + os.executeCommand(String.format("git checkout %s", tag), project).get(); } logger.log(iteration, "Successfully checked out projects."); @@ -114,7 +119,7 @@ public class GitOperations { update(module.getProject()).get(); String checkoutCommand = String.format("git checkout %s && git pull origin %s", branch, branch); - osCommandOperations.executeCommand(checkoutCommand, module.getProject()).get(); + os.executeCommand(checkoutCommand, module.getProject()).get(); } } @@ -131,6 +136,22 @@ public class GitOperations { } } + public void push(TrainIteration iteration) throws Exception { + + for (ModuleIteration module : iteration) { + + Branch branch = Branch.from(module); + os.executeCommand(String.format("git push origin %s", branch), module.getProject()).get(); + } + } + + public void pushTags(Train train) throws Exception { + + for (Module module : train) { + os.executeCommand("git push --tags", module.getProject()).get(); + } + } + public Future update(Project project) throws Exception { GitProject gitProject = new GitProject(project, server); @@ -140,8 +161,8 @@ public class GitOperations { logger.log(project, "Found existing repository %s. Obtaining latest changes…", repositoryName); - return osCommandOperations.executeCommand( - "git checkout master && git reset --hard && git fetch --tags && git pull origin master", project); + return os.executeCommand("git checkout master && git reset --hard && git fetch --tags && git pull origin master", + project); } else { @@ -150,13 +171,13 @@ public class GitOperations { File projectDirectory = workspace.getProjectDirectory(project); String command = String.format("git clone %s %s", gitProject.getProjectUri(), projectDirectory.getName()); - return osCommandOperations.executeCommand(command); + return os.executeCommand(command); } } public Tags getTags(Project project) throws Exception { - String result = osCommandOperations.executeForResult("git tag -l", project); + String result = os.executeForResult("git tag -l", project); List tags = new ArrayList<>(); for (String line : result.split("\n")) { @@ -177,15 +198,74 @@ public class GitOperations { Project project = module.getProject(); String checkoutCommand = String.format("git checkout %s", branch); - osCommandOperations.executeCommand(checkoutCommand, project).get(); + os.executeCommand(checkoutCommand, project).get(); String updateCommand = String.format("git pull origin %s", branch); - osCommandOperations.executeCommand(updateCommand, project).get(); + os.executeCommand(updateCommand, project).get(); String hash = getReleaseHash(module); Tag tag = getTags(project).createTag(module); String tagCommand = String.format("git tag %s %s", tag, hash); - osCommandOperations.executeCommand(tagCommand, project).get(); + os.executeCommand(tagCommand, project).get(); + } + } + + /** + * 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) { + + if (summary.contains("%s")) { + summary = String.format(summary, module.getVersionString()); + } + + commit(module, summary, details); + } + } + + /** + * 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 commitCommand = String.format("git commit -m \"%s\" --author \"%s\"", commit, author); + + if (files.length != 0) { + + for (File file : files) { + os.executeCommand(String.format("git add %s", file.getAbsolutePath()), project).get(); + } + + os.executeCommand(commitCommand, project).get(); + } else { + os.executeCommand(commitCommand.concat(" -a"), project).get(); } } @@ -193,7 +273,7 @@ public class GitOperations { Project project = module.getProject(); - String result = osCommandOperations.executeForResult("git log --pretty=format:'%h %s'", project); + String result = os.executeForResult("git log --pretty=format:'%h %s'", project); Ticket releaseTicket = issueTracker.getPluginFor(project).getReleaseTicketFor(module); String trigger = String.format("%s - Release", releaseTicket.getId()); diff --git a/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java b/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java index fc78f47..e5c34b1 100644 --- a/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java +++ b/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java @@ -15,6 +15,7 @@ */ package org.springframework.data.release.misc; +import java.io.File; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -22,6 +23,7 @@ import java.util.Set; import lombok.RequiredArgsConstructor; 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; @@ -56,6 +58,7 @@ public class ReleaseOperations { private final PluginRegistry trackers; private final Workspace workspace; + private final GitOperations git; private final Logger logger; /** @@ -94,6 +97,10 @@ public class ReleaseOperations { }); if (processed) { + + File file = workspace.getFile(location, module.getProject()); + git.commit(module, "Updated changelog.", null, file); + logger.log(module.getProject(), "Updated changelog %s.", location); } } @@ -104,20 +111,23 @@ public class ReleaseOperations { for (final ModuleIteration module : iteration) { - workspace.processFile("src/main/resources/notice.txt", module.getProject(), new LineCallback() { + boolean processed = workspace.processFile("src/main/resources/notice.txt", module.getProject(), + new LineCallback() { - @Override - public String doWith(String line, long number) { + @Override + public String doWith(String line, long number) { - if (number != 0) { - return line; - } + if (number != 0) { + return line; + } - return module.toString(); - } - }); + return module.toString(); + } + }); - logger.log(module, "Updated notice.txt."); + if (processed) { + logger.log(module, "Updated notice.txt."); + } } } }