From 2efc0bcbfcd7837c52593629d304e86c6d53f6df Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 10 Feb 2016 12:01:06 +0100 Subject: [PATCH] A way too big commit. Switched to Apache Maven Invoker for running Maven on the projects. Added JGraphT to correctly sort dependencies in topological order. Upgraded to Spring Boot 1.3.2, Lombok 1.16.6, jGit 4.2.0. Upgraded to Spring Plugin 1.3 to benefit from lazy exception creation using lambdas. Added Lombok configuration to throw IllegalArgumentException on null checks. --- release-tools/application-local.template | 11 + release-tools/lombok.config | 1 + release-tools/pom.xml | 26 +- release-tools/readme.md | 5 +- .../data/release/Streamable.java | 36 +++ .../announcement/AnnouncementCommands.java | 43 +++ .../announcement/AnnouncementOperations.java | 30 +- .../BuildConfiguration.java} | 22 +- .../data/release/build/BuildOperations.java | 162 ++++++++++ .../data/release/build/BuildSystem.java | 45 +++ .../data/release/build/GradleBuildSystem.java | 171 ++++++++++ .../release/{maven => build}/GroupId.java | 4 +- .../MavenArtifact.java} | 15 +- .../data/release/build/MavenBuildSystem.java | 304 ++++++++++++++++++ .../data/release/build/MavenProperties.java | 73 +++++ .../data/release/build/MavenRuntime.java | 110 +++++++ .../release/{maven => build}/ParentPom.java | 5 +- .../data/release/{maven => build}/Pom.java | 19 +- .../data/release/build/PomUpdater.java | 111 +++++++ .../release/{maven => build}/Repository.java | 26 +- .../release/cli/IssueTrackerCommands.java | 5 +- .../data/release/cli/ReleaseCommands.java | 103 ++++-- .../release/cli/TrainIterationConverter.java | 2 +- .../release/deployment/ArtifactoryClient.java | 89 +++++ .../deployment/DeploymentConfiguration.java | 76 +++++ .../deployment/DeploymentInformation.java | 95 ++++++ .../deployment/DeploymentOperations.java | 46 +++ .../deployment/DeploymentProperties.java | 98 ++++++ .../data/release/deployment/Errors.java | 56 ++++ .../release/docs/DocumentationOperations.java | 18 +- .../data/release/git/Branch.java | 27 +- .../data/release/git/Commit.java | 10 +- .../data/release/git/GitCommands.java | 7 +- .../data/release/git/GitOperations.java | 188 ++++++++--- .../data/release/git/GitProperties.java | 4 + .../data/release/git/VersionTags.java | 3 +- .../data/release/gradle/GradleOperations.java | 139 -------- .../io/CommonsExecOsCommandOperations.java | 47 ++- .../data/release/io/IoProperties.java | 12 +- ...mmandOperations.java => OsOperations.java} | 5 +- .../data/release/io/Workspace.java | 31 +- .../data/release/jira/GitHubIssue.java | 2 +- .../data/release/jira/GitHubIssueTracker.java | 35 +- ...on.java => IssueTrackerConfiguration.java} | 26 +- .../data/release/jira/Jira.java | 5 +- .../data/release/jira/JiraVersion.java | 25 +- .../data/release/jira/JqlQuery.java | 4 +- .../data/release/maven/MavenOperations.java | 251 --------------- .../data/release/misc/ReleaseOperations.java | 40 +-- .../ArtifactCoordinate.java} | 25 +- .../release/model/ArtifactCoordinates.java | 58 ++++ .../data/release/model/IterationVersion.java | 6 +- .../data/release/model/Module.java | 2 +- .../data/release/model/ModuleIteration.java | 62 +++- .../data/release/model/Modules.java | 36 +++ .../data/release/model/Phase.java | 2 +- .../data/release/model/Project.java | 18 +- .../data/release/model/Projects.java | 54 +++- .../data/release/model/ReleaseTrains.java | 4 +- .../data/release/model/Train.java | 26 +- .../data/release/model/TrainIteration.java | 21 +- .../data/release/model/UpdateInformation.java | 94 ++++++ .../data/release/model/VersionAware.java | 24 ++ .../data/release/utils/ExecutionUtils.java | 20 +- .../data/release/utils/Logger.java | 12 +- .../data/release/utils/StreamUtils.java | 38 +++ .../src/main/resources/application.properties | 11 + .../release/AbstractIntegrationTests.java | 2 +- .../data/release/ApplicationTests.java | 27 ++ .../data/release/ArtifactUnitTests.java | 4 +- .../MavenIntegrationTests.java | 27 +- ...DeploymentInformationIntegrationTests.java | 51 +++ .../DeploymentOperationsIntegrationTests.java | 56 ++++ .../model/ModuleIterationUnitTests.java | 8 +- .../data/release/model/ProjectUnitTests.java | 45 +++ .../release/model/SimpleIterationVersion.java | 9 + .../data/release/model/TrainsUnitTest.java | 14 + .../model/UpdateInformationUnitTests.java | 68 ++++ .../resources/application-test.properties | 13 +- 79 files changed, 2776 insertions(+), 729 deletions(-) create mode 100644 release-tools/application-local.template create mode 100644 release-tools/lombok.config create mode 100644 release-tools/src/main/java/org/springframework/data/release/Streamable.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java rename release-tools/src/main/java/org/springframework/data/release/{maven/MavenConfig.java => build/BuildConfiguration.java} (66%) create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/GradleBuildSystem.java rename release-tools/src/main/java/org/springframework/data/release/{maven => build}/GroupId.java (91%) rename release-tools/src/main/java/org/springframework/data/release/{maven/Artifact.java => build/MavenArtifact.java} (83%) create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java rename release-tools/src/main/java/org/springframework/data/release/{maven => build}/ParentPom.java (87%) rename release-tools/src/main/java/org/springframework/data/release/{maven => build}/Pom.java (85%) create mode 100644 release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java rename release-tools/src/main/java/org/springframework/data/release/{maven => build}/Repository.java (68%) create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java delete mode 100644 release-tools/src/main/java/org/springframework/data/release/gradle/GradleOperations.java rename release-tools/src/main/java/org/springframework/data/release/io/{OsCommandOperations.java => OsOperations.java} (95%) rename release-tools/src/main/java/org/springframework/data/release/jira/{JiraConfiguration.java => IssueTrackerConfiguration.java} (73%) delete mode 100644 release-tools/src/main/java/org/springframework/data/release/maven/MavenOperations.java rename release-tools/src/main/java/org/springframework/data/release/{maven/MavenProject.java => model/ArtifactCoordinate.java} (52%) create mode 100644 release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinates.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/model/Modules.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/model/UpdateInformation.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/model/VersionAware.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/utils/StreamUtils.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/ApplicationTests.java rename release-tools/src/test/java/org/springframework/data/release/{maven => build}/MavenIntegrationTests.java (64%) create mode 100644 release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentInformationIntegrationTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentOperationsIntegrationTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/model/ProjectUnitTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/model/UpdateInformationUnitTests.java diff --git a/release-tools/application-local.template b/release-tools/application-local.template new file mode 100644 index 0000000..57d53ff --- /dev/null +++ b/release-tools/application-local.template @@ -0,0 +1,11 @@ +# Git +git.username= +git.author= +git.email= +git.password= + +# Maven +maven.mavenHome= + +# Deployment +deployment.password= diff --git a/release-tools/lombok.config b/release-tools/lombok.config new file mode 100644 index 0000000..71bb610 --- /dev/null +++ b/release-tools/lombok.config @@ -0,0 +1 @@ +lombok.nonNull.exceptionType = IllegalArgumentException diff --git a/release-tools/pom.xml b/release-tools/pom.xml index bc65060..56efef4 100644 --- a/release-tools/pom.xml +++ b/release-tools/pom.xml @@ -8,12 +8,13 @@ org.springframework.boot spring-boot-starter-parent - 1.3.0.RELEASE + 1.3.2.RELEASE 1.8 org.springframework.data.release.Application + 1.3.0.BUILD-SNAPSHOT @@ -50,9 +51,9 @@ - org.xmlbeam - xmlprojector - 1.4.7 + org.xmlbeam + xmlprojector + 1.4.7 @@ -64,7 +65,7 @@ org.projectlombok lombok - 1.16.4 + 1.16.6 provided @@ -81,7 +82,7 @@ org.eclipse.jgit org.eclipse.jgit - 4.0.1.201506240215-r + 4.2.0.201601211800-r @@ -89,6 +90,19 @@ spring-boot-configuration-processor true + + + org.apache.maven.shared + maven-invoker + 2.2 + + + + org.jgrapht + jgrapht-core + 0.9.1 + + diff --git a/release-tools/readme.md b/release-tools/readme.md index 8d34a9a..b3cc1a4 100644 --- a/release-tools/readme.md +++ b/release-tools/readme.md @@ -1,8 +1,11 @@ 1. Add an `application-local.properties` to the project root and add the following properties: - `git.username` - Your GitHub username. -- `git.password` - Your GitHub password. +- `git.password` - Your GitHub password or API key. - `git.author` - Your full name (used for preparing commits). - `git.email` - Your email (used for preparing commits). +- `maven.mavenHome` - Pointing to the location of your Maven installation. +- `deployment.api-key` - The API key to use for artifact promotion. +- `deployment.password` - The password of the deployment user (buildmaster). 2. Run `mvn package appassembler:assemble && sh target/appassembler/bin/spring-data-release-shell` diff --git a/release-tools/src/main/java/org/springframework/data/release/Streamable.java b/release-tools/src/main/java/org/springframework/data/release/Streamable.java new file mode 100644 index 0000000..701cb77 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/Streamable.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release; + +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * SImple interface to ease streamability of {@link Iterable}s. + * + * @author Oliver Gierke + */ +public interface Streamable extends Iterable { + + /** + * Creates a non-parallel {@link Stream} of the underlying {@link Iterable}. + * + * @return will never be {@literal null}. + */ + public default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java new file mode 100644 index 0000000..61536ed --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.announcement; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.CliComponent; +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; + +/** + * Commands to create markup to be used in announcing blog posts. + * + * @author Oliver Gierke + */ +@CliComponent +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) +public class AnnouncementCommands implements CommandMarker { + + private final @NonNull AnnouncementOperations operations; + + @CliCommand("announcement") + public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { + System.out.println(operations.getProjectBulletpoints(iteration)); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java index 41c6b47..9e3bf08 100644 --- a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -17,26 +17,22 @@ package org.springframework.data.release.announcement; import static org.springframework.data.release.model.Projects.*; +import org.springframework.data.release.build.MavenArtifact; import org.springframework.data.release.cli.StaticResources; -import org.springframework.data.release.maven.Artifact; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; import org.springframework.data.release.model.TrainIteration; +import org.springframework.stereotype.Component; import org.springframework.util.Assert; /** * @author Oliver Gierke */ +@Component public class AnnouncementOperations { /** - * Returns the project list and links to be included in the release announcement for the given {@link Train} and - * {@link Iteration}. + * Returns the project list and links to be included in the release announcement for the given {@link TrainIteration}. * - * @param train must not be {@literal null}. * @param iteration must not be {@literal null}. * @return */ @@ -46,16 +42,16 @@ public class AnnouncementOperations { StringBuilder builder = new StringBuilder(); - for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { + iteration.getModulesExcept(BUILD).forEach(module -> { Project project = module.getProject(); builder.append("* "); builder.append(project.getFullName()).append(" "); - builder.append(module.getVersionString()); + builder.append(module.getShortVersionString()); builder.append(" - "); - Artifact artifact = new Artifact(module); + MavenArtifact artifact = new MavenArtifact(module); builder.append(getMarkDownLink("Artifacts", artifact.getRootUrl())); builder.append(" - "); @@ -67,18 +63,12 @@ public class AnnouncementOperations { builder.append(getMarkDownLink("Changelog", resources.getChangelogUrl())); builder.append("\n"); - } + }); return builder.toString(); } - private String getMarkDownLink(String name, String url) { + private static String getMarkDownLink(String name, String url) { return String.format("[%s](%s)", name, url); } - - public static void main(String[] args) { - - AnnouncementOperations operations = new AnnouncementOperations(); - System.out.println(operations.getProjectBulletpoints(new TrainIteration(ReleaseTrains.GOSLING, Iteration.SR1))); - } } diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/MavenConfig.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java similarity index 66% rename from release-tools/src/main/java/org/springframework/data/release/maven/MavenConfig.java rename to release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java index 7e10646..e5d5ca7 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/MavenConfig.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; + +import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.release.model.Project; +import org.springframework.plugin.core.OrderAwarePluginRegistry; +import org.springframework.plugin.core.PluginRegistry; import org.xmlbeam.ProjectionFactory; import org.xmlbeam.XBProjector; import org.xmlbeam.XBProjector.Flags; @@ -24,10 +29,17 @@ import org.xmlbeam.config.DefaultXMLFactoriesConfig; import org.xmlbeam.config.DefaultXMLFactoriesConfig.NamespacePhilosophy; /** + * Spring configuration for build related components. + * * @author Oliver Gierke */ @Configuration -class MavenConfig { +class BuildConfiguration { + + @Bean + public PluginRegistry buildSystems(List buildSystems) { + return OrderAwarePluginRegistry.create(buildSystems); + } @Bean public ProjectionFactory projectionFactory() { @@ -36,8 +48,6 @@ class MavenConfig { config.setNamespacePhilosophy(NamespacePhilosophy.AGNOSTIC); config.setOmitXMLDeclaration(false); - XBProjector projector = new XBProjector(config, Flags.TO_STRING_RENDERS_XML); - - return projector; + return new XBProjector(config, Flags.TO_STRING_RENDERS_XML); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java new file mode 100644 index 0000000..2ab0e5e --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java @@ -0,0 +1,162 @@ +/* + * Copyright 2016 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.build; + +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.deployment.DeploymentInformation; +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.model.UpdateInformation; +import org.springframework.plugin.core.PluginRegistry; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +/** + * @author Oliver Gierke + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) +public class BuildOperations { + + private final PluginRegistry buildSystems; + + /** + * Updates all inter-project dependencies based on the given {@link TrainIteration} and release {@link Phase}. + * + * @param iteration must not be {@literal null}. + * @param phase must not be {@literal null}. + * @throws Exception + */ + public void updateProjectDescriptors(TrainIteration iteration, Phase phase) throws Exception { + + Assert.notNull(iteration, "Train iteration must not be null!"); + Assert.notNull(phase, "Phase must not be null!"); + + UpdateInformation updateInformation = new UpdateInformation(iteration, phase); + + doWithBuildSystem(iteration, (system, it) -> system.updateProjectDescriptors(it, updateInformation)); + } + + /** + * Triggers the distribution builds for all modules particitpating in the given {@link TrainIteration}. + * + * @param iteration must not be {@literal null}. + */ + public void distributeResources(TrainIteration iteration) { + + Assert.notNull(iteration, "Train iteration must not be null!"); + + doWithBuildSystem(iteration, BuildSystem::triggerDistributionBuild); + } + + /** + * Performs the release build for all modules in the given {@link TrainIteration}. + * + * @param iteration must not be {@literal null}. + * @return + */ + public List performRelease(TrainIteration iteration) { + return iteration.stream().map(this::performRelease).collect(Collectors.toList()); + } + + /** + * Performs the release build for the given {@link ModuleIteration}. + * + * @param module must not be {@literal null}. + * @return + */ + public DeploymentInformation performRelease(ModuleIteration module) { + + prepareVersion(module, Phase.PREPARE); + return buildAndDeployRelease(module); + } + + /** + * Prepares the versions of the given {@link TrainIteration} depending on the given {@link Phase}. + * + * @param iteration must not be {@literal null}. + * @param phase must not be {@literal null}. + */ + public void prepareVersions(TrainIteration iteration, Phase phase) { + + Assert.notNull(iteration, "Train iteration must not be null!"); + Assert.notNull(phase, "Phase must not be null!"); + + doWithBuildSystem(iteration, (system, module) -> system.prepareVersion(module, phase)); + } + + /** + * Prepares the version of the given {@link ModuleIteration} depending on the given {@link Phase}. + * + * @param iteration must not be {@literal null}. + * @param phase must not be {@literal null}. + * @return + */ + public ModuleIteration prepareVersion(ModuleIteration iteration, Phase phase) { + + Assert.notNull(iteration, "Module iteration must not be null!"); + Assert.notNull(phase, "Phase must not be null!"); + + return doWithBuildSystem(iteration, (system, module) -> system.prepareVersion(module, phase)); + } + + /** + * Builds the release for the given {@link ModuleIteration} and deploys it to the staging repository. + * + * @param module must not be {@literal null}. + * @return + */ + public DeploymentInformation buildAndDeployRelease(ModuleIteration module) { + return doWithBuildSystem(module, BuildSystem::deploy); + } + + /** + * Selects the build system for each {@link ModuleIteration} contained in the given {@link TrainIteration} and + * executes the given function for it. + * + * @param iteration must not be {@literal null}. + * @param function must not be {@literal null}. + * @return + */ + private List doWithBuildSystem(TrainIteration iteration, + BiFunction function) { + + return iteration.stream()// + .map(module -> doWithBuildSystem(module, function))// + .collect(Collectors.toList()); + } + + /** + * Selects the build system for the module contained in the given {@link ModuleIteration} and executes the given + * function with it. + * + * @param module must not be {@literal null}. + * @param function must not be {@literal null}. + * @return + */ + private T doWithBuildSystem(ModuleIteration module, BiFunction function) { + return function.apply(buildSystems.getPluginFor(module.getProject(), () -> new IllegalStateException( + String.format("No build system plugin found for project %s!", module.getProject()))), module); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java new file mode 100644 index 0000000..133d56e --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 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.build; + +import org.springframework.data.release.deployment.DeploymentInformation; +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.UpdateInformation; +import org.springframework.plugin.core.Plugin; + +/** + * Plugin interface to back different build systems. + * + * @author Oliver Gierke + */ +interface BuildSystem extends Plugin { + + /** + * @param iteration + * @param train + * @param phase + * @throws Exception + */ + ModuleIteration updateProjectDescriptors(ModuleIteration iteration, UpdateInformation updateInformation); + + ModuleIteration prepareVersion(ModuleIteration module, Phase phase); + + ModuleIteration triggerDistributionBuild(ModuleIteration module); + + DeploymentInformation deploy(ModuleIteration module); +} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/GradleBuildSystem.java b/release-tools/src/main/java/org/springframework/data/release/build/GradleBuildSystem.java new file mode 100644 index 0000000..42ebb6c --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/GradleBuildSystem.java @@ -0,0 +1,171 @@ +/* + * 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.build; + +import static org.springframework.data.release.model.Projects.*; + +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.data.release.deployment.DeploymentInformation; +import org.springframework.data.release.deployment.DeploymentProperties; +import org.springframework.data.release.io.Workspace; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Phase; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.model.UpdateInformation; +import org.springframework.data.release.utils.Logger; +import org.springframework.stereotype.Component; + +/** + * Gradle specific operations. + * + * @author Oliver Gierke + */ +@Component +@Order(200) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) +class GradleBuildSystem implements BuildSystem { + + private static final String BUILD_GRADLE = "build.gradle"; + private static final String GRADLE_PROPERTIES = "gradle.properties"; + private static final String COMMONS_PROPERTY = "springDataCommonsVersion"; + private static final String BUILD_PROPERTY = "springDataBuildVersion"; + + private final Workspace workspace; + private final Logger logger; + private final DeploymentProperties properties; + + /** + * Updates all Gradle projects contained in the release. + * + * @param iteration + * @param phase + * @throws Exception + */ + public void updateProject(TrainIteration iteration, final Phase phase) throws Exception { + + UpdateInformation updateInformation = new UpdateInformation(iteration, phase); + + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { + updateProjectDescriptors(module, updateInformation); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#updateProjectDescriptors(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.UpdateInformation) + */ + @Override + public ModuleIteration updateProjectDescriptors(ModuleIteration iteration, UpdateInformation updateInformation) { + + Project project = iteration.getProject(); + Repository repository = new Repository(iteration.getIteration()); + ArtifactVersion commonsVersion = updateInformation.getIteration().getModuleVersion(COMMONS); + ArtifactVersion buildVersion = updateInformation.getIteration().getModuleVersion(BUILD); + + workspace.processFile(GRADLE_PROPERTIES, project, (line, number) -> { + + if (line.contains(COMMONS_PROPERTY)) { + + ArtifactVersion version = updateInformation.getPhase().equals(Phase.PREPARE) ? commonsVersion + : commonsVersion.getNextDevelopmentVersion(); + + logger.log(project, "Setting Spring Data Commons version in %s to %s.", GRADLE_PROPERTIES, version); + return String.format("%s=%s", COMMONS_PROPERTY, version); + } + + if (line.contains(BUILD_PROPERTY)) { + + ArtifactVersion version = updateInformation.getPhase().equals(Phase.PREPARE) ? buildVersion + : buildVersion.getNextDevelopmentVersion(); + + logger.log(project, "Setting Spring Data Build version in %s to %s.", GRADLE_PROPERTIES, version); + return String.format("%s=%s", BUILD_PROPERTY, version); + } + + return line; + }); + + workspace.processFile(BUILD_GRADLE, project, (line, number) -> { + + String snapshotUrl = repository.getSnapshotUrl(); + String releaseUrl = repository.getUrl(); + String message = "Switching to Spring repository %s"; + + switch (updateInformation.getPhase()) { + 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; + } + }); + + return iteration; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#prepareVersion(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.Phase) + */ + @Override + public ModuleIteration prepareVersion(ModuleIteration module, Phase phase) { + return module; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#triggerDistributionBuild(org.springframework.data.release.model.ModuleIteration) + */ + @Override + public ModuleIteration triggerDistributionBuild(ModuleIteration module) { + return module; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#deploy(org.springframework.data.release.model.ModuleIteration) + */ + @Override + public DeploymentInformation deploy(ModuleIteration module) { + return new DeploymentInformation(module, properties); + } + + /* + * (non-Javadoc) + * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) + */ + @Override + public boolean supports(Project project) { + return isGradleProject(project); + } + + /** + * 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/release-tools/src/main/java/org/springframework/data/release/maven/GroupId.java b/release-tools/src/main/java/org/springframework/data/release/build/GroupId.java similarity index 91% rename from release-tools/src/main/java/org/springframework/data/release/maven/GroupId.java rename to release-tools/src/main/java/org/springframework/data/release/build/GroupId.java index 1f47335..e9d4892 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/GroupId.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/GroupId.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; import lombok.Value; @@ -22,7 +22,7 @@ import lombok.Value; * * @author Oliver Gierke */ -@Value +@Value(staticConstructor = "of") class GroupId { private final String value; diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/Artifact.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java similarity index 83% rename from release-tools/src/main/java/org/springframework/data/release/maven/Artifact.java rename to release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java index e033592..0dde854 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/Artifact.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; import static org.springframework.data.release.model.Projects.*; + import lombok.EqualsAndHashCode; import lombok.Getter; @@ -24,25 +25,25 @@ import org.springframework.data.release.model.ModuleIteration; import org.springframework.util.Assert; /** - * Value object to represent a Maven {@link Artifact}. + * Value object to represent a Maven artifact. * * @author Oliver Gierke */ @EqualsAndHashCode -public class Artifact { +public class MavenArtifact { - private static final GroupId GROUP_ID = new GroupId("org.springframework.data"); + private static final GroupId GROUP_ID = GroupId.of("org.springframework.data"); private final ModuleIteration module; private final Repository repository; private final @Getter ArtifactVersion version; /** - * Creates a new {@link Artifact} for the given {@link ModuleIteration}. + * Creates a new {@link MavenArtifact} for the given {@link ModuleIteration}. * * @param module must not be {@literal null}. */ - public Artifact(ModuleIteration module) { + public MavenArtifact(ModuleIteration module) { Assert.notNull(module, "Module iteration must not be null!"); diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java new file mode 100644 index 0000000..6ef0a67 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java @@ -0,0 +1,304 @@ +/* + * 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.build; + +import static org.springframework.data.release.model.Projects.*; + +import lombok.RequiredArgsConstructor; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.data.release.deployment.DeploymentInformation; +import org.springframework.data.release.deployment.DeploymentProperties; +import org.springframework.data.release.io.Workspace; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Phase; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.model.UpdateInformation; +import org.springframework.data.release.utils.ExecutionUtils; +import org.springframework.data.release.utils.Logger; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.xmlbeam.ProjectionFactory; +import org.xmlbeam.io.XBFileIO; + +/** + * @author Oliver Gierke + */ +@Component +@Order(100) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) +class MavenBuildSystem implements BuildSystem { + + private static final String POM_XML = "pom.xml"; + + private final Workspace workspace; + private final ProjectionFactory projectionFactory; + private final Logger logger; + private final MavenRuntime mvn; + private final DeploymentProperties properties; + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#updateProjectDescriptors(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.TrainIteration, org.springframework.data.release.model.Phase) + */ + @Override + public ModuleIteration updateProjectDescriptors(ModuleIteration module, UpdateInformation information) { + + PomUpdater updater = new PomUpdater(logger, information, module.getProject()); + + if (updater.isBuildProject()) { + + updateBom(information); + updateParentPom(information); + + } else { + + execute(workspace.getFile(POM_XML, updater.getProject()), pom -> { + + updater.updateDependencyProperties(pom); + updater.updateParentVersion(pom); + updater.updateRepository(pom); + }); + } + + return module; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#triggerDistributionBuild(org.springframework.data.release.model.ModuleIteration) + */ + @Override + public ModuleIteration triggerDistributionBuild(ModuleIteration module) { + + Project project = module.getProject(); + + if (BUILD.equals(project)) { + return module; + } + + if (!isMavenProject(project)) { + logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); + return module; + } + + logger.log(project, "Triggering distribution build…"); + + ArtifactVersion version = ArtifactVersion.of(module); + + String profile = "-Pdistribute"; + + if (version.isMilestoneVersion()) { + profile = profile.concat(",milestone"); + } else if (version.isReleaseVersion()) { + profile = profile.concat(",release"); + } + + mvn.execute(project, "clean", "deploy", "-DskipTests", profile); + + logger.log(project, "Successfully finished distribution build!"); + + return module; + } + + private void updateBom(UpdateInformation updateInformation) { + + TrainIteration iteration = updateInformation.getIteration(); + + logger.log(BUILD, "Updating BOM pom.xml…"); + + execute(workspace.getFile("bom/pom.xml", BUILD), pom -> { + + for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { + + ArtifactVersion version = updateInformation.getProjectVersionToSet(module.getProject()); + + logger.log(BUILD, "%s", module); + + pom.setDependencyManagementVersion(new MavenArtifact(module).getArtifactId(), version); + + module.getProject().doWithAdditionalArtifacts( + additionalArtifact -> pom.setDependencyManagementVersion(additionalArtifact.getArtifactId(), version)); + } + }); + } + + private void updateParentPom(UpdateInformation information) { + + // Fix version of shared resources to to-be-released version. + execute(workspace.getFile("parent/pom.xml", BUILD), ParentPom.class, pom -> { + + logger.log(BUILD, "Setting shared resources version to %s.", information.getParentVersionToSet()); + pom.setSharedResourcesVersion(information.getParentVersionToSet()); + + logger.log(BUILD, "Setting releasetrain property to %s.", information.getReleaseTrainVersion()); + pom.setReleaseTrain(information.getReleaseTrainVersion()); + }); + } + + public boolean isMavenProject(ModuleIteration module) { + + Project project = module.getProject(); + + if (!isMavenProject(project)) { + logger.log(module, "No pom.xml file found, skipping project."); + return false; + } + + return true; + } + + /** + * Triggers building the distribution artifacts for all Maven projects of the given {@link Train}. + * + * @param iteration + * @throws Exception + */ + public void triggerDistributionBuild(TrainIteration iteration) throws Exception { + + ExecutionUtils.run(iteration, module -> { + + Project project = module.getProject(); + + if (BUILD.equals(project)) { + return; + } + + if (!isMavenProject(project)) { + logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); + return; + } + + logger.log(project, "Triggering distribution build…"); + + ArtifactVersion version = ArtifactVersion.of(module); + + String profile = "-Pdistribute"; + + if (version.isMilestoneVersion()) { + profile = profile.concat(",milestone"); + } else if (version.isReleaseVersion()) { + profile = profile.concat(",release"); + } + + mvn.execute(project, "clean", "deploy", "-DskipTests", profile); + + logger.log(project, "Successfully finished distribution build!"); + }); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#prepareVersion(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.Phase) + */ + @Override + public ModuleIteration prepareVersion(ModuleIteration module, Phase phase) { + + Project project = module.getProject(); + UpdateInformation information = new UpdateInformation(module.getTrainIteration(), phase); + + mvn.execute(project, "versions-set", + "-DnewVersion=".concat(information.getProjectVersionToSet(project).toString())); + + if (BUILD.equals(project)) { + + mvn.execute(project, "versions-set", // + "-DnewVersion=".concat(information.getReleaseTrainVersion()), // + "-DgroupId=org.springframework.data", // + "-DartifactId=spring-data-releasetrain"); + } + + return module; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.build.BuildSystem#deploy(org.springframework.data.release.model.ModuleIteration) + */ + @Override + public DeploymentInformation deploy(ModuleIteration module) { + + Assert.notNull(module, "Module must not be null!"); + + DeploymentInformation information = new DeploymentInformation(module, properties); + + List arguments = new ArrayList<>(); + arguments.add("clean"); + arguments.add("deploy"); + arguments.add("-Prelease"); + + arguments.add("-DskipTests"); + + arguments.add("-Dartifactory.server=".concat(properties.getServer().getUri())); + arguments.add("-Dartifactory.staging-repository=".concat(properties.getStagingRepository())); + arguments.add("-Dartifactory.username=".concat(properties.getUsername())); + arguments.add("-Dartifactory.password=".concat(properties.getPassword())); + arguments.add("-Dartifactory.build-name=\"".concat(information.getBuildName()).concat("\"")); + arguments.add("-Dartifactory.build-number=".concat(information.getBuildNumber())); + + mvn.execute(module.getProject(), arguments); + + return information; + } + + /* + * (non-Javadoc) + * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) + */ + @Override + public boolean supports(Project project) { + return isMavenProject(project); + } + + private void updateVersion(ModuleIteration iteration, File workingDirectory) { + + ArtifactVersion version = ArtifactVersion.of(iteration); + + mvn.execute(iteration.getProject(), "versions-set", "-DnewVersion=".concat(version.toString())); + } + + private boolean isMavenProject(Project project) { + return workspace.getFile(POM_XML, project).exists(); + } + + private void execute(File file, Consumer callback) { + execute(file, Pom.class, callback); + } + + private void execute(File file, Class type, Consumer callback) { + + XBFileIO io = projectionFactory.io().file(file); + + try { + + T pom = (T) io.read(type); + callback.accept(pom); + io.write(pom); + + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java new file mode 100644 index 0000000..7c7460b --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.build; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +/** + * Maven configuration properties. + * + * @author Oliver Gierke + */ +@Slf4j +@Data +@Component +@ConfigurationProperties(prefix = "maven") +class MavenProperties { + + private File mavenHome; + private File localRepository; + private Map plugins; + + /** + * Configures the local Maven repository location to use. In case the given folder does not already exists it's + * created. + * + * @param localRepository must not be {@literal null} or empty. + */ + public void setLocalRepository(String localRepository) { + + Assert.hasText(localRepository, "Local repository must not be null!"); + + log.info("Using {} as local Maven repository!", localRepository); + + this.localRepository = new File(localRepository.replace("~", System.getProperty("user.home"))); + + if (!this.localRepository.exists()) { + this.localRepository.mkdirs(); + } + } + + /** + * Returns the fully-qualified plugin goal for the given local one. + * + * @param goal must not be {@literal null} or empty. + * @return + */ + public String getFullyQualifiedPlugin(String goal) { + + Assert.hasText(goal, "Goal must not be null or empty!"); + return plugins.containsKey(goal) ? plugins.get(goal) : goal; + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java new file mode 100644 index 0000000..9127240 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java @@ -0,0 +1,110 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.build; + +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.DefaultInvoker; +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.invoker.Invoker; +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.io.OsOperations; +import org.springframework.data.release.io.Workspace; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.utils.Logger; +import org.springframework.stereotype.Component; + +/** + * @author Oliver Gierke + */ +@Slf4j +@Component +class MavenRuntime { + + private final Invoker invoker; + private final Workspace workspace; + private final OsOperations os; + private final Logger logger; + private final MavenProperties properties; + + /** + * Creates a new {@link MavenRuntime} for the given {@link Workspace} and Maven home. + * + * @param workspace must not be {@literal null}. + * @param os must not be {@literal null}. + * @param logger must not be {@literal null}. + * @param properties must not be {@literal null}. + */ + @Autowired + public MavenRuntime(Workspace workspace, OsOperations os, Logger logger, MavenProperties properties) { + + this.workspace = workspace; + this.os = os; + this.logger = logger; + this.properties = properties; + + this.invoker = new DefaultInvoker(); + this.invoker.setMavenHome(properties.getMavenHome()); + this.invoker.setOutputHandler(line -> log.info(line)); + this.invoker.setErrorHandler(line -> log.info(line)); + + File localRepository = properties.getLocalRepository(); + + if (localRepository != null) { + this.invoker.setLocalRepositoryDirectory(localRepository); + } + } + + public void execute(Project project, String... arguments) { + execute(project, Arrays.asList(arguments)); + } + + public void execute(Project project, List arguments) { + + DefaultInvocationRequest request = new DefaultInvocationRequest(); + request.setJavaHome(os.getJavaHome()); + request.setShellEnvironmentInherited(true); + request.setBaseDirectory(workspace.getProjectDirectory(project)); + + List goals = new ArrayList<>(); + goals.add(properties.getFullyQualifiedPlugin(arguments.get(0))); + goals.addAll(arguments.subList(1, arguments.size())); + + request.setGoals(goals); + + logger.log(project, "Executing mvn %s", goals.stream().collect(Collectors.joining(" "))); + + try { + + InvocationResult result = invoker.execute(request); + + if (result.getExitCode() != 0) { + throw new RuntimeException(result.getExecutionException()); + } + + } catch (MavenInvocationException o_O) { + throw new RuntimeException(o_O); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/ParentPom.java b/release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java similarity index 87% rename from release-tools/src/main/java/org/springframework/data/release/maven/ParentPom.java rename to release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java index c632da6..44d8024 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/ParentPom.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; import org.springframework.data.release.model.ArtifactVersion; import org.xmlbeam.annotation.XBValue; @@ -24,6 +24,9 @@ import org.xmlbeam.annotation.XBWrite; */ public interface ParentPom extends Pom { + @XBWrite("/project/properties/releasetrain") + void setReleaseTrain(@XBValue String releaseTrain); + @XBWrite("/project/profiles/profile[id=\"distribute\"]/dependencies/dependency/version") void setSharedResourcesVersion(@XBValue ArtifactVersion value); } diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/Pom.java b/release-tools/src/main/java/org/springframework/data/release/build/Pom.java similarity index 85% rename from release-tools/src/main/java/org/springframework/data/release/maven/Pom.java rename to release-tools/src/main/java/org/springframework/data/release/build/Pom.java index 0687c2e..63deca5 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/Pom.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/Pom.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; import org.springframework.data.release.model.ArtifactVersion; import org.xmlbeam.annotation.XBRead; @@ -26,13 +26,16 @@ import org.xmlbeam.annotation.XBWrite; public interface Pom { @XBRead("/project") - Artifact getArtifactId(); + Artifact getArtifact(); + + @XBRead("/project/version") + String getRawVersion(); @XBRead("/project/version") ArtifactVersion getVersion(); @XBWrite("/project/version") - void setVersion(String version); + void setVersion(ArtifactVersion version); @XBWrite("/project/parent/version") void setParentVersion(ArtifactVersion version); @@ -73,12 +76,20 @@ public interface Pom { public interface Artifact { @XBRead("child::groupId") - String getGroupId(); + GroupId getGroupId(); @XBRead("child::artifactId") String getArtifactId(); @XBRead("child::version") String getVersion(); + + default String getArtifactPath() { + return "/".concat(getGroupId().asPath()).concat("/").concat(getArtifactId()); + } + + default String getPath() { + return getArtifactPath().concat(getVersion()); + } } } diff --git a/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java b/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java new file mode 100644 index 0000000..56e2e04 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java @@ -0,0 +1,111 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.build; + +import static org.springframework.data.release.model.Phase.*; +import static org.springframework.data.release.model.Projects.*; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.UpdateInformation; +import org.springframework.data.release.utils.Logger; +import org.springframework.util.Assert; + +/** + * @author Oliver Gierke + */ +@RequiredArgsConstructor +class PomUpdater { + + private final Logger logger; + private final UpdateInformation information; + private final @Getter Project project; + + public boolean isBuildProject() { + return BUILD.equals(project); + } + + public void updateArtifactVersion(Pom pom) { + + ArtifactVersion version = information.getProjectVersionToSet(project); + logger.log(project, "Updated project version to %s.", version); + pom.setVersion(version); + } + + public void updateDependencyProperties(Pom pom) { + + project.getDependencies().forEach(dependency -> { + + String dependencyProperty = dependency.getDependencyProperty(); + + if (pom.getProperty(dependencyProperty) == null) { + return; + } + + ArtifactVersion version = information.getProjectVersionToSet(dependency); + + logger.log(project, "Updating %s dependency version property %s to %s.", dependency.getFullName(), + dependencyProperty, version); + pom.setProperty(dependencyProperty, version); + }); + } + + /** + * Updates the version of the parent project in the given {@link Pom}. + * + * @param pom must not be {@literal null}. + */ + public void updateParentVersion(Pom pom) { + + Assert.notNull(pom, "Pom must not be null!"); + + ArtifactVersion version = information.getParentVersionToSet(); + + logger.log(project, "Updating Spring Data Build Parent version to %s.", version); + pom.setParentVersion(version); + } + + /** + * Updates the repository section in the given {@link Pom}. + * + * @param pom must not be {@literal null}. + */ + public void updateRepository(Pom pom) { + + Assert.notNull(pom, "Pom must not be null!"); + + String message = "Switching to Spring repository %s (%s)."; + Repository repository = information.getRepository(); + + if (PREPARE.equals(information.getPhase())) { + + 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/release-tools/src/main/java/org/springframework/data/release/maven/Repository.java b/release-tools/src/main/java/org/springframework/data/release/build/Repository.java similarity index 68% rename from release-tools/src/main/java/org/springframework/data/release/maven/Repository.java rename to release-tools/src/main/java/org/springframework/data/release/build/Repository.java index d8a8e93..5119163 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/Repository.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/Repository.java @@ -13,35 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; + +import lombok.Value; import org.springframework.data.release.model.Iteration; +import org.springframework.util.Assert; /** * @author Oliver Gierke */ + +@Value public class Repository { + private static final String ID_BASE = "spring-libs-"; private static final String BASE = "https://repo.spring.io/libs-"; - private final String id; - private final String url; + String id, url; public Repository(Iteration iteration) { - this.id = iteration.isPublic() ? "spring-libs-release" : "spring-libs-milestone"; - this.url = iteration.isPublic() ? BASE.concat("release") : BASE.concat("milestone"); - } - public String getId() { - return id; + Assert.notNull(iteration, "Iteration must not be null!"); + + this.id = ID_BASE.concat(iteration.isPublic() ? "release" : "milestone"); + this.url = BASE.concat(iteration.isPublic() ? "release" : "milestone"); } public String getSnapshotId() { - return "spring-libs-snapshot"; - } - - public String getUrl() { - return url; + return ID_BASE.concat("snapshot"); } public String getSnapshotUrl() { diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java b/release-tools/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java index f2c0ebd..6d4742a 100644 --- a/release-tools/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java +++ b/release-tools/src/main/java/org/springframework/data/release/cli/IssueTrackerCommands.java @@ -45,8 +45,9 @@ public class IssueTrackerCommands implements CommandMarker { private final Credentials credentials; /** - * @param tracker - * @param environment + * @param tracker must not be {@literal null}. + * @param jira must not be {@literal null}. + * @param environment must not be {@literal null}. */ @Autowired public IssueTrackerCommands(PluginRegistry tracker, JiraConnector jira, diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java b/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java index a8b4268..952dd07 100644 --- a/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,35 +19,39 @@ import static org.springframework.data.release.model.Projects.*; import lombok.RequiredArgsConstructor; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.release.docs.DocumentationOperations; +import org.springframework.data.release.CliComponent; +import org.springframework.data.release.build.BuildOperations; +import org.springframework.data.release.deployment.DeploymentInformation; +import org.springframework.data.release.deployment.DeploymentOperations; import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.git.VersionTags; -import org.springframework.data.release.gradle.GradleOperations; -import org.springframework.data.release.maven.MavenOperations; import org.springframework.data.release.misc.ReleaseOperations; 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.ReleaseTrains; import org.springframework.data.release.model.Train; import org.springframework.data.release.model.TrainIteration; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; -import org.springframework.stereotype.Component; +import org.springframework.util.Assert; /** * @author Oliver Gierke */ -@Component +@CliComponent @RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class ReleaseCommands implements CommandMarker { - private final MavenOperations maven; - private final GradleOperations gradle; private final GitOperations git; private final ReleaseOperations misc; - private final DocumentationOperations docs; + private final DeploymentOperations deployment; + private final BuildOperations build; @CliCommand("release predict") public String predictTrainAndIteration() throws Exception { @@ -64,20 +68,6 @@ public class ReleaseCommands implements CommandMarker { findFirst().map(Train::getName).orElse(null); } - /** - * Triggers the distribution of release artifacts for all projects. - * - * @param trainName - * @param iterationName - * @throws Exception - */ - @CliCommand("release distribute") - public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - - git.checkout(iteration); - maven.triggerDistributionBuild(iteration); - } - /** * Prepares the release of the given iteration of the given train. * @@ -92,22 +82,73 @@ public class ReleaseCommands implements CommandMarker { misc.prepareChangelogs(iteration); misc.updateResources(iteration); - docs.updateDockbookIncludes(iteration); - maven.updatePom(iteration, Phase.PREPARE); - gradle.updateProject(iteration, Phase.PREPARE); + build.updateProjectDescriptors(iteration, Phase.PREPARE); - git.commit(iteration, "Prepare %s.", null); + git.commit(iteration, "Prepare %s."); } + @CliCommand(value = "release build") + public void buildRelease(@CliOption(key = "", mandatory = true) TrainIteration iteration, // + @CliOption(key = "project", mandatory = false) String projectName) throws Exception { + + if (projectName != null) { + + Project project = Projects.byName(projectName); + ModuleIteration module = iteration.getModule(project); + + DeploymentInformation information = build.performRelease(module); + deployment.promote(information); + + } else { + + List deploymentInformation = build.performRelease(iteration); + git.commit(iteration, "Release version %s."); + deploymentInformation.forEach(deployment::promote); + + build.prepareVersions(iteration, Phase.CLEANUP); + git.commit(iteration, "Prepare next development iteration."); + } + } + + /** + * Concludes the release of the given {@link TrainIteration}. + * + * @param iteration + * @throws Exception + */ @CliCommand(value = "release conclude") public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { + Assert.notNull(iteration, "Train iteration must not be null!"); + + // Tag release git.tagRelease(iteration); - maven.updatePom(iteration, Phase.CLEANUP); - gradle.updateProject(iteration, Phase.CLEANUP); + // Prepare master branch + build.updateProjectDescriptors(iteration, Phase.CLEANUP); + git.commit(iteration, "After release cleanups."); - git.commit(iteration, "After release cleanups.", null); + // Prepare maintenance branches + git.checkout(iteration); + git.createMaintenanceBranches(iteration); + + build.updateProjectDescriptors(iteration, Phase.MAINTENANCE); + build.prepareVersions(iteration, Phase.MAINTENANCE); + git.commit(iteration, "Prepare next development iteration."); + } + + /** + * Triggers the distribution of release artifacts for all projects. + * + * @param trainName + * @param iterationName + * @throws Exception + */ + @CliCommand("release distribute") + public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { + + git.checkout(iteration); + build.distributeResources(iteration); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java b/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java index 6850c2a..5856ecd 100644 --- a/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java +++ b/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java @@ -70,6 +70,6 @@ public class TrainIterationConverter implements Converter { } } - return false; + return true; } } diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java b/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java new file mode 100644 index 0000000..5d7cffa --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015-2016 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.deployment; + +import lombok.RequiredArgsConstructor; +import lombok.Value; + +import java.io.IOException; +import java.net.URI; + +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.utils.Logger; +import org.springframework.util.Assert; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestOperations; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * A client to interact with Artifactory. + * + * @author Oliver Gierke + */ +@RequiredArgsConstructor +class ArtifactoryClient { + + private final RestOperations template; + private final Logger logger; + private final DeploymentProperties properties; + + /** + * Triggers the promotion of the artifacts identified by the given {@link DeploymentInformation}. + * + * @param information must not be {@literal null}. + */ + public void promote(DeploymentInformation information) { + + Assert.notNull(information, "DeploymentInformation must not be null!"); + + ModuleIteration module = information.getModule(); + URI uri = properties.getServer().getPromotionResource(information); + + logger.log(module, "Promoting %s %s from %s to %s.", information.getBuildName(), information.getBuildNumber(), + properties.getStagingRepository(), information.getTargetRepository()); + + try { + template.postForEntity(uri, new PromotionRequest(information.getTargetRepository()), String.class); + } catch (HttpClientErrorException o_O) { + handle(o_O, module); + } + } + + private void handle(HttpClientErrorException o_O, ModuleIteration module) { + + try { + + logger.log(module, "Promotion failed!"); + + Errors errors = new ObjectMapper().readValue(o_O.getResponseBodyAsByteArray(), Errors.class); + errors.getErrors().forEach(error -> logger.log(module, error)); + errors.getMessages().forEach(message -> logger.log(module, message)); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void deleteArtifacts(DeploymentInformation information) { + template.delete(properties.getServer().getDeleteBuildResource(information)); + } + + @Value + private static class PromotionRequest { + String targetRepo; + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java new file mode 100644 index 0000000..634a471 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015-2016 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.deployment; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import java.io.IOException; +import java.util.Arrays; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.release.utils.Logger; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.RestTemplate; + +/** + * Configuration to set up deployment components. + * + * @author Oliver Gierke + */ +@Configuration +class DeploymentConfiguration { + + @Autowired DeploymentProperties properties; + + @Bean + public ArtifactoryClient client(Logger logger) { + return new ArtifactoryClient(artifactoryRestTemplate(), logger, properties); + } + + @Bean + public RestTemplate artifactoryRestTemplate() { + + RestTemplate template = new RestTemplate(); + template.setInterceptors(Arrays.asList(new AuthenticatingClientHttpRequestInterceptor(properties.getApiKey()))); + + return template; + } + + @RequiredArgsConstructor + private static class AuthenticatingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + private final @NonNull String apiKey; + + /* + * (non-Javadoc) + * @see org.springframework.http.client.ClientHttpRequestInterceptor#intercept(org.springframework.http.HttpRequest, byte[], org.springframework.http.client.ClientHttpRequestExecution) + */ + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) + throws IOException { + + request.getHeaders().add("X-Api-Key", apiKey); + + return execution.execute(request, body); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java new file mode 100644 index 0000000..4d0d380 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015-2016 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.deployment; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.web.util.UriTemplate; + +/** + * Information about a deployment. + * + * @author Oliver Gierke + */ +@RequiredArgsConstructor +public class DeploymentInformation { + + private static UriTemplate REPOSITORY_TEMPLATE = new UriTemplate( + "artifactory::default::{server};build.number={buildNumber};build.name={buildName}"); + + private final @Getter @NonNull ModuleIteration module; + private final @NonNull DeploymentProperties properties; + + /** + * Returns a unique build number for this particular deployment. + */ + private final @Getter String buildNumber = String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)); + + /** + * Returns the name of the build. + * + * @return + */ + public String getBuildName() { + return module.getProject().getFullName().concat(" - Release"); + } + + /** + * Returns the name of the repository to deploy to. + * + * @return + */ + public String getTargetRepository() { + return properties.getRepositoryPrefix() + .concat(module.getIteration().isPublic() ? "libs-release-local" : "libs-milestone-local"); + } + + /** + * Returns the full URL to be used as deployment target. + * + * @return + */ + public String getDeploymentTargetUrl() { + + Map parameters = new HashMap<>(); + parameters.put("server", properties.getStagingRepositoryUrl()); + parameters.putAll(getBuildInfoParameters()); + + return REPOSITORY_TEMPLATE.expand(parameters).toString(); + } + + /** + * Returns a {@link Map} to expand a URI template to access the build information. + * + * @return + */ + public Map getBuildInfoParameters() { + + Map parameters = new HashMap<>(); + parameters.put("buildNumber", buildNumber); + parameters.put("buildName", getBuildName()); + + return parameters; + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java new file mode 100644 index 0000000..eab57cd --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2016 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.deployment; + +import lombok.RequiredArgsConstructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +/** + * Deployment functionality. + * + * @author Oliver Gierke + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) +public class DeploymentOperations { + + private final ArtifactoryClient client; + + /** + * Promotes the artifacts identified by the given {@link DeploymentInformation}. + * + * @param information must not be {@literal null}. + */ + public void promote(DeploymentInformation information) { + + Assert.notNull(information, "DeploymentInformation must not be null!"); + + client.promote(information); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java new file mode 100644 index 0000000..fad0f0a --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java @@ -0,0 +1,98 @@ +/* + * Copyright 2015-2016 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.deployment; + +import lombok.Data; + +import java.net.URI; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.web.util.UriTemplate; + +/** + * @author Oliver Gierke + */ +@Data +@Component +@ConfigurationProperties(prefix = "deployment") +public class DeploymentProperties { + + /** + * The Artifactory host. + */ + private Server server; + + /** + * The deployer's username. + */ + private String username; + + private String apiKey; + + /** + * The deployer's password. + */ + private String password; + + /** + * The repository to deploy the artifacts to. + */ + private String stagingRepository; + + private String repositoryPrefix = ""; + + public String getStagingRepository() { + return repositoryPrefix.concat(stagingRepository); + } + + /** + * Returns the URI of the staging repository. + * + * @return + */ + public String getStagingRepositoryUrl() { + return server.getUri().toString().concat("/").concat(stagingRepository); + } + + @Data + public static class Server { + + private static final String PROMOTION_RESOURCE = "/api/build/promote/{buildName}/{buildNumber}"; + private static final String DELETE_BUILD_RESOURCE = "/api/build/{buildName}?buildNumbers={buildNumber}&artifacts=1"; + + private String uri; + + /** + * Returns the URI to the resource that a promotion can be triggered at. + * + * @param information must not be {@literal null}. + * @return + */ + public URI getPromotionResource(DeploymentInformation information) { + + Assert.notNull(information, "DeploymentInformation must not be null!"); + + return new UriTemplate(uri.concat(PROMOTION_RESOURCE)).expand(information.getBuildInfoParameters()); + } + + public URI getDeleteBuildResource(DeploymentInformation information) { + + return new UriTemplate(uri.concat(DELETE_BUILD_RESOURCE)).expand(information.getBuildInfoParameters()); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java b/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java new file mode 100644 index 0000000..92f0d3d --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.deployment; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Oliver Gierke + */ +@Data +public class Errors { + + private List errors = new ArrayList<>(); + private List messages = new ArrayList<>(); + + public List getErrors(Errors this) { + return errors; + } + + @Data + static class Error { + + private String message; + private int status; + + public String toString() { + return String.format("%s - %s", status, message); + } + } + + @Data + static class Message { + + private String level, message; + + public String toString() { + return String.format("%s - %s", level, message); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java b/release-tools/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java index 2ac390d..9025d19 100644 --- a/release-tools/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/docs/DocumentationOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -16,6 +16,7 @@ package org.springframework.data.release.docs; import static org.springframework.data.release.model.Projects.*; + import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -24,7 +25,6 @@ import org.springframework.data.release.git.GitProject; import org.springframework.data.release.git.Tag; import org.springframework.data.release.git.VersionTags; import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.io.Workspace.LineCallback; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.TrainIteration; @@ -34,7 +34,7 @@ import org.springframework.stereotype.Component; * @author Oliver Gierke */ @Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) public class DocumentationOperations { private static final String INDEX_LOCATION = "/src/docbkx/index.xml"; @@ -61,16 +61,12 @@ public class DocumentationOperations { continue; } - workspace.processFile(INDEX_LOCATION, project, new LineCallback() { + workspace.processFile(INDEX_LOCATION, project, (line, number) -> { - @Override - public String doWith(String line, long number) { + boolean isInclude = line.contains("xi:include"); + boolean containsGitRepo = line.contains(gitProject.getRepositoryName()); - boolean isInclude = line.contains("xi:include"); - boolean containsGitRepo = line.contains(gitProject.getRepositoryName()); - - return isInclude && containsGitRepo ? line.replace(previousTag.toString(), newTag.toString()) : line; - } + return isInclude && containsGitRepo ? line.replace(previousTag.toString(), newTag.toString()) : line; }); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Branch.java b/release-tools/src/main/java/org/springframework/data/release/git/Branch.java index b71988a..ec42c29 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/Branch.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/Branch.java @@ -15,11 +15,13 @@ */ package org.springframework.data.release.git; +import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import org.springframework.data.release.model.IterationVersion; import org.springframework.data.release.model.Version; +import org.springframework.data.release.model.VersionAware; import org.springframework.util.Assert; /** @@ -27,8 +29,8 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -@RequiredArgsConstructor @EqualsAndHashCode +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) class Branch { public static final Branch MASTER = new Branch("master"); @@ -43,17 +45,30 @@ class Branch { */ public static Branch from(IterationVersion iterationVersion) { - Assert.notNull(iterationVersion, "Iteration versoin must not be null!"); + Assert.notNull(iterationVersion, "Iteration version must not be null!"); - Version version = iterationVersion.getVersion(); - - if (iterationVersion.getIteration().isServiceIteration()) { - return new Branch(version.toString().concat(".x")); + if (iterationVersion.isServiceIteration()) { + return from((VersionAware) iterationVersion); } return MASTER; } + public static Branch from(VersionAware versioned) { + return from(versioned.getVersion()); + } + + private static Branch from(Version version) { + return from(version.toString().concat(".x")); + } + + private static Branch from(String name) { + + int slashIndex = name.lastIndexOf('/'); + + return new Branch(slashIndex != -1 ? name.substring(slashIndex + 1) : name); + } + /* * (non-Javadoc) * @see java.lang.Object#toString() diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Commit.java b/release-tools/src/main/java/org/springframework/data/release/git/Commit.java index d7bfd98..b063881 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/Commit.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/Commit.java @@ -18,6 +18,8 @@ package org.springframework.data.release.git; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; +import java.util.Optional; + import org.springframework.data.release.jira.Ticket; /** @@ -29,7 +31,7 @@ public class Commit { private final Ticket ticket; private final String summary; - private final String details; + private final Optional details; /* * (non-Javadoc) @@ -42,10 +44,10 @@ public class Commit { builder.append(ticket.getId()).append(" - ").append(summary).append("\n"); - if (details != null) { + details.ifPresent(it -> { builder.append("\n"); - builder.append(details).append("\n"); - } + builder.append(it).append("\n"); + }); return builder.toString(); } diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java b/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java index 62f4cfb..bb25caf 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java @@ -37,7 +37,12 @@ public class GitCommands implements CommandMarker { private final GitOperations git; - @CliCommand("git checkout") + @CliCommand("git co train") + public void checkout(@CliOption(key = "", mandatory = true) Train train) throws Exception { + git.checkout(train); + } + + @CliCommand("git co") public void checkout(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { git.checkout(iteration); } diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java b/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java index 9db5f6b..f903188 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ import lombok.RequiredArgsConstructor; import java.io.File; import java.io.IOException; +import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import org.eclipse.jgit.api.CheckoutCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; @@ -86,14 +86,49 @@ public class GitOperations { logger.log(module, "git reset --hard origin/%s", branch); - git.reset().// - setMode(ResetType.HARD).// - setRef("origin/".concat(branch.toString())).// - call(); + reset(git, branch.toString()); } }); } + /** + * Checks out all projects of the given {@link TrainIteration}. + * + * @param iteration + * @throws Exception + */ + public void checkout(Train train) { + + update(train); + + ExecutionUtils.run(train, module -> { + + try (Git git = new Git(getRepository(module.getProject()))) { + + Branch branch = Branch.from(module); + CheckoutCommand command = git.checkout().setName(branch.toString()); + + if (!branchExists(module.getProject(), branch)) { + + logger.log(module.getProject(), "git checkout -b %s --track origin/%s", branch, branch); + command.setCreateBranch(true).// + setStartPoint("origin/".concat(branch.toString())).// + call(); + + } else { + + logger.log(module.getProject(), "git checkout %s", branch); + command.call(); + } + + logger.log(module.getProject(), "git reset --hard origin/%s", branch); + reset(git, branch.toString()); + } + }); + + logger.log(train, "Successfully checked out projects."); + } + /** * Checks out all projects of the given {@link TrainIteration}. * @@ -120,7 +155,7 @@ public class GitOperations { logger.log(module, "git checkout %s", tag); - git.checkout().setStartPoint(tag.toString()); + git.checkout().setStartPoint(tag.toString()).call(); } }); @@ -197,12 +232,13 @@ public class GitOperations { if (workspace.hasProjectDirectory(project)) { logger.log(project, "Found existing repository %s. Obtaining latest changes…", repositoryName); - logger.log(project, "git checkout master && git reset --hard && git fetch --tags && git pull origin master"); + logger.log(project, + "git checkout master && git reset --hard origin/master && git fetch --tags && git pull origin master"); checkout(project, Branch.MASTER); - git.reset().setMode(ResetType.HARD).call(); - git.fetch().setTagOpt(TagOpt.FETCH_TAGS); + reset(git, "master"); + git.fetch().setTagOpt(TagOpt.FETCH_TAGS).call(); git.pull().call(); } else { @@ -256,6 +292,18 @@ public class GitOperations { }); } + /** + * 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. + * @throws Exception + */ + public void commit(TrainIteration iteration, String summary) throws Exception { + commit(iteration, summary, Optional.empty()); + } + /** * 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. @@ -265,7 +313,7 @@ public class GitOperations { * @param details can be {@literal null} or empty. * @throws Exception */ - public void commit(TrainIteration iteration, String summary, String details) throws Exception { + public void commit(TrainIteration iteration, String summary, Optional details) throws Exception { Assert.notNull(iteration, "Train iteration must not be null!"); Assert.hasText(summary, "Summary must not be null or empty!"); @@ -273,19 +321,22 @@ public class GitOperations { ExecutionUtils.run(iteration, module -> commit(module, expandSummary(summary, module, iteration), details)); } - private static String expandSummary(String summary, ModuleIteration module, TrainIteration iteration) { - - if (!summary.contains("%s")) { - return summary; - } - - return String.format(summary, - ArtifactVersion.of(module).toString().concat(String.format(" (%s)", iteration.toString()))); + /** + * Commits the given files for the given {@link ModuleIteration} using the given summary for the commit message. If no + * files are given, all pending changes are committed. + * + * @param module must not be {@literal null}. + * @param summary must not be {@literal null} or empty. + * @param files can be empty. + * @throws Exception + */ + public void commit(ModuleIteration module, String summary, File... files) throws Exception { + commit(module, summary, Optional.empty(), files); } /** * 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. + * message. If no files are given, all pending changes are committed. * * @param module must not be {@literal null}. * @param summary must not be {@literal null} or empty. @@ -293,7 +344,7 @@ public class GitOperations { * @param files can be empty. * @throws Exception */ - public void commit(ModuleIteration module, String summary, String details, File... files) throws Exception { + public void commit(ModuleIteration module, String summary, Optional details, File... files) throws Exception { Assert.notNull(module, "Module iteration must not be null!"); Assert.hasText(summary, "Summary must not be null or empty!"); @@ -318,6 +369,55 @@ public class GitOperations { } } + public void checkout(Project project, Branch branch) throws Exception { + + try (Git git = new Git(getRepository(project))) { + + Ref ref = git.getRepository().getRef(branch.toString()); + CheckoutCommand checkout = git.checkout().setName(branch.toString()); + + if (ref == null) { + + checkout.setCreateBranch(true).// + setUpstreamMode(SetupUpstreamMode.TRACK).// + setStartPoint("origin/".concat(branch.toString())); + } + + checkout.call(); + } + } + + public void createMaintenanceBranches(TrainIteration iteration) throws Exception { + + checkout(iteration); + + ExecutionUtils.run(iteration, module -> { + Branch branch = createMaintenanceBranch(module); + checkout(module.getProject(), branch); + }); + } + + private Branch createMaintenanceBranch(ModuleIteration module) throws Exception { + + try (Git git = new Git(getRepository(module.getProject()))) { + + Branch branch = Branch.from(module); + git.branchCreate().setName(branch.toString()).call(); + + return branch; + } + } + + /** + * Returns the {@link ObjectId} of the commit that is considered the release commit. It is identified by the summary + * starting with the release ticket identifier, followed by a dash separated by spaces and the key word + * {@code Release}. To prevent skimming through the entire Git history, we expect such a commit to be found within the + * 50 most recent commits. + * + * @param module + * @return + * @throws Exception + */ private ObjectId getReleaseHash(ModuleIteration module) throws Exception { Project project = module.getProject(); @@ -329,9 +429,7 @@ public class GitOperations { for (RevCommit commit : git.log().setMaxCount(50).call()) { - String summary = commit.getShortMessage(); - - if (summary.startsWith(trigger)) { + if (commit.getShortMessage().startsWith(trigger)) { return commit.getId(); } } @@ -351,36 +449,17 @@ public class GitOperations { */ private Tag findTagFor(Project project, ArtifactVersion version) { - return StreamSupport.stream(getTags(project).spliterator(), false).// + return getTags(project).stream().// filter(tag -> tag.toArtifactVersion().map(it -> it.equals(version)).orElse(false)).// findFirst().orElseThrow(() -> new IllegalArgumentException( String.format("No tag found for version %s of project %s!", version, project))); } - public void checkout(Project project, Branch branch) throws Exception { - - try (Git git = new Git(getRepository(project))) { - - Ref ref = git.getRepository().getRef(branch.toString()); - CheckoutCommand checkout = git.checkout().setName(branch.toString()); - - if (ref == null) { - - checkout.setCreateBranch(true).// - setUpstreamMode(SetupUpstreamMode.TRACK).// - setStartPoint("origin/".concat(branch.toString())); - } - - checkout.call(); - - } - } - private Repository getRepository(Project project) throws IOException { return FileRepositoryBuilder.create(workspace.getFile(".git", project)); } - public void clone(Project project) throws Exception { + private void clone(Project project) throws Exception { Git git = Git.cloneRepository().// setURI(getGitProject(project).getProjectUri()).// @@ -390,4 +469,23 @@ public class GitOperations { git.checkout().setName(Branch.MASTER.toString()).// call(); } + + private boolean branchExists(Project project, Branch branch) { + + try (Git git = new Git(getRepository(project))) { + + return git.getRepository().getRef(branch.toString()) != null; + + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + } + + private static void reset(Git git, String name) throws Exception { + git.reset().setMode(ResetType.HARD).setRef("origin/".concat(name)).call(); + } + + private static String expandSummary(String summary, ModuleIteration module, TrainIteration iteration) { + return summary.contains("%s") ? String.format(summary, module.getMediumVersionString()) : summary; + } } diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java b/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java index fa73c59..c35f460 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java @@ -57,4 +57,8 @@ public class GitProperties { public CredentialsProvider getCredentials() { return new UsernamePasswordCredentialsProvider(username, password); } + + public String getAuthenticationHeader() { + return username.concat(":").concat(password); + } } diff --git a/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java b/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java index 635e3a0..2b77eb3 100644 --- a/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java +++ b/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java @@ -21,6 +21,7 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; +import org.springframework.data.release.Streamable; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.ModuleIteration; import org.springframework.util.Assert; @@ -31,7 +32,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke */ @EqualsAndHashCode -public class VersionTags implements Iterable { +public class VersionTags implements Streamable { private final List tags; diff --git a/release-tools/src/main/java/org/springframework/data/release/gradle/GradleOperations.java b/release-tools/src/main/java/org/springframework/data/release/gradle/GradleOperations.java deleted file mode 100644 index 4198b87..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/gradle/GradleOperations.java +++ /dev/null @@ -1,139 +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.gradle; - -import static org.springframework.data.release.model.Projects.*; - -import lombok.RequiredArgsConstructor; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.io.Workspace.LineCallback; -import org.springframework.data.release.maven.Repository; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.stereotype.Component; - -/** - * Gradle specific operations. - * - * @author Oliver Gierke - */ -@Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) -public class GradleOperations { - - private static final String BUILD_GRADLE = "build.gradle"; - private static final String GRADLE_PROPERTIES = "gradle.properties"; - private static final String COMMONS_PROPERTY = "springDataCommonsVersion"; - private static final String BUILD_PROPERTY = "springDataBuildVersion"; - - private final Workspace workspace; - private final Logger logger; - - /** - * Updates all Gradle projects contained in the release. - * - * @param iteration - * @param phase - * @throws Exception - */ - public void updateProject(TrainIteration iteration, final Phase phase) throws Exception { - - final Repository repository = new Repository(iteration.getIteration()); - final ArtifactVersion commonsVersion = iteration.getModuleVersion(COMMONS); - final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD); - - for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { - - final Project project = module.getProject(); - - if (!isGradleProject(project)) { - continue; - } - - workspace.processFile(GRADLE_PROPERTIES, project, new LineCallback() { - - /* - * (non-Javadoc) - * @see org.springframework.data.release.io.Workspace.LineCallback#doWith(java.lang.String, long) - */ - @Override - public String doWith(String line, long number) { - - if (line.contains(COMMONS_PROPERTY)) { - - ArtifactVersion version = phase.equals(Phase.PREPARE) ? commonsVersion - : commonsVersion.getNextDevelopmentVersion(); - - logger.log(project, "Setting Spring Data Commons version in %s to %s.", GRADLE_PROPERTIES, version); - return String.format("%s=%s", COMMONS_PROPERTY, version); - } - - if (line.contains(BUILD_PROPERTY)) { - - ArtifactVersion version = phase.equals(Phase.PREPARE) ? buildVersion - : buildVersion.getNextDevelopmentVersion(); - - logger.log(project, "Setting Spring Data Build version in %s to %s.", GRADLE_PROPERTIES, version); - return String.format("%s=%s", BUILD_PROPERTY, version); - } - - return line; - } - }); - - workspace.processFile(BUILD_GRADLE, project, new LineCallback() { - - /* - * (non-Javadoc) - * @see org.springframework.data.release.io.Workspace.LineCallback#doWith(java.lang.String, long) - */ - @Override - public String doWith(String line, long number) { - - String snapshotUrl = repository.getSnapshotUrl(); - String releaseUrl = repository.getUrl(); - String message = "Switching to Spring repository %s"; - - switch (phase) { - case CLEANUP: - logger.log(project, message, snapshotUrl); - return line.contains(releaseUrl) ? line.replace(releaseUrl, snapshotUrl) : line; - case PREPARE: - default: - logger.log(project, message, releaseUrl); - return line.contains(snapshotUrl) ? line.replace(snapshotUrl, releaseUrl) : line; - } - } - }); - } - } - - /** - * Returns whether the given project is a Gradle project (checks for the presence of a build.gradle file). - * - * @param project - * @return - */ - private boolean isGradleProject(Project project) { - return workspace.getFile(BUILD_GRADLE, project).exists(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java b/release-tools/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java index 4eed8fa..674c807 100644 --- a/release-tools/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/io/CommonsExecOsCommandOperations.java @@ -15,8 +15,6 @@ */ package org.springframework.data.release.io; -import lombok.RequiredArgsConstructor; - import java.io.File; import java.io.IOException; import java.io.StringWriter; @@ -35,22 +33,35 @@ import org.springframework.data.release.utils.Logger; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; +import org.springframework.util.Assert; /** - * Implementation of {@link OsCommandOperations} interface. + * Implementation of {@link OsOperations} interface. * * @author Stefan Schmidt * @author Oliver Gierke * @since 1.2.0 */ @Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) -class CommonsExecOsCommandOperations implements OsCommandOperations { +class CommonsExecOsCommandOperations implements OsOperations { private static final Map ENVIRONMENT = new HashMap<>(); private final Workspace workspace; private final Logger logger; + private final File javaHome; + + @Autowired + public CommonsExecOsCommandOperations(Workspace workspace, Logger logger, IoProperties properties) throws Exception { + + this.workspace = workspace; + this.logger = logger; + this.javaHome = detectJavaHome(properties); + + Assert.isTrue(javaHome.exists(), String.format("Java home %s does not exist!", javaHome.getAbsolutePath())); + + ENVIRONMENT.put("JAVA_HOME", javaHome.getAbsolutePath()); + } /* * (non-Javadoc) @@ -133,20 +144,24 @@ class CommonsExecOsCommandOperations implements OsCommandOperations { new CommandResult(resultHandler.getExitValue(), writer.toString(), resultHandler.getException())); } - /** - * Adds {@code JAVA_HOME} to the ENVIRONMENT variables looking up the path to a Java 7. - * - * @throws Exception - */ - // @PostConstruct - public void initialize() throws Exception { + public File getJavaHome() { + return javaHome; + } - String javaHome = executeCommand("/usr/libexec/java_home -F -v 1.8 -a x86_64 -d64").get().getOutput(); + private File detectJavaHome(IoProperties properties) throws Exception { - if (javaHome.endsWith("\n")) { - javaHome = javaHome.substring(0, javaHome.length() - 1); + File javaHome = properties.getJavaHome(); + + if (javaHome != null) { + return javaHome; } - ENVIRONMENT.put("JAVA_HOME", javaHome); + String javaHomePath = executeCommand("/usr/libexec/java_home -F -v 1.8 -a x86_64 -d64").get().getOutput(); + + if (javaHomePath.endsWith("\n")) { + javaHomePath = javaHomePath.substring(0, javaHomePath.length() - 1); + } + + return new File(javaHomePath); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java b/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java index aca10cd..fdb5a71 100644 --- a/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java +++ b/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java @@ -30,17 +30,19 @@ import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "io") -public class IoProperties { +class IoProperties { - private String workDir; + private File workDir, javaHome; public void setWorkDir(String workDir) { - this.workDir = workDir.replace("~", System.getProperty("user.home")); log.info(String.format("Using %s as working directory!", workDir)); + this.workDir = new File(workDir.replace("~", System.getProperty("user.home"))); } - public File getWorkDir() { - return new File(workDir); + public void setJavaHome(String javaHome) { + + log.info(String.format("Using %s as Java home!", javaHome)); + this.workDir = new File(javaHome.replace("~", System.getProperty("user.home"))); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/io/OsCommandOperations.java b/release-tools/src/main/java/org/springframework/data/release/io/OsOperations.java similarity index 95% rename from release-tools/src/main/java/org/springframework/data/release/io/OsCommandOperations.java rename to release-tools/src/main/java/org/springframework/data/release/io/OsOperations.java index 8d18e35..6ef74a2 100644 --- a/release-tools/src/main/java/org/springframework/data/release/io/OsCommandOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/io/OsOperations.java @@ -15,6 +15,7 @@ */ package org.springframework.data.release.io; +import java.io.File; import java.io.IOException; import java.util.concurrent.Future; @@ -26,7 +27,7 @@ import org.springframework.data.release.model.Project; * @author Stefan Schmidt * @since 1.2.0 */ -public interface OsCommandOperations { +public interface OsOperations { /** * Attempts the execution of a commands and delegates the output to the standard logger. @@ -41,4 +42,6 @@ public interface OsCommandOperations { Future executeWithOutput(String command, Project project) throws IOException; String executeForResult(String command, Project project) throws Exception; + + File getJavaHome(); } diff --git a/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java b/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java index dfdeed3..3f4f292 100644 --- a/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java +++ b/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java @@ -15,17 +15,23 @@ */ package org.springframework.data.release.io; +import static org.springframework.data.release.utils.StreamUtils.*; + import lombok.RequiredArgsConstructor; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.Arrays; import java.util.Scanner; +import java.util.stream.Stream; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.data.release.model.Project; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -44,6 +50,7 @@ public class Workspace { private static final Charset UTF_8 = Charset.forName("UTF-8"); private final IoProperties ioProperties; + private final ResourcePatternResolver resolver; /** * Returns the current working directory. @@ -93,7 +100,23 @@ public class Workspace { return new File(getProjectDirectory(project), name); } - public boolean processFile(String filename, Project project, LineCallback callback) throws Exception { + public Stream getFiles(String pattern, Project project) { + + File projectDirectory = getProjectDirectory(project); + String patternToLookup = String.format("file:%s/%s", projectDirectory.getAbsolutePath(), pattern); + + try { + return Arrays.stream(resolver.getResources(patternToLookup)).map(wrap(Resource::getFile)); + } catch (IOException o_O) { + throw new RuntimeException(o_O); + } + } + + public boolean processFiles(String pattern, Project project, LineCallback callback) { + return false; + } + + public boolean processFile(String filename, Project project, LineCallback callback) { File file = getFile(filename, project); @@ -115,9 +138,13 @@ public class Workspace { builder.append(result).append("\n"); } } + + writeContentToFile(filename, project, builder.toString()); + + } catch (Exception o_O) { + throw new RuntimeException(o_O); } - writeContentToFile(filename, project, builder.toString()); return true; } diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssue.java b/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssue.java index 10578d5..3a8f738 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssue.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssue.java @@ -33,6 +33,6 @@ class GitHubIssue { } public boolean isReleaseTicket(ModuleIteration module) { - return title.contains("Release") && title.contains(module.getVersionString()); + return title.contains("Release") && title.contains(module.getShortVersionString()); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java b/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java index 540b1bb..54bbc04 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/GitHubIssueTracker.java @@ -18,18 +18,20 @@ package org.springframework.data.release.jira; import lombok.RequiredArgsConstructor; import java.net.URI; +import java.nio.charset.Charset; import java.util.Arrays; +import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.release.git.GitProject; +import org.springframework.data.release.git.GitProperties; import org.springframework.data.release.git.GitServer; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ModuleIteration; @@ -39,16 +41,16 @@ 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) ) +@RequiredArgsConstructor class GitHubIssueTracker implements IssueTracker { private static final String MILESTONE_URI = "https://api.github.com/repos/spring-projects/{repoName}/milestones?state={state}"; @@ -59,6 +61,7 @@ class GitHubIssueTracker implements IssueTracker { private final RestOperations operations; private final Logger logger; + private final GitProperties properties; /* * (non-Javadoc) @@ -112,7 +115,9 @@ class GitHubIssueTracker implements IssueTracker { parameters.put("repoName", repositoryName); parameters.put("id", milestone.getNumber()); - return operations.exchange(URI_TEMPLATE, HttpMethod.GET, null, ISSUES_TYPE, parameters).getBody(); + return operations + .exchange(URI_TEMPLATE, HttpMethod.GET, new HttpEntity<>(getAuthenticationHeaders()), ISSUES_TYPE, parameters) + .getBody(); } private GitHubMilestone findMilestone(ModuleIteration module, String repositoryName) { @@ -127,13 +132,13 @@ class GitHubIssueTracker implements IssueTracker { logger.log(module, "Looking up milestone from %s…", milestoneUri); - List exchange = operations - .exchange(MILESTONE_URI, HttpMethod.GET, null, MILESTONES_TYPE, parameters).getBody(); + List exchange = operations.exchange(MILESTONE_URI, HttpMethod.GET, + new HttpEntity<>(getAuthenticationHeaders()), MILESTONES_TYPE, parameters).getBody(); GitHubMilestone milestone = null; for (GitHubMilestone candidate : exchange) { - if (candidate.getTitle().contains(module.getVersionString())) { + if (candidate.getTitle().contains(module.getShortVersionString())) { milestone = candidate; } } @@ -144,7 +149,19 @@ class GitHubIssueTracker implements IssueTracker { } } - throw new IllegalStateException(String.format("No milestone found containing %s!", module.getVersionString())); + throw new IllegalStateException(String.format("No milestone found containing %s!", module.getShortVersionString())); + } + + private HttpHeaders getAuthenticationHeaders() { + + byte[] encodedAuth = Base64.getEncoder() + .encode(properties.getAuthenticationHeader().getBytes(Charset.forName("US-ASCII"))); + String authHeader = "Basic " + new String(encodedAuth); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authHeader); + + return headers; } public static void main(String[] args) { diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/jira/IssueTrackerConfiguration.java similarity index 73% rename from release-tools/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java rename to release-tools/src/main/java/org/springframework/data/release/jira/IssueTrackerConfiguration.java index 6a87c67..5b92b03 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/JiraConfiguration.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/IssueTrackerConfiguration.java @@ -23,10 +23,13 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; +import org.springframework.data.release.git.GitProperties; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.utils.Logger; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.plugin.core.config.EnablePluginRegistries; +import org.springframework.plugin.core.OrderAwarePluginRegistry; +import org.springframework.plugin.core.PluginRegistry; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -37,15 +40,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; */ @Configuration @EnableCaching -@PropertySource(value = "file:jira.properties", ignoreResourceNotFound = true) -@EnablePluginRegistries({ IssueTracker.class }) -class JiraConfiguration { +class IssueTrackerConfiguration { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); } + @Bean + public PluginRegistry issueTrackers(List plugins) { + return OrderAwarePluginRegistry.create(plugins); + } + @Bean public ObjectMapper jacksonObjectMapper() { @@ -69,4 +75,14 @@ class JiraConfiguration { return template; } + + @Bean + public Jira jira(Logger logger) { + return new Jira(restTemplate(), logger); + } + + @Bean + public GitHubIssueTracker github(Logger logger, GitProperties properties) { + return new GitHubIssueTracker(restTemplate(), logger, properties); + } } diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/Jira.java b/release-tools/src/main/java/org/springframework/data/release/jira/Jira.java index ca50090..10c02d3 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/Jira.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/Jira.java @@ -24,7 +24,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -40,15 +39,13 @@ 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) ) +@RequiredArgsConstructor class Jira implements JiraConnector { private static final String JIRA_HOST = "https://jira.spring.io"; diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/JiraVersion.java b/release-tools/src/main/java/org/springframework/data/release/jira/JiraVersion.java index 5928d33..3e08b41 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/JiraVersion.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/JiraVersion.java @@ -17,10 +17,7 @@ package org.springframework.data.release.jira; import lombok.Value; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.Module; import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Train; /** * @author Oliver Gierke @@ -28,16 +25,7 @@ import org.springframework.data.release.model.Train; @Value class JiraVersion { - private final Module module; - private final Train train; - private final Iteration iteration; - - public JiraVersion(ModuleIteration moduleIteration) { - - this.module = moduleIteration.getModule(); - this.iteration = moduleIteration.getIteration(); - this.train = moduleIteration.getTrain(); - } + private ModuleIteration module; /* * (non-Javadoc) @@ -45,15 +33,6 @@ class JiraVersion { */ @Override public String toString() { - - Iteration iteration = this.iteration.isInitialIteration() && module.hasCustomFirstIteration() ? module - .getCustomFirstIteration() : this.iteration; - - if (iteration.isServiceIteration()) { - return String.format("%s.%s (%s %s)", module.getVersion(), iteration.getBugfixValue(), train.getName(), - iteration.getName()); - } - - return String.format("%s %s (%s)", module.getVersion(), iteration.getName(), train.getName()); + return module.getMediumVersionString(); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/jira/JqlQuery.java b/release-tools/src/main/java/org/springframework/data/release/jira/JqlQuery.java index d54d1c6..b8dd9e2 100644 --- a/release-tools/src/main/java/org/springframework/data/release/jira/JqlQuery.java +++ b/release-tools/src/main/java/org/springframework/data/release/jira/JqlQuery.java @@ -17,11 +17,11 @@ package org.springframework.data.release.jira; import static org.springframework.data.release.model.Projects.*; +import lombok.Value; + import java.util.ArrayList; import java.util.List; -import lombok.Value; - import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.TrainIteration; import org.springframework.util.StringUtils; diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/MavenOperations.java b/release-tools/src/main/java/org/springframework/data/release/maven/MavenOperations.java deleted file mode 100644 index 7de4df7..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/maven/MavenOperations.java +++ /dev/null @@ -1,251 +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.maven; - -import static org.springframework.data.release.model.Phase.*; -import static org.springframework.data.release.model.Projects.*; - -import lombok.RequiredArgsConstructor; - -import java.io.File; -import java.io.IOException; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.GenericTypeResolver; -import org.springframework.data.release.io.CommandResult; -import org.springframework.data.release.io.OsCommandOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.data.release.utils.Logger; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.xmlbeam.ProjectionFactory; -import org.xmlbeam.io.XBFileIO; - -/** - * @author Oliver Gierke - */ -@Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired) ) -public class MavenOperations { - - private static final String POM_XML = "pom.xml"; - - private final Workspace workspace; - private final ProjectionFactory projectionFactory; - private final OsCommandOperations os; - private final Logger logger; - - public Pom getMavenProject(Project project) throws IOException { - - File file = workspace.getFile(POM_XML, project); - return projectionFactory.io().file(file).read(Pom.class); - } - - /** - * Updates the POM files for all Maven projects contained in the iteration: - *
    - *
  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()); - final ArtifactVersion buildVersion = iteration.getModuleVersion(BUILD); - final ArtifactVersion nextBuildVersion = buildVersion.getNextDevelopmentVersion(); - - // Fix version of shared resources to to-be-released version. - execute(workspace.getFile("parent/pom.xml", BUILD), new PomCallback() { - - @Override - public void doWith(ParentPom pom) { - pom.setSharedResourcesVersion(phase.equals(PREPARE) ? buildVersion : nextBuildVersion); - } - }); - - for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { - - final Project project = module.getProject(); - - if (!isMavenProject(project)) { - logger.log(module, "No pom.xml file found, skipping project."); - continue; - } - - execute(workspace.getFile(POM_XML, project), new PomCallback() { - - @Override - public void doWith(Pom pom) { - - for (Project dependency : project.getDependencies()) { - - String dependencyProperty = dependency.getDependencyProperty(); - - if (pom.getProperty(dependencyProperty) == null) { - continue; - } - - ArtifactVersion dependencyVersion = iteration.getModuleVersion(dependency); - ArtifactVersion version = CLEANUP.equals(phase) ? dependencyVersion.getNextDevelopmentVersion() - : dependencyVersion; - - logger.log(project, "Updating %s dependency version property %s to %s.", dependency.getFullName(), - dependencyProperty, version); - pom.setProperty(dependencyProperty, version); - } - - ArtifactVersion version = CLEANUP.equals(phase) ? nextBuildVersion : buildVersion; - logger.log(project, "Updating Spring Data Build Parent version to %s.", version); - pom.setParentVersion(version); - - updateRepository(project, pom, repository, phase); - } - }); - } - } - - /** - * Triggers building the distribution artifacts for all Maven projects of the given {@link Train}. - * - * @param train - * @param iteration - * @throws IOException - * @throws InterruptedException - */ - public void triggerDistributionBuild(TrainIteration iteration) throws Exception { - - ExecutionUtils.run(iteration, module -> { - - Project project = module.getProject(); - - if (BUILD.equals(project)) { - return; - } - - if (!isMavenProject(project)) { - logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); - return; - } - - logger.log(project, "Triggering distribution build…"); - - ArtifactVersion version = ArtifactVersion.of(module); - - String command = "mvn clean deploy -DskipTests -Pdistribute"; - - if (version.isMilestoneVersion()) { - command = command.concat(",milestone"); - } else if (version.isReleaseVersion()) { - command = command.concat(",release"); - } - - CommandResult result = os.executeWithOutput(command, module.getProject()).get(); - - if (result.hasError()) { - throw result.getException(); - } - - logger.log(project, "Successfully finished distribution build!"); - }); - } - - private boolean isMavenProject(Project project) { - return workspace.getFile(POM_XML, project).exists(); - } - - private void updateBomPom(final TrainIteration iteration, final Phase phase) throws Exception { - - File bomPomFile = workspace.getFile("bom/pom.xml", BUILD); - - logger.log(BUILD, "Updating BOM pom.xml…"); - - execute(bomPomFile, new PomCallback() { - - @Override - public void doWith(Pom pom) { - - for (ModuleIteration module : iteration.getModulesExcept(BUILD)) { - - Artifact artifact = new Artifact(module); - ArtifactVersion version = artifact.getVersion(); - version = PREPARE.equals(phase) ? version : version.getNextDevelopmentVersion(); - - logger.log(BUILD, "%s", module); - - pom.setDependencyManagementVersion(artifact.getArtifactId(), version); - - for (String additionalArtifact : module.getProject().getAdditionalArtifacts()) { - pom.setDependencyManagementVersion(additionalArtifact, version); - } - } - } - }); - } - - private void updateRepository(Project project, Pom pom, Repository repository, Phase phase) { - - String message = "Switching to Spring repository %s (%s)."; - - if (PREPARE.equals(phase)) { - - logger.log(project, message, repository.getId(), repository.getUrl()); - - pom.setRepositoryId(repository.getSnapshotId(), repository.getId()); - pom.setRepositoryUrl(repository.getId(), repository.getUrl()); - - } else { - - logger.log(project, message, repository.getSnapshotId(), repository.getSnapshotUrl()); - - pom.setRepositoryId(repository.getId(), repository.getSnapshotId()); - pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl()); - } - } - - @SuppressWarnings("unchecked") - private void execute(File file, PomCallback callback) throws Exception { - - XBFileIO io = projectionFactory.io().file(file); - Class typeArgument = GenericTypeResolver.resolveTypeArgument(callback.getClass(), PomCallback.class); - - T pom = (T) io.read(typeArgument); - callback.doWith(pom); - io.write(pom); - } - - private interface PomCallback { - public void doWith(T pom); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java b/release-tools/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java index 04a9013..b270720 100644 --- a/release-tools/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java +++ b/release-tools/src/main/java/org/springframework/data/release/misc/ReleaseOperations.java @@ -24,7 +24,6 @@ import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.git.GitOperations; import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.io.Workspace.LineCallback; import org.springframework.data.release.jira.Changelog; import org.springframework.data.release.jira.IssueTracker; import org.springframework.data.release.model.Iteration; @@ -77,27 +76,23 @@ public class ReleaseOperations { for (String location : CHANGELOG_LOCATIONS) { - boolean processed = workspace.processFile(location, module.getProject(), new LineCallback() { + boolean processed = workspace.processFile(location, module.getProject(), (line, number) -> { - @Override - public String doWith(String line, long number) { + if (line.startsWith("=")) { - if (line.startsWith("=")) { + StringBuilder builder = new StringBuilder(); + builder.append(line).append("\n\n"); + builder.append(changelog.toString()); - StringBuilder builder = new StringBuilder(); - builder.append(line).append("\n\n"); - builder.append(changelog.toString()); - - return builder.toString(); - } else { - return line; - } + return builder.toString(); + } else { + return line; } }); if (processed) { - git.commit(module, "Updated changelog.", null); + git.commit(module, "Updated changelog."); logger.log(module.getProject(), "Updated changelog %s.", location); } @@ -107,25 +102,14 @@ public class ReleaseOperations { public void updateResources(TrainIteration iteration) throws Exception { - for (final ModuleIteration module : iteration) { + iteration.stream().forEach(module -> { boolean processed = workspace.processFile("src/main/resources/notice.txt", module.getProject(), - new LineCallback() { - - @Override - public String doWith(String line, long number) { - - if (number != 0) { - return line; - } - - return module.toString(); - } - }); + (line, number) -> number != 0 ? line : module.toString()); if (processed) { logger.log(module, "Updated notice.txt."); } - } + }); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/maven/MavenProject.java b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinate.java similarity index 52% rename from release-tools/src/main/java/org/springframework/data/release/maven/MavenProject.java rename to release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinate.java index c010c31..46f8693 100644 --- a/release-tools/src/main/java/org/springframework/data/release/maven/MavenProject.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinate.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,30 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.model; import lombok.Value; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Module; - /** * @author Oliver Gierke */ -@Value -class MavenProject { +@Value(staticConstructor = "of") +public class ArtifactCoordinate { - private final Module module; + String groupId, artifactId; - public String getGroupId() { - return "org.springframework.data"; - } + public static ArtifactCoordinate from(String coordinate) { - public String getArtifactId() { - return String.format("spring-data-%s", module.getProject().getName().toLowerCase()); - } - - public ArtifactVersion getReleaseVersion() { - return ArtifactVersion.of(module.getVersion()); + String[] parts = coordinate.split(":"); + return new ArtifactCoordinate(parts[0], parts[1]); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinates.java b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinates.java new file mode 100644 index 0000000..b46f9d8 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactCoordinates.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Oliver Gierke + */ +@RequiredArgsConstructor +public class ArtifactCoordinates { + + public static ArtifactCoordinates NONE = forGroupId("org.springframework.data"); + + private final String groupId; + private final @Getter(AccessLevel.PACKAGE) List coordinates; + + public static ArtifactCoordinates forGroupId(String groupId) { + return new ArtifactCoordinates(groupId, new ArrayList<>()); + } + + public ArtifactCoordinates artifacts(String... artifactIds) { + + return new ArtifactCoordinates(groupId, + Arrays.stream(artifactIds)// + .map(artifactId -> ArtifactCoordinate.of(groupId, artifactId))// + .collect(Collectors.toList())); + } + + public ArtifactCoordinates artifact(ArtifactCoordinate coordinate) { + + List artifacts = new ArrayList<>(coordinates); + artifacts.add(coordinate); + + return new ArtifactCoordinates(groupId, artifacts); + } + +} diff --git a/release-tools/src/main/java/org/springframework/data/release/model/IterationVersion.java b/release-tools/src/main/java/org/springframework/data/release/model/IterationVersion.java index 09ee3bc..9016003 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/IterationVersion.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/IterationVersion.java @@ -20,9 +20,9 @@ package org.springframework.data.release.model; * * @author Oliver Gierke */ -public interface IterationVersion { - - Version getVersion(); +public interface IterationVersion extends VersionAware { Iteration getIteration(); + + boolean isServiceIteration(); } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Module.java b/release-tools/src/main/java/org/springframework/data/release/model/Module.java index 73bc537..84e9b31 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Module.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Module.java @@ -23,7 +23,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke */ @Value -public class Module { +public class Module implements VersionAware { private final Project project; private final Version version; diff --git a/release-tools/src/main/java/org/springframework/data/release/model/ModuleIteration.java b/release-tools/src/main/java/org/springframework/data/release/model/ModuleIteration.java index 815be00..eaec3c4 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/ModuleIteration.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/ModuleIteration.java @@ -27,8 +27,14 @@ import lombok.RequiredArgsConstructor; public class ModuleIteration implements IterationVersion { private final @Getter Module module; - private final Iteration iteration; - private final @Getter Train train; + private final @Getter TrainIteration trainIteration; + + /** + * @return the train + */ + public Train getTrain() { + return trainIteration.getTrain(); + } public ProjectKey getProjectKey() { return module.getProject().getKey(); @@ -52,20 +58,33 @@ public class ModuleIteration implements IterationVersion { * @see org.springframework.data.release.model.IterationVersion#getIteration() */ public Iteration getIteration() { - return this.iteration.isInitialIteration() && this.module.hasCustomFirstIteration() ? module - .getCustomFirstIteration() : this.iteration; + + return trainIteration.getIteration().isInitialIteration() && this.module.hasCustomFirstIteration() + ? module.getCustomFirstIteration() : this.trainIteration.getIteration(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.model.IterationVersion#isServiceIteration() + */ + @Override + public boolean isServiceIteration() { + return getIteration().isServiceIteration(); } /** - * Returns the {@link String} representation of the logical version of the {@link ModuleIteration}. + * Returns the {@link String} representation of the logical version of the {@link ModuleIteration}. This will + * abbreviate trailing zeros and not include the release train name. * * @return */ - public String getVersionString() { + public String getShortVersionString() { StringBuilder builder = new StringBuilder(); builder.append(ArtifactVersion.of(this).toShortString()); + Iteration iteration = trainIteration.getIteration(); + if (!iteration.isServiceIteration()) { builder.append(" ").append(iteration.getName()); } @@ -73,12 +92,41 @@ public class ModuleIteration implements IterationVersion { return builder.toString(); } + public String getMediumVersionString() { + + StringBuilder builder = new StringBuilder(); + builder.append(ArtifactVersion.of(this).toShortString()); + + Iteration iteration = trainIteration.getIteration(); + + if (iteration.isServiceIteration()) { + builder.append(" (").append(trainIteration.toString()); + } else { + builder.append(" ").append(iteration.getName()).append(" ("); + builder.append(trainIteration.getTrain().getName()); + } + + return builder.append(")").toString(); + } + + /** + * Returns the {@link String} representation of the logical version of the {@link ModuleIteration}. This will use the + * technical version string and append the train iteration. + * + * @return + */ + public String getFullVersionString() { + + String result = ArtifactVersion.of(this).toString(); + return result.concat(" (").concat(trainIteration.toString()).concat(")"); + } + /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { - return String.format("%s %s", module.getProject().getFullName(), getVersionString()); + return String.format("%s %s", module.getProject().getFullName(), getShortVersionString()); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Modules.java b/release-tools/src/main/java/org/springframework/data/release/model/Modules.java new file mode 100644 index 0000000..4965380 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/model/Modules.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import lombok.RequiredArgsConstructor; + +import java.util.Collection; +import java.util.Iterator; + +import org.springframework.data.release.Streamable; + +/** + * @author Oliver Gierke + */ +@RequiredArgsConstructor +public class Modules implements Streamable { + + private final Collection modules; + + public Iterator iterator() { + return modules.iterator(); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Phase.java b/release-tools/src/main/java/org/springframework/data/release/model/Phase.java index e35b9fa..65d13c0 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Phase.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Phase.java @@ -20,5 +20,5 @@ package org.springframework.data.release.model; */ public enum Phase { - PREPARE, CLEANUP; + PREPARE, CLEANUP, MAINTENANCE; } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Project.java b/release-tools/src/main/java/org/springframework/data/release/model/Project.java index 9df3281..a1919b3 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Project.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Project.java @@ -19,8 +19,8 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import org.springframework.util.Assert; @@ -35,17 +35,18 @@ public class Project { private final @Getter String name; private final @Getter List dependencies; private final Tracker tracker; - private final @Getter List additionalArtifacts; + private final @Getter ArtifactCoordinates additionalArtifacts; Project(String key, String name, List dependencies) { - this(key, name, Tracker.JIRA, dependencies, Collections.emptyList()); + this(key, name, Tracker.JIRA, dependencies, ArtifactCoordinates.NONE); } - Project(String key, String name, List dependencies, List additionalArtifacts) { + Project(String key, String name, List dependencies, ArtifactCoordinates additionalArtifacts) { this(key, name, Tracker.JIRA, dependencies, additionalArtifacts); } - Project(String key, String name, Tracker tracker, List dependencies, List additionalArtifacts) { + Project(String key, String name, Tracker tracker, List dependencies, + ArtifactCoordinates additionalArtifacts) { this.key = new ProjectKey(key); this.name = name; @@ -66,6 +67,10 @@ public class Project { return "springdata.".concat(name.toLowerCase()); } + public void doWithAdditionalArtifacts(Consumer consumer) { + additionalArtifacts.getCoordinates().forEach(consumer); + } + /** * Returns whether the current project depends on the given one. * @@ -75,6 +80,7 @@ public class Project { public boolean dependsOn(Project project) { Assert.notNull(project, "Project must not be null!"); - return dependencies.contains(project); + + return dependencies.stream().anyMatch(dependency -> dependency.equals(project) || dependency.dependsOn(project)); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Projects.java b/release-tools/src/main/java/org/springframework/data/release/model/Projects.java index 351cfcd..dc18ae9 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Projects.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Projects.java @@ -15,10 +15,16 @@ */ package org.springframework.data.release.model; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import org.jgrapht.graph.DefaultDirectedGraph; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.traverse.TopologicalOrderIterator; + /** * @author Oliver Gierke */ @@ -30,26 +36,60 @@ public class Projects { static { - BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB, Collections.emptyList(), Collections.emptyList()); + BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB, Collections.emptyList(), + ArtifactCoordinates.forGroupId("org.springframework.data.build") + .artifacts("spring-data-build-parent", "spring-data-build-resources") + .artifact(ArtifactCoordinate.of("org.springframework.data", "spring-data-releasetrain"))); COMMONS = new Project("DATACMNS", "Commons", Arrays.asList(BUILD)); JPA = new Project("DATAJPA", "JPA", Arrays.asList(COMMONS)); MONGO_DB = new Project("DATAMONGO", "MongoDB", Arrays.asList(COMMONS), - Arrays.asList("spring-data-mongodb-cross-store", "spring-data-mongodb-log4j")); + ArtifactCoordinates.NONE.artifacts("spring-data-mongodb-cross-store", "spring-data-mongodb-log4j")); NEO4J = new Project("DATAGRAPH", "Neo4j", Arrays.asList(COMMONS)); SOLR = new Project("DATASOLR", "Solr", Arrays.asList(COMMONS)); COUCHBASE = new Project("DATACOUCH", "Couchbase", Arrays.asList(COMMONS)); - CASSANDRA = new Project("DATACASS", "Cassandra", Arrays.asList(COMMONS), Arrays.asList("spring-cql")); + CASSANDRA = new Project("DATACASS", "Cassandra", Arrays.asList(COMMONS), + ArtifactCoordinates.NONE.artifacts("spring-cql")); ELASTICSEARCH = new Project("DATAES", "Elasticsearch", Arrays.asList(COMMONS)); REDIS = new Project("DATAREDIS", "Redis", Collections.emptyList()); GEMFIRE = new Project("SGF", "Gemfire", Arrays.asList(COMMONS)); REST = new Project("DATAREST", "REST", Arrays.asList(COMMONS, JPA, MONGO_DB, NEO4J, GEMFIRE, SOLR, CASSANDRA), - Arrays.asList("spring-data-rest-core", "spring-data-rest-core", "spring-data-rest-hal-browser")); + ArtifactCoordinates.NONE.artifacts("spring-data-rest-core", "spring-data-rest-core", + "spring-data-rest-hal-browser")); KEY_VALUE = new Project("DATAKV", "KeyValue", Arrays.asList(COMMONS)); - ENVERS = new Project("DATAENV", "Envers", Arrays.asList(JPA, COMMONS)); + ENVERS = new Project("DATAENV", "Envers", Tracker.GITHUB, Arrays.asList(JPA, COMMONS), ArtifactCoordinates.NONE); - PROJECTS = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, ELASTICSEARCH, REDIS, - GEMFIRE, REST, KEY_VALUE, ENVERS); + List projects = Arrays.asList(BUILD, COMMONS, JPA, MONGO_DB, NEO4J, SOLR, COUCHBASE, CASSANDRA, + ELASTICSEARCH, REDIS, GEMFIRE, REST, KEY_VALUE, ENVERS); + + DefaultDirectedGraph graph = new DefaultDirectedGraph<>(DefaultEdge.class); + + projects.forEach(project -> { + + graph.addVertex(project); + + project.getDependencies().forEach(dependency -> { + graph.addEdge(project, dependency); + }); + }); + + Iterator iterator = new TopologicalOrderIterator<>(graph); + List intermediate = new ArrayList<>(projects.size()); + + while (iterator.hasNext()) { + intermediate.add(iterator.next()); + } + + Collections.reverse(intermediate); + + PROJECTS = Collections.unmodifiableList(intermediate); + } + + public static Project byName(String name) { + + return PROJECTS.stream().// + filter(project -> project.getName().equalsIgnoreCase(name)).// + findFirst().orElseThrow(() -> new IllegalArgumentException("No project named %s available!")); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/ReleaseTrains.java b/release-tools/src/main/java/org/springframework/data/release/model/ReleaseTrains.java index 21489f7..3f560a5 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/ReleaseTrains.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/ReleaseTrains.java @@ -36,8 +36,8 @@ public class ReleaseTrains { EVANS = DIJKSTRA.next("Evans", Transition.MINOR); FOWLER = EVANS.next("Fowler", Transition.MINOR); GOSLING = FOWLER.next("Gosling", Transition.MINOR, new Module(KEY_VALUE, "1.0")); - HOPPER = GOSLING.next("Hopper", Transition.MINOR, new Module(NEO4J, "4.1"), new Module(COUCHBASE, "2.1"), - new Module(ENVERS, "1.0")); + HOPPER = GOSLING.next("Hopper", Transition.MINOR, new Module(SOLR, "2.0"), new Module(ENVERS, "1.0"), + new Module(NEO4J, "4.1"), new Module(COUCHBASE, "2.1")); // Trains diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Train.java b/release-tools/src/main/java/org/springframework/data/release/model/Train.java index 788a724..1cc23b3 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Train.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Train.java @@ -23,13 +23,13 @@ import lombok.Value; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.springframework.data.release.Streamable; import org.springframework.shell.support.util.OsUtils; import org.springframework.util.Assert; @@ -37,23 +37,20 @@ import org.springframework.util.Assert; * @author Oliver Gierke */ @Value -public class Train implements Iterable { +public class Train implements Streamable { private final String name;; - private final Collection modules; + private final Modules modules; private final Iterations iterations; public Train(String name, Module... modules) { - - this.name = name; - this.modules = Arrays.asList(modules); - this.iterations = Iterations.DEFAULT; + this(name, Arrays.asList(modules)); } public Train(String name, Collection modules) { this.name = name; - this.modules = Collections.unmodifiableCollection(modules); + this.modules = new Modules(modules); this.iterations = Iterations.DEFAULT; } @@ -66,6 +63,12 @@ public class Train implements Iterable { return modules.iterator(); } + public boolean contains(Project project) { + + return modules.stream().// + anyMatch(module -> module.getProject().equals(project)); + } + public Module getModule(String name) { return modules.stream().// @@ -98,7 +101,7 @@ public class Train implements Iterable { return modules.stream().// filter(module -> module.hasName(moduleName)).// findFirst().// - map(module -> new ModuleIteration(module, iteration, this)).// + map(module -> new ModuleIteration(module, new TrainIteration(this, iteration))).// orElseThrow( () -> new IllegalArgumentException(String.format("No module found with module name %s!", moduleName))); } @@ -113,7 +116,8 @@ public class Train implements Iterable { return modules.stream().// filter(module -> !exclusionList.contains(module.getProject())).// - map(module -> new ModuleIteration(module, iteration, this)).// + map(module -> new ModuleIteration(module, new TrainIteration(this, iteration))).// + sorted().// collect(Collectors.toList()); } @@ -125,7 +129,7 @@ public class Train implements Iterable { Module module = getModule(project); - return ArtifactVersion.of(new ModuleIteration(module, iteration, this)); + return ArtifactVersion.of(new ModuleIteration(module, new TrainIteration(this, iteration))); } /* diff --git a/release-tools/src/main/java/org/springframework/data/release/model/TrainIteration.java b/release-tools/src/main/java/org/springframework/data/release/model/TrainIteration.java index 7c5ae52..71b5f61 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/TrainIteration.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/TrainIteration.java @@ -18,12 +18,15 @@ package org.springframework.data.release.model; import lombok.Value; import java.util.Iterator; +import java.util.List; + +import org.springframework.data.release.Streamable; /** * @author Oliver Gierke */ @Value -public class TrainIteration implements Iterable { +public class TrainIteration implements Streamable { private final Train train; private final Iteration iteration; @@ -49,16 +52,29 @@ public class TrainIteration implements Iterable { return train.getModuleIteration(iteration, project.getName()); } - public Iterable getModulesExcept(Project... exclusions) { + public List getModulesExcept(Project... exclusions) { return train.getModuleIterations(iteration, exclusions); } + public boolean contains(Project project) { + return train.contains(project); + } + public ModuleIteration getPreviousIteration(ModuleIteration module) { Iteration previousIteration = train.getIterations().getPreviousIteration(iteration); return train.getModuleIteration(previousIteration, module.getProject().getName()); } + /** + * Returns the version string to be used for the train iteration. + * + * @return + */ + public String toVersionString() { + return toString().replace(' ', '-'); + } + /* * (non-Javadoc) * @see java.lang.Object#toString() @@ -67,4 +83,5 @@ public class TrainIteration implements Iterable { public String toString() { return String.format("%s %s", train.getName(), iteration.getName()); } + } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/UpdateInformation.java b/release-tools/src/main/java/org/springframework/data/release/model/UpdateInformation.java new file mode 100644 index 0000000..0e40d90 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/model/UpdateInformation.java @@ -0,0 +1,94 @@ +package org.springframework.data.release.model; + +import static org.springframework.data.release.model.Projects.*; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.release.build.Repository; +import org.springframework.util.Assert; + +/** + * Value object to expose update information for a given {@link TrainIteration} and phase. + * + * @author Oliver Gierke + */ +@RequiredArgsConstructor +public class UpdateInformation { + + private final @NonNull @Getter TrainIteration iteration; + private final @NonNull @Getter Phase phase; + + /** + * Returns the {@link ArtifactVersion} to be set for the given {@link Project}. + * + * @param dependency must not be {@literal null}. + * @return will never be {@literal null}. + */ + public ArtifactVersion getProjectVersionToSet(Project dependency) { + + Assert.notNull(dependency, "Project must not be null!"); + + ArtifactVersion dependencyVersion = iteration.getModuleVersion(dependency); + + switch (phase) { + case PREPARE: + return dependencyVersion; + case CLEANUP: + return dependencyVersion.getNextDevelopmentVersion(); + case MAINTENANCE: + return dependencyVersion.getNextBugfixVersion(); + } + + throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); + } + + /** + * Returns the {@link ArtifactVersion} to be set for the parent reference. + * + * @return will never be {@literal null}. + */ + public ArtifactVersion getParentVersionToSet() { + + ArtifactVersion version = iteration.getModuleVersion(BUILD); + + switch (phase) { + case PREPARE: + return version; + case CLEANUP: + return version.getNextDevelopmentVersion(); + case MAINTENANCE: + return version.getNextBugfixVersion(); + } + + throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); + } + + /** + * Returns the {@link Repository} to use (milestone or release). + * + * @return will never be {@literal null}. + */ + public Repository getRepository() { + return new Repository(iteration.getIteration()); + } + + /** + * Returns the version {@link String} to be used to describe the release train. + * + * @return will never be {@literal null}. + */ + public String getReleaseTrainVersion() { + + switch (phase) { + case PREPARE: + return iteration.toVersionString(); + case CLEANUP: + case MAINTENANCE: + return iteration.getTrain().getName().concat("-BUILD-SNAPSHOT"); + } + + throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/model/VersionAware.java b/release-tools/src/main/java/org/springframework/data/release/model/VersionAware.java new file mode 100644 index 0000000..17a4c88 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/model/VersionAware.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +/** + * @author Oliver Gierke + */ +public interface VersionAware { + + Version getVersion(); +} diff --git a/release-tools/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java b/release-tools/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java index 840e182..cc05c98 100644 --- a/release-tools/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java +++ b/release-tools/src/main/java/org/springframework/data/release/utils/ExecutionUtils.java @@ -19,8 +19,8 @@ import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; +import org.springframework.data.release.Streamable; import org.springframework.util.Assert; /** @@ -35,15 +35,15 @@ public class ExecutionUtils { * all executions to complete before returning. Exceptions being thrown in the {@link ConsumerWithException} will be * converted into {@link RuntimeException}s. * - * @param iterable must not be {@literal null}. + * @param streamable must not be {@literal null}. * @param consumer must not be {@literal null}. */ - public static void run(Iterable iterable, ConsumerWithException consumer) { + public static void run(Streamable streamable, ConsumerWithException consumer) { - Assert.notNull(iterable, "Iterable must not be null!"); + Assert.notNull(streamable, "Streamable must not be null!"); Assert.notNull(consumer, "Consumer must not be null!"); - StreamSupport.stream(iterable.spliterator(), false).// + streamable.stream().// map(it -> CompletableFuture.runAsync(() -> { try { consumer.accept(it); @@ -55,19 +55,19 @@ public class ExecutionUtils { } /** - * Runs the given {@link Function} for each element in the given {@link Iterable} in parallel waiting for all + * Runs the given {@link Function} for each element in the given {@link Streamable} in parallel waiting for all * executions to complete before returning the results. * - * @param iterable must not be {@literal null}. + * @param streamable must not be {@literal null}. * @param function must not be {@literal null}. * @return */ - public static Collection runAndReturn(Iterable iterable, Function function) { + public static Collection runAndReturn(Streamable streamable, Function function) { - Assert.notNull(iterable, "Iterable must not be null!"); + Assert.notNull(streamable, "Iterable must not be null!"); Assert.notNull(function, "Consumer must not be null!"); - return StreamSupport.stream(iterable.spliterator(), false).// + return streamable.stream().// map(it -> CompletableFuture.supplyAsync(() -> function.apply(it))).// collect(Collectors.toList()).// stream().// diff --git a/release-tools/src/main/java/org/springframework/data/release/utils/Logger.java b/release-tools/src/main/java/org/springframework/data/release/utils/Logger.java index cd357b3..fdbd384 100644 --- a/release-tools/src/main/java/org/springframework/data/release/utils/Logger.java +++ b/release-tools/src/main/java/org/springframework/data/release/utils/Logger.java @@ -32,23 +32,23 @@ public class Logger { private final java.util.logging.Logger LOGGER = HandlerUtils.getLogger(getClass()); - public void log(ModuleIteration module, String template, Object... args) { + public void log(ModuleIteration module, Object template, Object... args) { log(module.getProject(), template, args); } - public void log(Project project, String template, Object... args) { + public void log(Project project, Object template, Object... args) { log(project.getName(), template, args); } - public void log(TrainIteration iteration, String template, Object... args) { + public void log(TrainIteration iteration, Object template, Object... args) { log(iteration.toString(), template, args); } - public void log(Train train, String template, Object... args) { + public void log(Train train, Object template, Object... args) { log(train.getName(), template, args); } - private void log(String context, String template, Object... args) { - LOGGER.info(String.format(PREFIX_TEMPLATE, context, String.format(template, args))); + private void log(String context, Object template, Object... args) { + LOGGER.info(String.format(PREFIX_TEMPLATE, context, String.format(template.toString(), args))); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/utils/StreamUtils.java b/release-tools/src/main/java/org/springframework/data/release/utils/StreamUtils.java new file mode 100644 index 0000000..33b3fc6 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/utils/StreamUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.utils; + +import java.util.function.Function; + +/** + * @author Oliver Gierke + */ +public interface StreamUtils { + + public static Function wrap(FunctionWithException function) { + return it -> { + try { + return function.apply(it); + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + }; + } + + public interface FunctionWithException { + S apply(T source) throws Exception; + } +} diff --git a/release-tools/src/main/resources/application.properties b/release-tools/src/main/resources/application.properties index f759759..40e639e 100644 --- a/release-tools/src/main/resources/application.properties +++ b/release-tools/src/main/resources/application.properties @@ -1 +1,12 @@ io.work-dir=~/temp/spring-data-shell + +maven.release-command=install -DskipTests + +#maven.plugins.deploy=org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy +maven.plugins.versions-set=org.codehaus.mojo:versions-maven-plugin:2.2:set +maven.plugins.artifactory-deploy=org.jfrog.buildinfo:artifactory-maven-plugin:2.4.0:publish + +deployment.server.uri=https://repo.spring.io +deployment.staging-repository=libs-staging-local +deployment.username=buildmaster +#deployment.password <- local \ No newline at end of file diff --git a/release-tools/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java b/release-tools/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java index 9faa6c4..ccb79c1 100644 --- a/release-tools/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java +++ b/release-tools/src/test/java/org/springframework/data/release/AbstractIntegrationTests.java @@ -23,7 +23,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Oliver Gierke */ -@ActiveProfiles({ "test", "local" }) +@ActiveProfiles({ "local", "test" }) @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) public abstract class AbstractIntegrationTests {} diff --git a/release-tools/src/test/java/org/springframework/data/release/ApplicationTests.java b/release-tools/src/test/java/org/springframework/data/release/ApplicationTests.java new file mode 100644 index 0000000..a269533 --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/ApplicationTests.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release; + +import org.junit.Test; + +/** + * @author Oliver Gierke + */ +public class ApplicationTests extends AbstractIntegrationTests { + + @Test + public void bootstrapsApplication() {} +} diff --git a/release-tools/src/test/java/org/springframework/data/release/ArtifactUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/ArtifactUnitTests.java index 13dda41..260dcb5 100644 --- a/release-tools/src/test/java/org/springframework/data/release/ArtifactUnitTests.java +++ b/release-tools/src/test/java/org/springframework/data/release/ArtifactUnitTests.java @@ -19,7 +19,7 @@ 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.build.MavenArtifact; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.Iteration; import org.springframework.data.release.model.ReleaseTrains; @@ -32,7 +32,7 @@ public class ArtifactUnitTests { @Test public void testname() { - Artifact artifact = new Artifact(ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.M1, "JPA")); + MavenArtifact artifact = new MavenArtifact(ReleaseTrains.DIJKSTRA.getModuleIteration(Iteration.M1, "JPA")); assertThat(artifact.getArtifactId(), is("spring-data-jpa")); assertThat(artifact.getVersion(), is(ArtifactVersion.of("1.6.0.M1"))); diff --git a/release-tools/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java b/release-tools/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java similarity index 64% rename from release-tools/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java rename to release-tools/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java index d85930d..aaa8d8e 100644 --- a/release-tools/src/test/java/org/springframework/data/release/maven/MavenIntegrationTests.java +++ b/release-tools/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.maven; +package org.springframework.data.release.build; import java.io.IOException; @@ -21,8 +21,16 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.data.release.AbstractIntegrationTests; +import org.springframework.data.release.git.GitOperations; 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.Projects; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.model.UpdateInformation; import org.xmlbeam.ProjectionFactory; import org.xmlbeam.io.XBFileIO; @@ -33,6 +41,8 @@ public class MavenIntegrationTests extends AbstractIntegrationTests { @Autowired Workspace workspace; @Autowired ProjectionFactory projection; + @Autowired MavenBuildSystem maven; + @Autowired GitOperations git; @Test public void modifiesParentPomCorrectly() throws IOException { @@ -42,7 +52,7 @@ public class MavenIntegrationTests extends AbstractIntegrationTests { ParentPom pom = io.read(ParentPom.class); pom.setSharedResourcesVersion(ArtifactVersion.of("1.2.0.RELEASE")); - // System.out.println(projection.asString(pom)); + System.out.println(projection.asString(pom)); } @Test @@ -57,4 +67,17 @@ public class MavenIntegrationTests extends AbstractIntegrationTests { // System.out.println(projection.asString(pom)); } + + @Test + public void testname() throws Exception { + + git.update(ReleaseTrains.HOPPER); + + TrainIteration iteration = new TrainIteration(ReleaseTrains.HOPPER, Iteration.M1); + ModuleIteration build = iteration.getModule(Projects.BUILD); + UpdateInformation information = new UpdateInformation(iteration, Phase.PREPARE); + + maven.updateProjectDescriptors(build, information); + maven.prepareVersion(build, Phase.PREPARE); + } } diff --git a/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentInformationIntegrationTests.java b/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentInformationIntegrationTests.java new file mode 100644 index 0000000..18e3d3a --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentInformationIntegrationTests.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015-2016 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.deployment; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.AbstractIntegrationTests; +import org.springframework.data.release.model.Iteration; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.TrainIteration; + +/** + * Integration test for {@link DeploymentInformation}. + * + * @author Oliver Gierke + */ +public class DeploymentInformationIntegrationTests extends AbstractIntegrationTests { + + @Autowired DeploymentProperties properties; + + @Test + public void createsDeploymentInformation() { + + TrainIteration iteration = new TrainIteration(ReleaseTrains.HOPPER, Iteration.M1); + ModuleIteration buildModule = iteration.getModule(Projects.BUILD); + + DeploymentInformation information = new DeploymentInformation(buildModule, properties); + + assertThat(information.getDeploymentTargetUrl(), containsString(properties.getServer().getUri().toString())); + assertThat(information.getBuildName(), is("Spring Data Build - Release")); + assertThat(information.getTargetRepository(), is("test-libs-milestone-local")); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentOperationsIntegrationTests.java b/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentOperationsIntegrationTests.java new file mode 100644 index 0000000..4f747a9 --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/deployment/DeploymentOperationsIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.deployment; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.AbstractIntegrationTests; +import org.springframework.data.release.build.BuildOperations; +import org.springframework.data.release.git.GitOperations; +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.ReleaseTrains; +import org.springframework.data.release.model.Train; + +/** + * @author Oliver Gierke + */ +public class DeploymentOperationsIntegrationTests extends AbstractIntegrationTests { + + @Autowired GitOperations git; + @Autowired BuildOperations build; + @Autowired DeploymentOperations deployment; + @Autowired ArtifactoryClient client; + + @Test + public void testname() { + + Train train = ReleaseTrains.HOPPER; + ModuleIteration buildModule = train.getModuleIteration(Iteration.M1, "build"); + + git.update(train); + build.prepareVersion(buildModule, Phase.PREPARE); + + DeploymentInformation information = build.buildAndDeployRelease(buildModule); + + try { + deployment.promote(information); + } finally { + client.deleteArtifacts(information); + } + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java index a6f1b7b..986f2a7 100644 --- a/release-tools/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java +++ b/release-tools/src/test/java/org/springframework/data/release/model/ModuleIterationUnitTests.java @@ -31,7 +31,9 @@ public class ModuleIterationUnitTests { TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.M1); ModuleIteration module = iteration.getModule(Projects.JPA); - assertThat(module.getVersionString(), is("1.6 M1")); + assertThat(module.getShortVersionString(), is("1.6 M1")); + assertThat(module.getMediumVersionString(), is("1.6 M1 (Dijkstra)")); + assertThat(module.getFullVersionString(), is("1.6.0.M1 (Dijkstra M1)")); } @Test @@ -40,6 +42,8 @@ public class ModuleIterationUnitTests { TrainIteration iteration = new TrainIteration(ReleaseTrains.DIJKSTRA, Iteration.SR1); ModuleIteration module = iteration.getModule(Projects.JPA); - assertThat(module.getVersionString(), is("1.6.1")); + assertThat(module.getShortVersionString(), is("1.6.1")); + assertThat(module.getMediumVersionString(), is("1.6.1 (Dijkstra SR1)")); + assertThat(module.getFullVersionString(), is("1.6.1.RELEASE (Dijkstra SR1)")); } } diff --git a/release-tools/src/test/java/org/springframework/data/release/model/ProjectUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/model/ProjectUnitTests.java new file mode 100644 index 0000000..2e53c91 --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/model/ProjectUnitTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +/** + * @author Oliver Gierke + */ +public class ProjectUnitTests { + + @Test + public void testname() { + + List projects = new ArrayList<>(Projects.PROJECTS); + // Collections.reverse(projects); + // Collections.sort(projects); + + projects.stream().map(Project::getName).forEach(System.out::println); + + System.out.println(); + + assertThat(projects.get(0), is(Projects.BUILD)); + assertThat(projects.get(1), is(Projects.COMMONS)); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/model/SimpleIterationVersion.java b/release-tools/src/test/java/org/springframework/data/release/model/SimpleIterationVersion.java index 203c090..ad6292b 100644 --- a/release-tools/src/test/java/org/springframework/data/release/model/SimpleIterationVersion.java +++ b/release-tools/src/test/java/org/springframework/data/release/model/SimpleIterationVersion.java @@ -22,4 +22,13 @@ public class SimpleIterationVersion implements IterationVersion { private final Version version; private final Iteration iteration; + + /* + * (non-Javadoc) + * @see org.springframework.data.release.model.IterationVersion#isServiceIteration() + */ + @Override + public boolean isServiceIteration() { + return iteration.isServiceIteration(); + } } diff --git a/release-tools/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java b/release-tools/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java index f52650e..6ae3e7a 100644 --- a/release-tools/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java +++ b/release-tools/src/test/java/org/springframework/data/release/model/TrainsUnitTest.java @@ -39,4 +39,18 @@ public class TrainsUnitTest { public void addsNewlyAddedModule() { assertThat(ReleaseTrains.HOPPER.getModule(Projects.ENVERS), is(notNullValue())); } + + @Test + public void testname() { + + Iterable iterations = ReleaseTrains.HOPPER.getModuleIterations(Iteration.M1); + + for (ModuleIteration iteration : iterations) { + System.out.println(iteration); + } + + System.out.println(); + + iterations.forEach(System.out::println); + } } diff --git a/release-tools/src/test/java/org/springframework/data/release/model/UpdateInformationUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/model/UpdateInformationUnitTests.java new file mode 100644 index 0000000..5284f7d --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/model/UpdateInformationUnitTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.release.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Unit tests for {@link UpdateInformation}. + * + * @author Oliver Gierke + */ +public class UpdateInformationUnitTests { + + TrainIteration hopperM1 = new TrainIteration(ReleaseTrains.HOPPER, Iteration.M1); + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullTrainIteration() { + new UpdateInformation(null, Phase.CLEANUP); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullPhase() { + new UpdateInformation(hopperM1, null); + } + + @Test + public void exposesMilestoneRepositoryForMilestone() { + assertThat(new UpdateInformation(hopperM1, Phase.PREPARE).getRepository().getId(), is("spring-libs-milestone")); + } + + @Test + public void exposesReleaseRepositoryForGA() { + + Arrays.asList(Iteration.GA, Iteration.SR1).forEach(iteration -> { + TrainIteration trainIteration = new TrainIteration(ReleaseTrains.HOPPER, iteration); + assertThat(new UpdateInformation(trainIteration, Phase.PREPARE).getRepository().getId(), + is("spring-libs-release")); + }); + } + + @Test + public void calculatesProjectVersionToSetCorrectly() { + + UpdateInformation updateInformation = new UpdateInformation(hopperM1, Phase.PREPARE); + assertThat(updateInformation.getProjectVersionToSet(Projects.JPA).toString(), is("1.10.0.M1")); + + updateInformation = new UpdateInformation(hopperM1, Phase.CLEANUP); + assertThat(updateInformation.getProjectVersionToSet(Projects.JPA).toString(), is("1.10.0.BUILD-SNAPSHOT")); + } +} diff --git a/release-tools/src/test/resources/application-test.properties b/release-tools/src/test/resources/application-test.properties index 4982dd4..6dd9cd1 100644 --- a/release-tools/src/test/resources/application-test.properties +++ b/release-tools/src/test/resources/application-test.properties @@ -1,3 +1,14 @@ -io.work-dir=~/temp/shell-test +# Logging logging.level.org.springframework=WARN logging.level.org.springframework.data.release=INFO +logging.level.org.springframework.web.client=TRACE + +# Workspace +io.work-dir=~/temp/shell-test + +# Maven +maven.localRepository=~/temp/.m2/repository + +# Deployment +deployment.server.uri=https://repo.spring.io +deployment.repository-prefix=test-