diff --git a/src/main/java/org/springframework/data/release/git/GitOperations.java b/src/main/java/org/springframework/data/release/git/GitOperations.java index 86eb625..c34de4e 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -21,12 +21,15 @@ import lombok.experimental.FieldDefaults; import java.io.File; import java.io.IOException; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.TimeZone; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -38,10 +41,12 @@ import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus; import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; @@ -67,6 +72,7 @@ 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.data.util.Streamable; import org.springframework.plugin.core.PluginRegistry; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -309,11 +315,38 @@ public class GitOperations { return doWithGit(project, git -> { return new VersionTags(git.tagList().call().stream()// - .map(ref -> Tag.of(ref.getName()))// + .map(ref -> { + + RevCommit commit = getCommit(git.getRepository(), ref); + + PersonIdent authorIdent = commit.getAuthorIdent(); + Date authorDate = authorIdent.getWhen(); + TimeZone authorTimeZone = authorIdent.getTimeZone(); + LocalDateTime localDate = authorDate.toInstant().atZone(authorTimeZone.toZoneId()).toLocalDateTime(); + + return Tag.of(ref.getName(), localDate); + })// .collect(Collectors.toList())); }); } + private RevCommit getCommit(Repository repository, Ref ref) { + + return doWithGit(repository, git -> { + + Ref peeledRef = git.getRepository().getRefDatabase().peel(ref); + LogCommand log = git.log(); + if (peeledRef.getPeeledObjectId() != null) { + log.add(peeledRef.getPeeledObjectId()); + } else { + log.add(ref.getObjectId()); + } + + return Streamable.of(log.call()).stream().findFirst() + .orElseThrow(() -> new IllegalStateException("Cannot resolve commit for " + ref)); + }); + } + /** * Retrieve a list of remote branches where their related ticket is resolved. * @@ -788,9 +821,16 @@ public class GitOperations { private T doWithGit(Project project, GitCallback callback) { try (Git git = new Git(getRepository(project))) { - T result = callback.doWithGit(git); - Thread.sleep(100); - return result; + return callback.doWithGit(git); + } catch (Exception o_O) { + throw new RuntimeException(o_O); + } + } + + private T doWithGit(Repository repository, GitCallback callback) { + + try (Git git = new Git(repository)) { + return callback.doWithGit(git); } catch (Exception o_O) { throw new RuntimeException(o_O); } diff --git a/src/main/java/org/springframework/data/release/git/Tag.java b/src/main/java/org/springframework/data/release/git/Tag.java index 6ece071..0975385 100644 --- a/src/main/java/org/springframework/data/release/git/Tag.java +++ b/src/main/java/org/springframework/data/release/git/Tag.java @@ -17,34 +17,43 @@ package org.springframework.data.release.git; import lombok.AccessLevel; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.time.LocalDateTime; import java.util.Optional; import org.springframework.data.release.model.ArtifactVersion; /** * Value object to represent an SCM tag. - * + * * @author Oliver Gierke + * @author Mark Paluch */ @EqualsAndHashCode @RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@Getter public class Tag implements Comparable { private final String name; + private final LocalDateTime creationDate; public static Tag of(String source) { + return of(source, LocalDateTime.now()); + } + + public static Tag of(String source, LocalDateTime creationDate) { int slashIndex = source.lastIndexOf('/'); - return new Tag(source.substring(slashIndex == -1 ? 0 : slashIndex + 1)); + return new Tag(source.substring(slashIndex == -1 ? 0 : slashIndex + 1), creationDate); } /** * Returns the part of the name of the tag that is suitable to derive a version from the tag. Will transparently strip * a {@code v} prefix from the name. - * + * * @return */ private String getVersionSource() { @@ -66,15 +75,15 @@ public class Tag implements Comparable { /** * Creates a new {@link Tag} for the given {@link ArtifactVersion} based on the format of the current one. - * + * * @param version * @return */ public Tag createNew(ArtifactVersion version) { - return new Tag(name.startsWith("v") ? "v".concat(version.toString()) : version.toString()); + return new Tag(name.startsWith("v") ? "v".concat(version.toString()) : version.toString(), LocalDateTime.now()); } - /* + /* * (non-Javadoc) * @see java.lang.Object#toString() */ @@ -83,7 +92,7 @@ public class Tag implements Comparable { return name; } - /* + /* * (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ diff --git a/src/main/java/org/springframework/data/release/model/ArtifactVersion.java b/src/main/java/org/springframework/data/release/model/ArtifactVersion.java index 13abae9..fabbcb2 100644 --- a/src/main/java/org/springframework/data/release/model/ArtifactVersion.java +++ b/src/main/java/org/springframework/data/release/model/ArtifactVersion.java @@ -272,4 +272,8 @@ public class ArtifactVersion implements Comparable { private String getSnapshotSuffix() { return modifierFormat ? SNAPSHOT_MODIFIER : SNAPSHOT_SUFFIX; } + + public String getGeneration() { + return String.format("%s.%s.x", version.getMajor(), version.getMinor()); + } } diff --git a/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java b/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java index 875d898..848bc2d 100644 --- a/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java +++ b/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java @@ -21,6 +21,7 @@ import lombok.experimental.FieldDefaults; import net.minidev.json.JSONArray; import java.net.URI; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -67,9 +68,9 @@ class DefaultSaganClient implements SaganClient { @Override public String getProjectMetadata(Project project) { - URI resource = properties.getProjectMetadataResource(project); + URI resource = properties.getProjectReleasesResource(project); - logger.log(project, "Getting project metadata from %s…", resource); + logger.log(project, "Getting project releases from %s…", resource); return operations.getForObject(resource, String.class); } @@ -81,28 +82,44 @@ class DefaultSaganClient implements SaganClient { @Override public void updateProjectMetadata(Project project, MaintainedVersions versions) { - URI resource = properties.getProjectMetadataResource(project); + URI resource = properties.getProjectReleasesResource(project); String versionsString = versions.stream()// .map(MaintainedVersion::getVersion)// .map(Object::toString) // .collect(Collectors.joining(", ")); + List versionsToRetain = versions.stream() // + .map(version -> new ProjectMetadata(version, versions)).map(ProjectMetadata::getVersion) + .collect(Collectors.toList()); + List versionsInSagan = new ArrayList<>(); - logger.log(project, "Updating project metadata to %s via %s…", versionsString, resource); + logger.log(project, "Updating project version 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(operations::delete); + deleteExistingVersions(project, versionsToRetain, versionsInSagan); - logger.log(project, "Writing project metadata for versions %s!", versionsString); + logger.log(project, "Writing project versions %s.", versionsString); // Write new ones - List payload = versions.stream() // - .map(version -> new ProjectMetadata(version, versions)) // - .collect(Collectors.toList()); + createVersions(versions, resource, versionsInSagan); + } - operations.put(resource, payload); + private void createVersions(MaintainedVersions versions, URI resource, List versionsInSagan) { + + versions.stream() // + .map(it -> new ProjectMetadata(it, versions)) // + .filter(version -> !versionsInSagan.contains(version.getVersion())) // + .forEach(payload -> operations.postForObject(resource, payload, String.class)); + } + + private void deleteExistingVersions(Project project, List versionsToRetain, List versionsInSagan) { + + Arrays.stream(JsonPath.compile("$..version"). read(getProjectMetadata(project)).toArray())// + .map(Object::toString) // + .peek(versionsInSagan::add) // + .filter(version -> !versionsToRetain.contains(version)) // + .map(version -> properties.getProjectReleaseResource(project, version))// + .peek(uri -> logger.log(project, "Deleting existing project version at %s…", uri)) // + .forEach(operations::delete); } } diff --git a/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java b/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java index b179508..cb0cd04 100644 --- a/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java +++ b/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java @@ -19,6 +19,7 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -31,8 +32,9 @@ 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 + * @author Mark Paluch */ @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) @@ -61,7 +63,7 @@ class DummySaganClient implements SaganClient { } } - /* + /* * (non-Javadoc) * @see org.springframework.data.release.sagan.SaganClient#getProjectMetadata(org.springframework.data.release.sagan.MaintainedVersion) */ @@ -69,13 +71,13 @@ class DummySaganClient implements SaganClient { public String getProjectMetadata(MaintainedVersion version) { try { - return mapper.writeValueAsString(new ProjectMetadata(version, MaintainedVersions.of(version))); + return mapper.writeValueAsString(new ProjectMetadata(version, MaintainedVersions.of(Collections.emptyList()))); } 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) */ diff --git a/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java b/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java index f5c85f6..07ef829 100644 --- a/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java +++ b/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java @@ -17,6 +17,7 @@ package org.springframework.data.release.sagan; import lombok.Value; +import java.time.LocalDate; import java.util.Comparator; import java.util.stream.Stream; @@ -24,12 +25,14 @@ 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.lang.Nullable; import org.springframework.util.Assert; /** * Represents a maintained project version to be represented in Sagan. - * + * * @author Oliver Gierke + * @author Mark Paluch */ @Value(staticConstructor = "of") class MaintainedVersion implements Comparable { @@ -37,16 +40,19 @@ class MaintainedVersion implements Comparable { Project project; ArtifactVersion version; Train train; + @Nullable LocalDate releaseDate; + @Nullable LocalDate generationInception; /** - * Creates a new {@link MaintainedVersion} representing the snaphot version of the given {@link Module} and + * Creates a new {@link MaintainedVersion} representing the snapshot version of the given {@link Module} and * {@link Train}. - * + * * @param module must not be {@literal null}. * @param train must not be {@literal null}. + * @param train can be {@literal null}. * @return */ - public static MaintainedVersion snapshot(Module module, Train train) { + public static MaintainedVersion snapshot(Module module, Train train, LocalDate generationInception) { Assert.notNull(module, "Module must not be null!"); Assert.notNull(train, "Train must not be null!"); @@ -54,13 +60,13 @@ class MaintainedVersion implements Comparable { ArtifactVersion snapshotVersion = ArtifactVersion.of(module.getVersion()).withModifierFormat(train.usesCalver()) .getSnapshotVersion(); - return MaintainedVersion.of(module.getProject(), snapshotVersion, train); + return MaintainedVersion.of(module.getProject(), snapshotVersion, train, null, generationInception); } /** * 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() { @@ -69,14 +75,14 @@ class MaintainedVersion implements Comparable { /** * Creates the {@link MaintainedVersion} for the next development version of the current one. - * + * * @return */ MaintainedVersion nextDevelopmentVersion() { - return MaintainedVersion.of(project, version.getNextBugfixVersion(), train); + return MaintainedVersion.of(project, version.getNextBugfixVersion(), train, LocalDate.now(), generationInception); } - /* + /* * (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ diff --git a/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java b/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java index ef29a79..7476134 100644 --- a/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java +++ b/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java @@ -17,7 +17,6 @@ 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.data.release.model.Train; @@ -25,11 +24,13 @@ import org.springframework.util.Assert; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; /** * Simple value object to create payloads to update project metadata in Sagan. * * @author Oliver Gierke + * @author Mark Paluch */ @JsonInclude(Include.NON_NULL) class ProjectMetadata { @@ -38,13 +39,11 @@ class ProjectMetadata { 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; + private final MaintainedVersions versions; /** - * Creates a new {@link ProjectMetadata} instace from the given {@link MaintainedVersion} in the context of the - * {@link MaintainedVersions}. + * Creates a new {@link ProjectMetadata} instance from the given {@link MaintainedVersion}. * * @param version must not be {@literal null}. * @param versions must not be {@literal null}. @@ -52,67 +51,9 @@ class ProjectMetadata { 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; } /** @@ -120,13 +61,23 @@ class ProjectMetadata { * * @return */ - public String getRefDocUrl() { + public String getReferenceDocUrl() { return version.getVersion().isSnapshotVersion() || Projects.BUILD.equals(version.getProject()) // ? "" // : String.format(DOCS, version.getProject().getName().toLowerCase(Locale.US)); } + /** + * Returns whether the version is the most current one. + * + * @return + */ + @JsonProperty("isCurrent") + public boolean getCurrent() { + return versions.isMainVersion(version); + } + /** * Returns the JavaDoc URL for non-snapshot versions and not the build project. * @@ -139,10 +90,6 @@ class ProjectMetadata { : String.format(JAVADOC, version.getProject().getName().toLowerCase(Locale.US)); } - public Repository getRepository() { - return new Repository(); - } - /** * Return the version to use. For the build project that's the release train name, for everything else the artifact * version. @@ -172,38 +119,4 @@ class ProjectMetadata { return version.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/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java b/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java index 999d773..9ff0074 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java +++ b/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java @@ -29,6 +29,7 @@ import org.springframework.web.client.RestTemplate; * Configuration for the Sagan interaction subsystem. * * @author Oliver Gierke + * @author Mark Paluch */ @Configuration class SaganConfiguration { @@ -50,6 +51,6 @@ class SaganConfiguration { @Bean RestTemplate saganRestTemplate() { - return new RestTemplateBuilder().basicAuthentication(properties.key, "").build(); + return new RestTemplateBuilder().basicAuthentication("mp911de", properties.key).build(); } } diff --git a/src/main/java/org/springframework/data/release/sagan/SaganOperations.java b/src/main/java/org/springframework/data/release/sagan/SaganOperations.java index 4f4c557..d603c6d 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganOperations.java +++ b/src/main/java/org/springframework/data/release/sagan/SaganOperations.java @@ -19,12 +19,14 @@ import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import java.time.LocalDate; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; -import java.util.function.Function; import java.util.stream.Collectors; import org.springframework.data.release.git.GitOperations; @@ -106,22 +108,25 @@ class SaganOperations { return ExecutionUtils.runAndReturn(executor, Streamable.of(() -> train.stream().filter(module -> !TO_FILTER.contains(module.getProject()))), module -> { return getLatestVersion(module, train); - }).stream().flatMap(MaintainedVersion::all); - }).stream().flatMap(Function.identity()).collect( + }); + }).stream().flatMap(Collection::stream).flatMap(Collection::stream).collect( Collectors.groupingBy(MaintainedVersion::getProject, ListWrapperCollector.collectInto(MaintainedVersions::of))); } - private MaintainedVersion getLatestVersion(Module module, Train train) { + private List getLatestVersion(Module module, Train train) { Project project = module.getProject(); - MaintainedVersion version = git.getTags(project).stream()// + List version = git.getTags(project).stream()// .filter(tag -> matches(tag, module.getVersion())).max(Comparator.naturalOrder()) // - .flatMap(Tag::toArtifactVersion) // - .map(it -> MaintainedVersion.of(module.getProject(), it, train)) // - .orElseGet(() -> MaintainedVersion.snapshot(module, train)); + .map(it -> { + MaintainedVersion maintainedVersion = MaintainedVersion.of(module.getProject(), it.toArtifactVersion().get(), + train, it.getCreationDate().toLocalDate(), it.getCreationDate().toLocalDate()); + return Arrays.asList(maintainedVersion, maintainedVersion.nextDevelopmentVersion()); + }) // + .orElseGet(() -> Collections.singletonList(MaintainedVersion.snapshot(module, train, LocalDate.now()))); - logger.log(project, "Found version %s for train %s!", version.getVersion(), train.getName()); + logger.log(project, "Found version %s for train %s!", version, train.getName()); return version; } diff --git a/src/main/java/org/springframework/data/release/sagan/SaganProperties.java b/src/main/java/org/springframework/data/release/sagan/SaganProperties.java index 3585424..b6fe656 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganProperties.java +++ b/src/main/java/org/springframework/data/release/sagan/SaganProperties.java @@ -20,74 +20,78 @@ 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}"); + private static String SAGAN_BASE = "https://spring.io/api/"; + private static String SAGAN_RELEASES = SAGAN_BASE.concat("projects/{project}/releases"); + private static String SAGAN_GENERATIONS = SAGAN_BASE.concat("projects/{project}/generations"); + private static String SAGAN_RELEASE_VERSION = SAGAN_RELEASES.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}. - * + * Returns the URI to the resource exposing the project releases for the given {@link Project}. + * * @param project must not be {@literal null}. * @return */ - URI getProjectMetadataResource(Project project) { + URI getProjectReleasesResource(Project project) { - Assert.notNull(project, "Project must not be null!"); + Assert.notNull(project, "Project must not be null!"); - return new UriTemplate(SAGAN_PROJECT_METADATA).expand(getProjectPathSegment(project)); + return new UriTemplate(SAGAN_RELEASES).expand(getProjectPathSegment(project)); } /** - * Returns the URI to the resource exposing the project metadata for the given {@link Project} and version + * Returns the URI to the resource exposing the project generations for the given {@link Project}. + * + * @param project must not be {@literal null}. + * @return + */ + URI getProjectGenerationsResource(Project project) { + + Assert.notNull(project, "Project must not be null!"); + + return new UriTemplate(SAGAN_GENERATIONS).expand(getProjectPathSegment(project)); + } + + /** + * Returns the URI to the resource exposing the project version 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) { + URI getProjectReleaseResource(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); + return new UriTemplate(SAGAN_RELEASE_VERSION).expand(getProjectPathSegment(project), version); } /** - * Returns the {@link URI} to the resource exposing the project metadata for the given {@link MaintainedVersion}. - * + * Returns the {@link URI} to the resource exposing the project version for the given {@link MaintainedVersion}. + * * @param version must not be {@literal null}. * @return */ URI getProjectMetadataResource(MaintainedVersion version) { - return getProjectMetadataResource(version.getProject(), version.getVersion().toString()); + return getProjectReleaseResource(version.getProject(), version.getVersion().toString()); } private static String getProjectPathSegment(Project project) { diff --git a/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java b/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java index fce142f..fec6c6f 100644 --- a/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/build/MavenIntegrationTests.java @@ -91,6 +91,6 @@ class MavenIntegrationTests extends AbstractIntegrationTests { @Test void findsSnapshotDependencies() throws Exception { - Pom pom = projection.io().file(workspace.getFile("bom/pom.xml", Projects.BUILD)).read(Pom.class); + Pom pom = projection.io().file(workspace.getFile("pom.xml", Projects.BUILD)).read(Pom.class); } } diff --git a/src/test/java/org/springframework/data/release/issues/github/GitHubIssueTrackerIntegrationTests.java b/src/test/java/org/springframework/data/release/issues/github/GitHubIssueTrackerIntegrationTests.java index 0955b9f..79a4d78 100644 --- a/src/test/java/org/springframework/data/release/issues/github/GitHubIssueTrackerIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/issues/github/GitHubIssueTrackerIntegrationTests.java @@ -105,14 +105,25 @@ class GitHubIssueTrackerIntegrationTests extends AbstractIntegrationTests { } @Test // #5 - void noReleaseTicketFound() { + void noMilestoneNoReleaseTicketFound() { mockGetMilestonesWith("emptyMilestones.json"); - assertThatIllegalArgumentException().isThrownBy(() -> github.getReleaseTicketFor(BUILD_HOPPER_RC1)) + assertThatIllegalStateException().isThrownBy(() -> github.getReleaseTicketFor(BUILD_HOPPER_RC1)) .withMessageContaining("No milestone for Spring Data Build found containing 1.8 RC1 (Hopper)!"); } + @Test // #5 + void noReleaseTicketFound() { + + mockGetMilestonesWith("milestones.json"); + mockGetIssuesWith("emptyIssues.json"); + + assertThatIllegalArgumentException().isThrownBy(() -> github.getReleaseTicketFor(BUILD_HOPPER_RC1)) + .withMessageContaining( + "Did not find a release ticket for Spring Data Build 1.8 RC1 (Hopper) containing Release 1.8 RC1 (Hopper)!"); + } + @Test // #5 void createReleaseVersionShouldCreateAVersion() { @@ -156,7 +167,7 @@ class GitHubIssueTrackerIntegrationTests extends AbstractIntegrationTests { mockGetIssuesWith("emptyIssues.json"); mockGetMilestonesWith("emptyMilestones.json"); - assertThatIllegalArgumentException().isThrownBy(() -> github.createReleaseTicket(moduleIteration)) + assertThatIllegalStateException().isThrownBy(() -> github.createReleaseTicket(moduleIteration)) .withMessageContaining("No milestone for Spring Data Build found containing 1.8 RC1 (Hopper)!"); } diff --git a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java b/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java index 1031203..6a429af 100644 --- a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java +++ b/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java @@ -24,7 +24,7 @@ import org.springframework.data.release.model.ReleaseTrains; /** * Unit tests for {@link MaintainedVersion}. - * + * * @author Oliver Gierke */ class MaintainedVersionUnitTests { @@ -33,9 +33,9 @@ class MaintainedVersionUnitTests { void newerVersionIsOrderedFirst() { MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.0.RELEASE"), - ReleaseTrains.INGALLS); + ReleaseTrains.INGALLS, null, null); MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.0.RELEASE"), - ReleaseTrains.HOPPER); + ReleaseTrains.HOPPER, null, null); assertThat(ingalls.compareTo(hopper)).isGreaterThan(0); } diff --git a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java b/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java index 610d06b..8901490 100644 --- a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java +++ b/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java @@ -24,7 +24,7 @@ import org.springframework.data.release.model.ReleaseTrains; /** * Unit tests for {@link MaintainedVersions}. - * + * * @author Oliver Gierke */ class MaintainedVersionsUnitTests { @@ -33,9 +33,9 @@ class MaintainedVersionsUnitTests { void considersMostRecentReleaseVersionTheMainOne() { MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.0.RELEASE"), - ReleaseTrains.INGALLS); + ReleaseTrains.INGALLS, null, null); MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.0.RELEASE"), - ReleaseTrains.HOPPER); + ReleaseTrains.HOPPER, null, null); MaintainedVersions versions = MaintainedVersions.of(ingalls, hopper); diff --git a/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java b/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java index 93d719d..7b1c654 100644 --- a/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java +++ b/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java @@ -25,8 +25,9 @@ import com.fasterxml.jackson.databind.ObjectWriter; /** * Tests for serialization of {@link ProjectMetadata}. - * + * * @author Oliver Gierke + * @author Mark Paluch */ class ProjectMetadataSerializationTests { @@ -35,14 +36,15 @@ class ProjectMetadataSerializationTests { ObjectWriter mapper = new ObjectMapper().writerWithDefaultPrettyPrinter(); - MaintainedVersion kay = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("2.0.0.RC1"), ReleaseTrains.KAY); + MaintainedVersion kay = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("2.0.0.RC1"), ReleaseTrains.KAY, + null, null); MaintainedVersion ingalls = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.13.5.RELEASE"), - ReleaseTrains.INGALLS); + ReleaseTrains.INGALLS, null, null); MaintainedVersion ingallsSnapshot = ingalls.nextDevelopmentVersion(); MaintainedVersion hopper = MaintainedVersion.of(Projects.COMMONS, ArtifactVersion.of("1.12.8.RELEASE"), - ReleaseTrains.HOPPER); + ReleaseTrains.HOPPER, null, null); - MaintainedVersions versions = MaintainedVersions.of(kay, ingalls, ingallsSnapshot, hopper); + MaintainedVersions versions = MaintainedVersions.of(kay, ingalls, hopper); System.out.println(mapper.writeValueAsString(new ProjectMetadata(kay, versions))); System.out.println(mapper.writeValueAsString(new ProjectMetadata(ingallsSnapshot, versions))); diff --git a/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java b/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java index fa0e1a8..fb1dc11 100644 --- a/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java @@ -15,11 +15,17 @@ */ package org.springframework.data.release.sagan; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import java.util.Map; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.release.AbstractIntegrationTests; +import org.springframework.data.release.model.Project; import org.springframework.data.release.model.Projects; import org.springframework.data.release.model.ReleaseTrains; @@ -48,8 +54,6 @@ class SaganOperationsIntegrationTests extends AbstractIntegrationTests { String output = version.toString(); System.out.println(versions.isMainVersion(version) ? output.concat(" (main release)") : output); }); - - System.out.println(); }); } @@ -58,6 +62,19 @@ class SaganOperationsIntegrationTests extends AbstractIntegrationTests { sagan.updateProjectMetadata(ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER); } + @Test + void findVersions() { + Map versions = sagan.findVersions(ReleaseTrains.LOVELACE, ReleaseTrains.MOORE, + ReleaseTrains.NEUMANN); + + List maintainedVersions = versions.get(Projects.ELASTICSEARCH) + .filter(it -> it.getVersion().isReleaseVersion()).toList(); + + assertThat(maintainedVersions).isNotEmpty(); + assertThat(maintainedVersions.get(0).getGenerationInception()).isNotNull(); + assertThat(maintainedVersions.get(0).getReleaseDate()).isNotNull(); + } + @Test void updateJpa() {