From cabacc002f4d717fadb2713f787f33f7a944b20f Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 30 Apr 2014 20:40:35 +0200 Subject: [PATCH] More release steps automated. - Dockbook references to Spring Data Commons files get updated. - Updating of notice.txt. - Gradle projects get Spring Data Commons version and repositories updated. - Added release commit detection and tagging for cleanup. Upgraded to Spring Boot 1.0.2.RELEASE. --- pom.xml | 2 +- .../data/release/cli/ReleaseCommands.java | 14 +- .../release/cli/TrainIterationConverter.java | 5 - .../release/docs/DocumentationOperations.java | 77 +++++++++++ .../data/release/git/GitCommands.java | 10 +- .../data/release/git/GitOperations.java | 47 +++++++ .../springframework/data/release/git/Tag.java | 10 ++ .../data/release/git/Tags.java | 6 + .../data/release/gradle/GradleOperations.java | 126 ++++++++++++++++++ .../data/release/io/Workspace.java | 49 ++++++- .../data/release/maven/MavenOperations.java | 59 ++++++-- .../data/release/misc/ReleaseOperations.java | 75 ++++++++--- .../data/release/model/Iteration.java | 4 + .../data/release/model/Project.java | 14 ++ .../data/release/model/Train.java | 17 ++- .../data/release/model/TrainIteration.java | 6 + 16 files changed, 481 insertions(+), 40 deletions(-) create mode 100644 src/main/java/org/springframework/data/release/docs/DocumentationOperations.java create mode 100644 src/main/java/org/springframework/data/release/gradle/GradleOperations.java diff --git a/pom.xml b/pom.xml index bfbff66..1fd91bc 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.springframework.boot spring-boot-starter-parent - 1.0.0.RC4 + 1.0.2.RELEASE 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 2b4667c..8de7c1e 100644 --- a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -19,8 +19,10 @@ 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; @@ -43,8 +45,10 @@ import org.springframework.stereotype.Component; 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 { @@ -95,18 +99,24 @@ public class ReleaseCommands implements CommandMarker { 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); } @CliCommand(value = "release conclude") public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - // - pull updates - // - tag release + git.tagRelease(iteration); + // - post release pom updates maven.updatePom(iteration, Phase.CLEANUP); + gradle.updateProject(iteration, Phase.CLEANUP); // - push } diff --git a/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java b/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java index 61ab822..6850c2a 100644 --- a/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java +++ b/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java @@ -65,11 +65,6 @@ public class TrainIterationConverter implements Converter { 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())); } diff --git a/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java b/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java new file mode 100644 index 0000000..584cf89 --- /dev/null +++ b/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java @@ -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; + } + }); + } + } +} 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 0afaf99..e78077b 100644 --- a/src/main/java/org/springframework/data/release/git/GitCommands.java +++ b/src/main/java/org/springframework/data/release/git/GitCommands.java @@ -20,6 +20,7 @@ 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; @@ -55,9 +56,14 @@ public class GitCommands implements CommandMarker { 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 = { "", "train" }, mandatory = true) String trainName, @CliOption(key = "iteration", - mandatory = true) String iterationName) throws Exception { + public void reset(@CliOption(key = { "", "train" }, mandatory = true) String trainName) throws Exception { git.reset(ReleaseTrains.getTrainByName(trainName)); } 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 3b8cd7d..1c80a2d 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -27,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired; 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.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; @@ -35,6 +37,7 @@ 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; import org.springframework.util.StringUtils; @@ -52,6 +55,7 @@ public class GitOperations { private final OsCommandOperations osCommandOperations; private final Workspace workspace; private final Logger logger; + private final PluginRegistry issueTracker; public GitProject getGitProject(Project project) { return new GitProject(project, server); @@ -168,6 +172,49 @@ public class GitOperations { return new Tags(tags); } + public void tagRelease(TrainIteration iteration) throws Exception { + + for (ModuleIteration module : iteration) { + + Branch branch = Branch.from(module); + Project project = module.getProject(); + + String checkoutCommand = String.format("git checkout %s", branch); + osCommandOperations.executeCommand(checkoutCommand, project).get(); + + String updateCommand = String.format("git pull origin %s", branch); + osCommandOperations.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(); + } + } + + private String getReleaseHash(ModuleIteration module) throws Exception { + + Project project = module.getProject(); + + String result = osCommandOperations.executeForResult("git log --pretty=format:'%h %s'", project); + Ticket releaseTicket = issueTracker.getPluginFor(project).getReleaseTicketFor(module); + String trigger = String.format("%s - Release", releaseTicket.getId()); + + logger.log(project, "Looking up release commit (ticket id %s)", releaseTicket.getId()); + + for (String line : result.split("\n")) { + + int summaryStart = line.indexOf(" "); + + if (line.substring(summaryStart + 1).startsWith(trigger)) { + return line.substring(0, summaryStart); + } + } + + 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}. * diff --git a/src/main/java/org/springframework/data/release/git/Tag.java b/src/main/java/org/springframework/data/release/git/Tag.java index 77172c6..c09210f 100644 --- a/src/main/java/org/springframework/data/release/git/Tag.java +++ b/src/main/java/org/springframework/data/release/git/Tag.java @@ -45,6 +45,16 @@ public class Tag implements Comparable { 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() diff --git a/src/main/java/org/springframework/data/release/git/Tags.java b/src/main/java/org/springframework/data/release/git/Tags.java index 231b68c..ec2fde6 100644 --- a/src/main/java/org/springframework/data/release/git/Tags.java +++ b/src/main/java/org/springframework/data/release/git/Tags.java @@ -22,6 +22,8 @@ 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; /** @@ -58,6 +60,10 @@ public class Tags implements Iterable { return tags.get(0); } + public Tag createTag(ModuleIteration iteration) { + return getLatest().createNew(ArtifactVersion.from(iteration)); + } + /** * Returns all {@link Tag}s as {@link List}. * diff --git a/src/main/java/org/springframework/data/release/gradle/GradleOperations.java b/src/main/java/org/springframework/data/release/gradle/GradleOperations.java new file mode 100644 index 0000000..ba32109 --- /dev/null +++ b/src/main/java/org/springframework/data/release/gradle/GradleOperations.java @@ -0,0 +1,126 @@ +/* + * 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 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); + + 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)) { + return line; + } + + 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); + } + }); + + 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(); + } +} diff --git a/src/main/java/org/springframework/data/release/io/Workspace.java b/src/main/java/org/springframework/data/release/io/Workspace.java index 6e924d2..34366bc 100644 --- a/src/main/java/org/springframework/data/release/io/Workspace.java +++ b/src/main/java/org/springframework/data/release/io/Workspace.java @@ -17,8 +17,9 @@ package org.springframework.data.release.io; import java.io.File; import java.io.IOException; -import java.nio.file.Files; +import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.Scanner; import javax.annotation.PostConstruct; @@ -30,6 +31,8 @@ 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. * @@ -39,6 +42,8 @@ import org.springframework.util.Assert; @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; @@ -93,6 +98,40 @@ public class Workspace { 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. * @@ -103,8 +142,12 @@ public class Workspace { Path path = getWorkingDirectory().toPath(); - if (!Files.exists(path)) { - Files.createDirectories(path); + if (!java.nio.file.Files.exists(path)) { + java.nio.file.Files.createDirectories(path); } } + + public interface LineCallback { + String doWith(String line, long number); + } } diff --git a/src/main/java/org/springframework/data/release/maven/MavenOperations.java b/src/main/java/org/springframework/data/release/maven/MavenOperations.java index 012454c..febd8ef 100644 --- a/src/main/java/org/springframework/data/release/maven/MavenOperations.java +++ b/src/main/java/org/springframework/data/release/maven/MavenOperations.java @@ -31,10 +31,12 @@ 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.Projects; 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; @@ -59,8 +61,25 @@ public class MavenOperations { return projectionFactory.io().file(file).read(Pom.class); } + /** + * Updates the POM files for all Maven projects contained in the iteration: + *
    + *
  1. Updates the BOM POM.
  2. + *
  3. Updates the dependency version to Spring Data Commons to the current release version for all projects depending + * on it.
  4. + *
  5. Switches to the Spring release Maven repository.
  6. + *
