From 4cd460fa98135e392e5671f662711cf561acb3a7 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 26 Jul 2017 17:25:00 +0200 Subject: [PATCH] #7 - Added support to update project information on spring.io. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces a command "sagan update …" to take a comma-separated list of release train names that are supported. The implementation will find the latest releases by inspecting the Git tags and the creating the necessary project metadata information. The code will then wipe the existing metadata and PUT the current information to the server. --- .../data/release/build/MavenArtifact.java | 23 ++- .../data/release/build/Repository.java | 26 +++ .../data/release/issues/Tickets.java | 59 +----- .../data/release/model/ArtifactVersion.java | 28 ++- .../data/release/model/Password.java | 2 + .../data/release/model/Version.java | 3 + .../release/sagan/DefaultSaganClient.java | 107 ++++++++++ .../data/release/sagan/DummySaganClient.java | 86 ++++++++ .../data/release/sagan/MaintainedVersion.java | 94 +++++++++ .../release/sagan/MaintainedVersions.java | 93 +++++++++ .../data/release/sagan/ProjectMetadata.java | 193 ++++++++++++++++++ .../data/release/sagan/SaganClient.java | 50 +++++ .../data/release/sagan/SaganCommands.java | 52 +++++ .../release/sagan/SaganConfiguration.java | 86 ++++++++ .../data/release/sagan/SaganOperations.java | 129 ++++++++++++ .../data/release/sagan/SaganProperties.java | 96 +++++++++ .../release/utils/HttpBasicCredentials.java | 5 +- .../release/utils/ListWrapperCollector.java | 99 +++++++++ .../sagan/MaintainedVersionUnitTests.java | 42 ++++ .../sagan/MaintainedVersionsUnitTests.java | 45 ++++ .../ProjectMetadataSerializationTests.java | 52 +++++ .../SaganOperationsIntegrationTests.java | 82 ++++++++ 22 files changed, 1387 insertions(+), 65 deletions(-) create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/SaganClient.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/SaganCommands.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/SaganOperations.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/sagan/SaganProperties.java create mode 100644 release-tools/src/main/java/org/springframework/data/release/utils/ListWrapperCollector.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java create mode 100644 release-tools/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java index 0dde854..dde07c7 100644 --- a/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java @@ -22,6 +22,7 @@ import lombok.Getter; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Project; import org.springframework.util.Assert; /** @@ -34,8 +35,8 @@ public class MavenArtifact { private static final GroupId GROUP_ID = GroupId.of("org.springframework.data"); - private final ModuleIteration module; - private final Repository repository; + private final Project project; + private final @Getter Repository repository; private final @Getter ArtifactVersion version; /** @@ -47,11 +48,22 @@ public class MavenArtifact { Assert.notNull(module, "Module iteration must not be null!"); - this.module = module; + this.project = module.getModule().getProject(); this.repository = new Repository(module.getIteration()); this.version = ArtifactVersion.of(module); } + public MavenArtifact(Project project, ArtifactVersion version) { + + this.project = project; + this.repository = new Repository(version); + this.version = version; + } + + public String getGroupId() { + return GROUP_ID.getValue(); + } + /** * Returns the Maven artifact identifier. * @@ -59,8 +71,9 @@ public class MavenArtifact { */ public String getArtifactId() { - String artifactId = String.format("spring-data-%s", module.getProject().getName().toLowerCase()); - return REST.equals(module.getProject()) ? artifactId.concat("-webmvc") : artifactId; + String artifactId = String.format("spring-data-%s", project.getName().toLowerCase()); + + return REST.equals(project) ? artifactId.concat("-webmvc") : artifactId; } public ArtifactVersion getNextDevelopmentVersion() { diff --git a/release-tools/src/main/java/org/springframework/data/release/build/Repository.java b/release-tools/src/main/java/org/springframework/data/release/build/Repository.java index 5119163..43a1a7e 100644 --- a/release-tools/src/main/java/org/springframework/data/release/build/Repository.java +++ b/release-tools/src/main/java/org/springframework/data/release/build/Repository.java @@ -17,6 +17,7 @@ package org.springframework.data.release.build; import lombok.Value; +import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.Iteration; import org.springframework.util.Assert; @@ -40,6 +41,14 @@ public class Repository { this.url = BASE.concat(iteration.isPublic() ? "release" : "milestone"); } + public Repository(ArtifactVersion version) { + + String suffix = getSuffixFor(version); + + this.id = ID_BASE.concat(suffix); + this.url = BASE.concat(suffix); + } + public String getSnapshotId() { return ID_BASE.concat("snapshot"); } @@ -47,4 +56,21 @@ public class Repository { public String getSnapshotUrl() { return BASE.concat("snapshot"); } + + private static String getSuffixFor(ArtifactVersion version) { + + if (version.isSnapshotVersion()) { + return "snapshot"; + } + + if (version.isMilestoneVersion()) { + return "milestone"; + } + + if (version.isReleaseVersion()) { + return "release"; + } + + throw new IllegalArgumentException(String.format("Unsupported ArtifactVersion %s!", version)); + } } diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java b/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java index 9b74234..c8760ba 100644 --- a/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java +++ b/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java @@ -18,22 +18,17 @@ package org.springframework.data.release.issues; import lombok.RequiredArgsConstructor; import lombok.Value; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Stream; import org.springframework.data.release.Streamable; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.utils.ListWrapperCollector; import org.springframework.util.StringUtils; /** @@ -115,56 +110,6 @@ public class Tickets implements Streamable { * @return */ public static Collector toTicketsCollector() { - - return new Collector, Tickets>() { - - /* - * (non-Javadoc) - * @see java.util.stream.Collector#supplier() - */ - @Override - public Supplier> supplier() { - return ArrayList::new; - } - - /* - * (non-Javadoc) - * @see java.util.stream.Collector#accumulator() - */ - @Override - public BiConsumer, Ticket> accumulator() { - return List::add; - } - - /* - * (non-Javadoc) - * @see java.util.stream.Collector#combiner() - */ - @Override - public BinaryOperator> combiner() { - return (left, right) -> { - left.addAll(right); - return left; - }; - } - - /* - * (non-Javadoc) - * @see java.util.stream.Collector#finisher() - */ - @Override - public Function, Tickets> finisher() { - return tickets -> new Tickets(tickets); - } - - /* - * (non-Javadoc) - * @see java.util.stream.Collector#characteristics() - */ - @Override - public Set characteristics() { - return Collections.emptySet(); - } - }; + return ListWrapperCollector.collectInto(Tickets::new); } } diff --git a/release-tools/src/main/java/org/springframework/data/release/model/ArtifactVersion.java b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactVersion.java index 57a507c..50097fa 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/ArtifactVersion.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/ArtifactVersion.java @@ -16,6 +16,7 @@ package org.springframework.data.release.model; import lombok.EqualsAndHashCode; +import lombok.Getter; import org.springframework.util.Assert; @@ -35,7 +36,7 @@ public class ArtifactVersion implements Comparable { SNAPSHOT_SUFFIX); private final Version version; - private final String suffix; + private final @Getter String suffix; /** * Creates a new {@link ArtifactVersion} from the given logical {@link Version}. @@ -102,6 +103,10 @@ public class ArtifactVersion implements Comparable { return new ArtifactVersion(version, iteration.getName()); } + public boolean isVersionWithin(Version version) { + return this.version.toMajorMinorBugfix().startsWith(version.toString()); + } + /** * Returns the release version for the current one. * @@ -138,6 +143,14 @@ public class ArtifactVersion implements Comparable { return suffix.matches(MILESTONE_SUFFIX); } + public boolean isSnapshotVersion() { + return suffix.matches(SNAPSHOT_SUFFIX); + } + + public boolean isBugFixVersion() { + return isReleaseVersion() && !version.toMajorMinorBugfix().endsWith("0"); + } + /** * Returns the next development version to be used for the current release version, which means next minor for GA * versions and next bug fix for service releases. Will return the current version as snapshot otherwise. @@ -172,6 +185,19 @@ public class ArtifactVersion implements Comparable { return suffix.equals(SNAPSHOT_SUFFIX) ? this : new ArtifactVersion(version, SNAPSHOT_SUFFIX); } + public String getReleaseTrainSuffix() { + + if (isSnapshotVersion() || isMilestoneVersion()) { + return suffix; + } + + if (isBugFixVersion()) { + return "SR" + version.getBugfix(); + } + + return "GA"; + } + /* * (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Password.java b/release-tools/src/main/java/org/springframework/data/release/model/Password.java index e06b736..1dbad3c 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Password.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Password.java @@ -31,6 +31,8 @@ import org.springframework.util.Assert; @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class Password implements Masked { + public static Password NONE = new Password(""); + @Getter(AccessLevel.NONE) String value; /** diff --git a/release-tools/src/main/java/org/springframework/data/release/model/Version.java b/release-tools/src/main/java/org/springframework/data/release/model/Version.java index 1ea2452..9a2576f 100644 --- a/release-tools/src/main/java/org/springframework/data/release/model/Version.java +++ b/release-tools/src/main/java/org/springframework/data/release/model/Version.java @@ -1,5 +1,7 @@ package org.springframework.data.release.model; +import lombok.Getter; + import java.util.ArrayList; import java.util.List; @@ -11,6 +13,7 @@ import org.springframework.util.StringUtils; * * @author Oliver Gierke */ +@Getter public class Version implements Comparable { private final int major; diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java b/release-tools/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java new file mode 100644 index 0000000..3ba36df --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java @@ -0,0 +1,107 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import net.minidev.json.JSONArray; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.release.model.Project; +import org.springframework.data.release.utils.Logger; +import org.springframework.web.client.RestOperations; + +import com.jayway.jsonpath.JsonPath; + +/** + * Sagan client to interact with the Sagan instance defined through {@link SaganProperties}. + * + * @author Oliver Gierke + */ +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +class DefaultSaganClient implements SaganClient { + + RestOperations operations; + SaganProperties properties; + Logger logger; + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#getProjectMetadata(org.springframework.data.release.sagan.MaintainedVersion) + */ + @Override + public String getProjectMetadata(MaintainedVersion version) { + + URI resource = properties.getProjectMetadataResource(version); + + logger.log(version.getProject(), "Getting project metadata for version %s from %s…", version.getVersion(), + resource); + + return operations.getForObject(resource, String.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#getProjectMetadata(org.springframework.data.release.model.Project) + */ + @Override + public String getProjectMetadata(Project project) { + + URI resource = properties.getProjectMetadataResource(project); + + logger.log(project, "Getting project metadata from %s…", resource); + + return operations.getForObject(resource, String.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#updateProjectMetadata(org.springframework.data.release.model.Project, java.util.List) + */ + @Override + public void updateProjectMetadata(Project project, MaintainedVersions versions) { + + URI resource = properties.getProjectMetadataResource(project); + + String versionsString = versions.stream()// + .map(MaintainedVersion::getVersion)// + .map(Object::toString) // + .collect(Collectors.joining(", ")); + + logger.log(project, "Updating project metadata to %s via %s…", versionsString, resource); + + // Delete all existing versions first + Arrays.stream(JsonPath.compile("$..version"). read(getProjectMetadata(project)).toArray())// + .map(version -> properties.getProjectMetadataResource(project, version.toString()))// + .peek(uri -> logger.log(project, "Deleting existing project metadata at %s…", uri)) // + .forEach(uri -> operations.delete(uri)); + + logger.log(project, "Writing project metadata for versions %s!", versionsString); + + // Write new ones + List payload = versions.stream() // + .map(version -> new ProjectMetadata(version, versions)) // + .collect(Collectors.toList()); + + operations.put(resource, payload); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java b/release-tools/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java new file mode 100644 index 0000000..0c8d4d7 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java @@ -0,0 +1,86 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.data.release.model.Project; +import org.springframework.data.release.utils.Logger; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectWriter; + +/** + * Dummy implementation of {@link SaganClient} to just dump the request payloads into {@link System#out} instead of + * communicating with a real server. + * + * @author Oliver Gierke + */ +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +class DummySaganClient implements SaganClient { + + Logger logger; + ObjectWriter mapper; + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#updateProjectMetadata(org.springframework.data.release.model.Project, org.springframework.data.release.sagan.MaintainedVersions) + */ + @Override + public void updateProjectMetadata(Project project, MaintainedVersions versions) { + + logger.log(project, "Updating released version on Sagan to %s!", versions); + + List payload = versions.stream() // + .map(it -> new ProjectMetadata(it, versions)) // + .collect(Collectors.toList()); + + try { + System.out.println(mapper.writeValueAsString(payload)); + } catch (JsonProcessingException o_O) { + throw new RuntimeException(o_O); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#getProjectMetadata(org.springframework.data.release.sagan.MaintainedVersion) + */ + @Override + public String getProjectMetadata(MaintainedVersion version) { + + try { + return mapper.writeValueAsString(new ProjectMetadata(version, MaintainedVersions.of(version))); + } catch (JsonProcessingException o_O) { + throw new RuntimeException(o_O); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.release.sagan.SaganClient#getProjectMetadata(org.springframework.data.release.model.Project) + */ + @Override + public String getProjectMetadata(Project project) { + return null; + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java b/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java new file mode 100644 index 0000000..1a44bda --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java @@ -0,0 +1,94 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.Value; + +import java.util.Comparator; +import java.util.stream.Stream; + +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Module; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Train; +import org.springframework.util.Assert; + +/** + * Represents a maintained project version to be represented in Sagan. + * + * @author Oliver Gierke + */ +@Value(staticConstructor = "of") +class MaintainedVersion implements Comparable { + + Project project; + ArtifactVersion version; + Train train; + + /** + * Creates a new {@link MaintainedVersion} representing the snaphot version of the given {@link Module} and + * {@link Train}. + * + * @param module must not be {@literal null}. + * @param train must not be {@literal null}. + * @return + */ + public static MaintainedVersion snapshot(Module module, Train train) { + + Assert.notNull(module, "Module must not be null!"); + Assert.notNull(train, "Train must not be null!"); + + ArtifactVersion snapshotVersion = ArtifactVersion.of(module.getVersion()).getSnapshotVersion(); + + return MaintainedVersion.of(module.getProject(), snapshotVersion, train); + } + + /** + * Returns a {@link Stream} of {@link MaintainedVersion} that are related to the current one. In case the current one + * is not a snapshot version in the first place, the next development version is added, too. + * + * @return + */ + Stream all() { + return version.isSnapshotVersion() ? Stream.of(this) : Stream.of(nextDevelopmentVersion(), this); + } + + /** + * Creates the {@link MaintainedVersion} for the next development version of the current one. + * + * @return + */ + MaintainedVersion nextDevelopmentVersion() { + return MaintainedVersion.of(project, version.getNextDevelopmentVersion(), train); + } + + /* + * (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(MaintainedVersion o) { + return Comparator.comparing(MaintainedVersion::getVersion).compare(this, o); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return String.format("%s - %s - %s", project.getName(), train.getName(), version); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java b/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java new file mode 100644 index 0000000..cf12184 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java @@ -0,0 +1,93 @@ +/* + * 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.sagan; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.Arrays; +import java.util.Comparator; +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; + +/** + * Wrapper type for a {@link List} of {@link MaintainedVersion}. + * + * @author Oliver Gierke + */ +@ToString +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +class MaintainedVersions implements Streamable { + + private final List versions; + + /** + * Creates a new {@link MaintainedVersions} with the given {@link MaintainedVersion}s in descending order (more recent + * versions first). + * + * @param versions must not be {@literal null}. + * @return + */ + static MaintainedVersions of(List versions) { + + return new MaintainedVersions(versions.stream()// + .sorted(Comparator.reverseOrder())// + .collect(Collectors.toList())); + } + + /** + * Creates a new {@link MaintainedVersions} with the given {@link MaintainedVersion}s in descending order (more recent + * versions first). + * + * @param versions must not be {@literal null}. + * @return + */ + static MaintainedVersions of(MaintainedVersion... versions) { + return MaintainedVersions.of(Arrays.asList(versions)); + } + + /** + * Returns whether the given {@link MaintainedVersion} is the main version of the current set. + * + * @param version must not be {@literal null}. + * @return + */ + boolean isMainVersion(MaintainedVersion version) { + + if (!version.getVersion().isReleaseVersion()) { + return false; + } + + return versions.stream() // + .map(it -> it.getVersion()) // + .filter(ArtifactVersion::isReleaseVersion) // + .findFirst() // + .map(it -> it.equals(version.getVersion())) // + .orElse(false); + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return versions.iterator(); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java b/release-tools/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java new file mode 100644 index 0000000..84bf52d --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java @@ -0,0 +1,193 @@ +/* + * Copyright 2017 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.sagan; + +import java.util.Locale; + +import org.springframework.data.release.build.MavenArtifact; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Projects; +import org.springframework.util.Assert; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +/** + * Simple value object to create payloads to update project metadata in Sagan. + * + * @author Oliver Gierke + */ +@JsonInclude(Include.NON_NULL) +class ProjectMetadata { + + private static String DOCS_BASE = "https://docs.spring.io/spring-data/%s/docs/{version}"; + private static String DOCS = DOCS_BASE.concat("/reference/html/"); + private static String JAVADOC = DOCS_BASE.concat("/api"); + + private final MaintainedVersions versions; + private final MaintainedVersion version; + private final MavenArtifact artifact; + + /** + * Creates a new {@link ProjectMetadata} instace from the given {@link MaintainedVersion} in the context of the + * {@link MaintainedVersions}. + * + * @param version must not be {@literal null}. + * @param versions must not be {@literal null}. + */ + public ProjectMetadata(MaintainedVersion version, MaintainedVersions versions) { + + Assert.notNull(version, "MaintainedVersion must not be null!"); + Assert.notNull(versions, "MaintainedVersions must not be null!"); + + this.version = version; + this.versions = versions; + this.artifact = new MavenArtifact(version.getProject(), version.getVersion()); + } + + /** + * Returns the release status used on Sagan. + * + * @return + */ + public String getReleaseStatus() { + + ArtifactVersion artifactVersion = version.getVersion(); + + if (artifactVersion.isReleaseVersion()) { + return "GENERAL_AVAILABILITY"; + } + + if (artifactVersion.isMilestoneVersion()) { + return "PRERELEASE"; + } + + if (artifactVersion.isSnapshotVersion()) { + return "SNAPSHOT"; + } + + throw new IllegalStateException(); + } + + /** + * Returns the group identifier of the release. + * + * @return + */ + public String getGroupId() { + return artifact.getGroupId(); + } + + /** + * Returns the artifact identifier. + * + * @return + */ + public String getArtifactId() { + + if (Projects.BUILD.equals(version.getProject())) { + return "spring-data-releasetrain"; + } + + return artifact.getArtifactId(); + } + + /** + * Returns whether the version is the most current one. + * + * @return + */ + public Boolean getCurrent() { + return versions.isMainVersion(version) ? true : null; + } + + /** + * Returns the reference documentation URL for non-snapshot versions and not the build project. + * + * @return + */ + public String getRefDocUrl() { + + return version.getVersion().isSnapshotVersion() || Projects.BUILD.equals(version.getProject()) // + ? "" // + : String.format(DOCS, version.getProject().getName().toLowerCase(Locale.US)); + } + + /** + * Returns the JavaDoc URL for non-snapshot versions and not the build project. + * + * @return + */ + public String getApiDocUrl() { + + return version.getVersion().isSnapshotVersion() || Projects.BUILD.equals(version.getProject()) // + ? "" // + : String.format(JAVADOC, version.getProject().getName().toLowerCase(Locale.US)); + } + + public Repository getRepository() { + return new Repository(); + } + + /** + * Return the version to use. For the build project thats the release train name, for everything else the artifact + * version. + * + * @return + */ + public String getVersion() { + + if (Projects.BUILD.equals(version.getProject())) { + return String.format("%s-%s", version.getTrain().getName(), version.getVersion().getReleaseTrainSuffix()); + } + + return version.getVersion().toString(); + } + + public class Repository { + + public String getId() { + return artifact.getRepository().getId(); + } + + public boolean isSnapshotsEnabled() { + return version.getVersion().isSnapshotVersion(); + } + + public String getUrl() { + return artifact.getRepository().getUrl(); + } + + public String getName() { + + String result = "Spring "; + + if (version.getVersion().isMilestoneVersion()) { + return result.concat("Milestones"); + } + + if (version.getVersion().isReleaseVersion()) { + return result.concat("Releases"); + } + + if (isSnapshotsEnabled()) { + return result.concat("Snapshots"); + } + + throw new IllegalStateException(); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/SaganClient.java b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganClient.java new file mode 100644 index 0000000..b818cc8 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganClient.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 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.sagan; + +import org.springframework.data.release.model.Project; + +/** + * Interface to abstract the actual interaction with the Sagan server. + * + * @author Oliver Gierke + */ +interface SaganClient { + + /** + * Returns the project metadata for the given {@link MaintainedVersion}. + * + * @param version must not be {@literal null}. + * @return + */ + String getProjectMetadata(MaintainedVersion version); + + /** + * Returns all project release metadata for the given {@link Project}. + * + * @param project must not be {@literal null}. + * @return + */ + String getProjectMetadata(Project project); + + /** + * Updates the project metadata for the given {@link Project} to the given {@link MaintainedVersions}. + * + * @param project must not be {@literal null}. + * @param versions must not be {@literal null}. + */ + void updateProjectMetadata(Project project, MaintainedVersions versions); +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/SaganCommands.java b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganCommands.java new file mode 100644 index 0000000..a267c33 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganCommands.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.data.release.CliComponent; +import org.springframework.data.release.TimedCommand; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; +import org.springframework.stereotype.Component; + +/** + * Operations for Sagan interaction. + * + * @author Oliver Gierke + */ +@Component +@CliComponent +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +class SaganCommands extends TimedCommand { + + SaganOperations sagan; + + @CliCommand("sagan update") + public void updateProjectInformation(@CliOption(key = "", mandatory = true) String trains) { + + sagan.updateProjectMetadata(Stream.of(trains.split(","))// + .map(train -> ReleaseTrains.getTrainByName(train)) // + .collect(Collectors.toList())); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java new file mode 100644 index 0000000..5bc3f38 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java @@ -0,0 +1,86 @@ +/* + * Copyright 2017 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.sagan; + +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.git.GitOperations; +import org.springframework.data.release.utils.Logger; +import org.springframework.http.HttpHeaders; +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 for the Sagan interaction subsystem. + * + * @author Oliver Gierke + */ +@Configuration +class SaganConfiguration { + + @Autowired SaganProperties properties; + @Autowired Logger logger; + + @Bean + public SaganOperations saganOperations(GitOperations operations) { + return new SaganOperations(operations, saganClient(), logger); + } + + @Bean + SaganClient saganClient() { + + return new DefaultSaganClient(saganRestTemplate(), properties, logger); + // return new DummySaganClient(logger, new ObjectMapper().writerWithDefaultPrettyPrinter()); + } + + @Bean + RestTemplate saganRestTemplate() { + + RestTemplate template = new RestTemplate(); + template.setInterceptors(Arrays.asList(new AuthenticatingClientHttpRequestInterceptor(properties))); + + return template; + } + + @RequiredArgsConstructor + private static class AuthenticatingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + private final @NonNull SaganProperties properties; + + /* + * (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(HttpHeaders.AUTHORIZATION, properties.getCredentials().toString()); + + return execution.execute(request, body); + } + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/SaganOperations.java b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganOperations.java new file mode 100644 index 0000000..c51d2e2 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganOperations.java @@ -0,0 +1,129 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.data.release.git.GitOperations; +import org.springframework.data.release.git.Tag; +import org.springframework.data.release.model.Module; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.Train; +import org.springframework.data.release.model.Version; +import org.springframework.data.release.utils.ListWrapperCollector; +import org.springframework.data.release.utils.Logger; +import org.springframework.util.Assert; + +/** + * @author Oliver Gierke + */ +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +class SaganOperations { + + private static final List TO_FILTER = Arrays.asList(Projects.COMMONS, // + Projects.GEODE, // + Projects.KEY_VALUE); + + GitOperations git; + SaganClient client; + Logger logger; + + /** + * Updates the project metadata for the modules in the given release {@link Train}s. + * + * @param trains must not be {@literal null}. + */ + void updateProjectMetadata(Train... trains) { + + Assert.notNull(trains, "Trains must not be null!"); + + updateProjectMetadata(Arrays.asList(trains)); + } + + void updateProjectMetadata(List trains) { + + Assert.notNull(trains, "Trains must not be null!"); + + findVersions(trains).forEach(client::updateProjectMetadata); + } + + /** + * Returns all {@link MaintainedVersions} grouped by {@link Project} for the given release {@link Train}. + * + * @param trains must not be {@literal null}. + * @return will never be {@literal null}. + */ + Map findVersions(Train... trains) { + + Assert.notNull(trains, "Trains must not be null!"); + + return findVersions(Arrays.asList(trains)); + } + + Map findVersions(List trains) { + + Assert.notNull(trains, "Trains must not be null!"); + + return trains.stream() // + .flatMap(train -> train.stream()// + .filter(module -> !TO_FILTER.contains(module.getProject())) // + .map(module -> getLatestVersion(module, train)) // + .flatMap(MaintainedVersion::all)) // + .collect( + Collectors.groupingBy(it -> it.getProject(), ListWrapperCollector.collectInto(MaintainedVersions::of))); + } + + private MaintainedVersion getLatestVersion(Module module, Train train) { + + Project project = module.getProject(); + + MaintainedVersion version = git.getTags(project).stream()// + .filter(tag -> matches(tag, module.getVersion())) // + .sorted(Comparator.reverseOrder()) // + .findFirst() // + .flatMap(tag -> tag.toArtifactVersion()) // + .map(it -> MaintainedVersion.of(module.getProject(), it, train)) // + .orElseGet(() -> MaintainedVersion.snapshot(module, train)); + + logger.log(project, "Found version %s for train %s!", version.getVersion(), train.getName()); + + return version; + } + + /** + * Returns whether the given {@link Tag} is one that logically belongs to the given version. + * + * @param tag must not be {@literal null}. + * @param version must not be {@literal null}. + * @return + */ + private static boolean matches(Tag tag, Version version) { + + return tag.toArtifactVersion()// + .map(it -> it.isVersionWithin(version))// + .orElse(false); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/sagan/SaganProperties.java b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganProperties.java new file mode 100644 index 0000000..3910cd5 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/sagan/SaganProperties.java @@ -0,0 +1,96 @@ +/* + * Copyright 2017 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.sagan; + +import lombok.Setter; + +import java.net.URI; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.data.release.model.Password; +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.utils.HttpBasicCredentials; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.web.util.UriTemplate; + +/** + * Configuration properties for the Sagan instance to talk to. + * + * @author Oliver Gierke + */ +@Component +@ConfigurationProperties(prefix = "sagan") +class SaganProperties { + + private static String SAGAN_PROJECT_METADATA = "https://spring.io/project_metadata/{project}/releases"; + private static String SAGAN_PROJECT_VERSION_METADATA = SAGAN_PROJECT_METADATA.concat("/{version}"); + + @Setter String key; + + /** + * Returns the {@link HttpBasicCredentials} to be used when talking to the server. + * + * @return + */ + HttpBasicCredentials getCredentials() { + return new HttpBasicCredentials(key, Password.NONE); + } + + /** + * Returns the URI to the resource exposing the project metadata for the given {@link Project}. + * + * @param project must not be {@literal null}. + * @return + */ + URI getProjectMetadataResource(Project project) { + + Assert.notNull(project, "Project must not be null!"); + + return new UriTemplate(SAGAN_PROJECT_METADATA).expand(getProjectPathSegment(project)); + } + + /** + * Returns the URI to the resource exposing the project metadata for the given {@link Project} and version + * {@link String}. + * + * @param project must not be {@literal null}. + * @param version must not be {@literal null}. + * @return + */ + URI getProjectMetadataResource(Project project, String version) { + + Assert.notNull(project, "Project must not be null!"); + Assert.hasText(version, "Version must not be null!"); + + return new UriTemplate(SAGAN_PROJECT_VERSION_METADATA).expand(getProjectPathSegment(project), version); + } + + /** + * Returns the {@link URI} to the resource exposing the project metadata for the given {@link MaintainedVersion}. + * + * @param version must not be {@literal null}. + * @return + */ + URI getProjectMetadataResource(MaintainedVersion version) { + return getProjectMetadataResource(version.getProject(), version.getVersion().toString()); + } + + private static String getProjectPathSegment(Project project) { + return Projects.BUILD.equals(project) ? "spring-data" : project.getFolderName(); + } +} diff --git a/release-tools/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java b/release-tools/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java index 68d86ff..39f325c 100644 --- a/release-tools/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java +++ b/release-tools/src/main/java/org/springframework/data/release/utils/HttpBasicCredentials.java @@ -15,6 +15,7 @@ */ package org.springframework.data.release.utils; +import lombok.NonNull; import lombok.Value; import java.nio.charset.StandardCharsets; @@ -28,8 +29,8 @@ import org.springframework.data.release.model.Password; @Value public class HttpBasicCredentials { - private final String username; - private final Password password; + private final @NonNull String username; + private final @NonNull Password password; /* * (non-Javadoc) diff --git a/release-tools/src/main/java/org/springframework/data/release/utils/ListWrapperCollector.java b/release-tools/src/main/java/org/springframework/data/release/utils/ListWrapperCollector.java new file mode 100644 index 0000000..3baeb10 --- /dev/null +++ b/release-tools/src/main/java/org/springframework/data/release/utils/ListWrapperCollector.java @@ -0,0 +1,99 @@ +/* + * Copyright 2017 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.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import org.springframework.util.Assert; + +/** + * Base class to easily create a {@link Collector} to create a collection wrapper type. + * + * @author Oliver Gierke + */ +public abstract class ListWrapperCollector implements Collector, S> { + + /** + * Creates a new {@link Collector} to eventually apply the given function to turn the {@link List} of elements into a + * wrapper type. + * + * @param function must not be {@literal null}. + * @return + */ + public static ListWrapperCollector collectInto(Function, S> function) { + + Assert.notNull(function, "Finisher function must not be null!"); + + return new ListWrapperCollector() { + + /* + * (non-Javadoc) + * @see java.util.stream.Collector#finisher() + */ + @Override + public Function, S> finisher() { + return function; + } + }; + } + + /* + * (non-Javadoc) + * @see java.util.stream.Collector#supplier() + */ + @Override + public Supplier> supplier() { + return ArrayList::new; + } + + /* + * (non-Javadoc) + * @see java.util.stream.Collector#accumulator() + */ + @Override + public BiConsumer, T> accumulator() { + return List::add; + } + + /* + * (non-Javadoc) + * @see java.util.stream.Collector#combiner() + */ + @Override + public BinaryOperator> combiner() { + return (left, right) -> { + left.addAll(right); + return left; + }; + } + + /* + * (non-Javadoc) + * @see java.util.stream.Collector#characteristics() + */ + @Override + public Set characteristics() { + return Collections.emptySet(); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java new file mode 100644 index 0000000..60d7aca --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 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.sagan; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; + +/** + * Unit tests for {@link MaintainedVersion}. + * + * @author Oliver Gierke + */ +public class MaintainedVersionUnitTests { + + @Test + public void newerVersionIsOrderedFirst() { + + MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.0.RELEASE"), + ReleaseTrains.INGALLS); + MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.0.RELEASE"), + ReleaseTrains.HOPPER); + + assertThat(ingalls.compareTo(hopper)).isGreaterThan(0); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java b/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java new file mode 100644 index 0000000..5c6dd1d --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 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.sagan; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; + +/** + * Unit tests for {@link MaintainedVersions}. + * + * @author Oliver Gierke + */ +public class MaintainedVersionsUnitTests { + + @Test + public void considersMostRecentReleaseVersionTheMainOne() { + + MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.0.RELEASE"), + ReleaseTrains.INGALLS); + MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.0.RELEASE"), + ReleaseTrains.HOPPER); + + MaintainedVersions versions = MaintainedVersions.of(ingalls, hopper); + + assertThat(versions.isMainVersion(ingalls)).isTrue(); + assertThat(versions.isMainVersion(hopper)).isFalse(); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java b/release-tools/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java new file mode 100644 index 0000000..f8f0323 --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017 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.sagan; + +import org.junit.Test; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +/** + * Tests for serialization of {@link ProjectMetadata}. + * + * @author Oliver Gierke + */ +public class ProjectMetadataSerializationTests { + + @Test + public void serializesMaintainedVersionsIntoProjectMetadata() throws Exception { + + ObjectWriter mapper = new ObjectMapper().writerWithDefaultPrettyPrinter(); + + MaintainedVersion kay = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("2.0.0.RC1"), ReleaseTrains.KAY); + MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.5.RELEASE"), + ReleaseTrains.INGALLS); + MaintainedVersion ingallsSnapshot = ingalls.nextDevelopmentVersion(); + MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.8.RELEASE"), + ReleaseTrains.HOPPER); + + MaintainedVersions versions = MaintainedVersions.of(kay, ingalls, ingallsSnapshot, hopper); + + System.out.println(mapper.writeValueAsString(new ProjectMetadata(kay, versions))); + System.out.println(mapper.writeValueAsString(new ProjectMetadata(ingallsSnapshot, versions))); + System.out.println(mapper.writeValueAsString(new ProjectMetadata(ingalls, versions))); + System.out.println(mapper.writeValueAsString(new ProjectMetadata(hopper, versions))); + } +} diff --git a/release-tools/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java b/release-tools/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java new file mode 100644 index 0000000..0eaaa10 --- /dev/null +++ b/release-tools/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2017 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.sagan; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.release.AbstractIntegrationTests; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; + +/** + * Integration tests for {@link SaganOperations}. + * + * @author Oliver Gierke + */ +public class SaganOperationsIntegrationTests extends AbstractIntegrationTests { + + @Autowired SaganOperations sagan; + @Autowired SaganClient client; + + @Test + public void detectVersionsToUpdate() { + + sagan.findVersions(ReleaseTrains.LOVELACE, ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER) + .forEach((project, versions) -> { + + System.out.println(project.getName()); + System.out.println("-----"); + + versions.forEach(version -> { + + String output = version.toString(); + System.out.println(versions.isMainVersion(version) ? output.concat(" (main release)") : output); + }); + + System.out.println(); + }); + } + + @Test + public void updateVersions() { + sagan.updateProjectMetadata(ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER); + } + + @Test + public void updateJpa() { + + MaintainedVersions versions = sagan.findVersions(ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER) + .get(Projects.JPA); + + System.out.println(versions); + + client.updateProjectMetadata(Projects.JPA, versions); + } + + @Test + public void getJpa() { + System.out.println(client.getProjectMetadata(Projects.JPA)); + } + + @Test + public void updateBuild() { + + MaintainedVersions versions = sagan.findVersions(ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER) + .get(Projects.BUILD); + + client.updateProjectMetadata(Projects.BUILD, versions); + } +}