+ * 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()); @@ -70,20 +89,31 @@ public class MavenOperations { for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { final Project project = module.getProject(); - File pomFile = workspace.getFile(POM_XML, project); - execute(pomFile, new PomCallback() { + if (!isMavenProject(project)) { + logger.log(module, "No pom.xml file found, skipping project."); + continue; + } + + execute(workspace.getFile(POM_XML, project), new PomCallback() { @Override public Pom doWith(Pom pom) { - if (!project.equals(COMMONS)) { - pom.setProperty(COMMONS_VERSION_PROPERTY, - CLEANUP.equals(phase) ? commonsVersion.getNextDevelopmentVersion() : commonsVersion); + if (project.dependsOn(Projects.COMMONS)) { + + ArtifactVersion version = CLEANUP.equals(phase) ? commonsVersion.getNextDevelopmentVersion() + : commonsVersion; + logger.log(project, "Updating Spring Data Commons version dependecy to %s (setting property %s).", version, + COMMONS_VERSION_PROPERTY); + pom.setProperty(COMMONS_VERSION_PROPERTY, version); } - pom.setParentVersion(CLEANUP.equals(phase) ? buildVersion.getNextDevelopmentVersion() : buildVersion); - updateRepository(pom, repository, phase); + ArtifactVersion version = CLEANUP.equals(phase) ? buildVersion.getNextDevelopmentVersion() : buildVersion; + logger.log(project, "Updating Spring Data Build Parent version to %s.", version); + pom.setParentVersion(version); + + updateRepository(project, pom, repository, phase); return pom; } @@ -144,6 +174,8 @@ public class MavenOperations { File bomPomFile = workspace.getFile("bom/pom.xml", BUILD); + logger.log(BUILD, "Updating BOM pom.xml…"); + execute(bomPomFile, new PomCallback() { @Override @@ -155,6 +187,8 @@ public class MavenOperations { ArtifactVersion version = artifact.getVersion(); version = PREPARE.equals(phase) ? version : version.getNextDevelopmentVersion(); + logger.log(BUILD, "%s", module); + pom.setDependencyVersion(artifact.getArtifactId(), version); } @@ -163,12 +197,21 @@ public class MavenOperations { }); } - private void updateRepository(Pom pom, Repository repository, Phase phase) { + 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()); } 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 6c02351..fc78f47 100644 --- a/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java +++ b/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java @@ -15,14 +15,15 @@ */ package org.springframework.data.release.misc; -import java.io.File; -import java.nio.charset.Charset; -import java.util.Scanner; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; 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.jira.Changelog; import org.springframework.data.release.jira.IssueTracker; import org.springframework.data.release.model.Iteration; @@ -30,12 +31,11 @@ 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; -import com.google.common.io.Files; - /** * @author Oliver Gierke */ @@ -43,8 +43,20 @@ import com.google.common.io.Files; @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class ReleaseOperations { + private static final Set CHANGELOG_LOCATIONS; + + static { + + Set 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 trackers; private final Workspace workspace; + private final Logger logger; /** * Creates {@link Changelog} instances for all modules of the given {@link Train} and {@link Iteration}. @@ -59,26 +71,53 @@ public class ReleaseOperations { for (ModuleIteration module : iteration) { - Changelog changelog = trackers.getPluginFor(module.getProject()).getChangelogFor(module); - File file = workspace.getFile("src/main/resources/changelog.txt", module.getProject()); - StringBuilder builder = new StringBuilder(); + final Changelog changelog = trackers.getPluginFor(module.getProject()).getChangelogFor(module); - try (Scanner scanner = new Scanner(file)) { + for (String location : CHANGELOG_LOCATIONS) { - // Copy headline - builder.append(scanner.nextLine()).append("\n"); - builder.append(scanner.nextLine()).append("\n"); + boolean processed = workspace.processFile(location, module.getProject(), new LineCallback() { - // Add new changelog - builder.append(changelog.toString()); + @Override + public String doWith(String line, long number) { - // Append existing - while (scanner.hasNextLine()) { - builder.append(scanner.nextLine()).append("\n"); + 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) { + logger.log(module.getProject(), "Updated changelog %s.", location); } } + } + } - Files.write(builder, file, Charset.forName("UTF-8")); + public void updateResources(TrainIteration iteration) throws Exception { + + for (final ModuleIteration module : iteration) { + + 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(); + } + }); + + logger.log(module, "Updated notice.txt."); } } } diff --git a/src/main/java/org/springframework/data/release/model/Iteration.java b/src/main/java/org/springframework/data/release/model/Iteration.java index 935a1d4..90bdf31 100644 --- a/src/main/java/org/springframework/data/release/model/Iteration.java +++ b/src/main/java/org/springframework/data/release/model/Iteration.java @@ -62,6 +62,10 @@ public class Iteration { return name.startsWith("SR"); } + public boolean isNext(Iteration iteration) { + return next.equals(iteration); + } + public int getBugfixValue() { return name.startsWith("SR") ? Integer.parseInt(name.substring(2)) : 0; } diff --git a/src/main/java/org/springframework/data/release/model/Project.java b/src/main/java/org/springframework/data/release/model/Project.java index 8cb89d2..76cdf62 100644 --- a/src/main/java/org/springframework/data/release/model/Project.java +++ b/src/main/java/org/springframework/data/release/model/Project.java @@ -22,6 +22,8 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; +import org.springframework.util.Assert; + /** * @author Oliver Gierke */ @@ -53,4 +55,16 @@ public class Project { public String getFullName() { return "Spring Data ".concat(name); } + + /** + * 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); + } } diff --git a/src/main/java/org/springframework/data/release/model/Train.java b/src/main/java/org/springframework/data/release/model/Train.java index 7f06b5b..6622c5a 100644 --- a/src/main/java/org/springframework/data/release/model/Train.java +++ b/src/main/java/org/springframework/data/release/model/Train.java @@ -139,6 +139,10 @@ public class Train implements Iterable { return ArtifactVersion.from(new ModuleIteration(module, iteration, this)); } + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ @Override public String toString() { @@ -157,7 +161,7 @@ public class Train implements Iterable { */ @EqualsAndHashCode @ToString - private static class Iterations implements Iterable { + public static class Iterations implements Iterable { public static Iterations DEFAULT = new Iterations(M1, RC1, GA, SR1, SR2, SR3, SR4); @@ -191,6 +195,17 @@ public class Train implements Iterable { 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() diff --git a/src/main/java/org/springframework/data/release/model/TrainIteration.java b/src/main/java/org/springframework/data/release/model/TrainIteration.java index b00372c..1d89850 100644 --- a/src/main/java/org/springframework/data/release/model/TrainIteration.java +++ b/src/main/java/org/springframework/data/release/model/TrainIteration.java @@ -53,6 +53,12 @@ public class TrainIteration implements Iterable { 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()