diff --git a/application-local.template b/application-local.template index 3366381..370db39 100644 --- a/application-local.template +++ b/application-local.template @@ -30,4 +30,4 @@ gpg.executable=/usr/local/bin/gpg1 # A GitHub token with user:email, read:user and read:org scopes. # User needs to be part of the Spring team on GitHub as well. -sagan.key= +project-service.key= diff --git a/readme.adoc b/readme.adoc index a7f2c72..325d0fe 100644 --- a/readme.adoc +++ b/readme.adoc @@ -80,8 +80,8 @@ A job should have started. Click on the active job, and then click on *Open Blue |Create new release versions and tickets for upcoming version |`$ tracker setup-next $trainIteration.next` -|Update versions in Sagan. `$targets` is given as comma separated lists of code names, without spaces. E.g. `Moore,Neumann` -|`$ sagan update $releasetrains` +|Update versions in the Projects Service. `$targets` is given as comma separated lists of code names, without spaces. E.g. `Moore,Neumann` +|`$ projects update $releasetrains` |Create list of docs for release announcements |`$ announcement $trainIteration` @@ -120,7 +120,7 @@ Add an `application-local.properties` to the project root and add the following * `gpg.passphrase` - The password of your GPG key. * `gpg.executable` - Path to your GPG executable, typically `/usr/local/MacGPG2/bin/gpg2` or `/usr/local/bin/gpg`. -* `sagan.key` - Sagan authentication token. Must be a valid GitHub token. Can be the same +* `project-service.key` - Project Service authentication token. Must be a valid GitHub token. Can be the same as `git.password` when using a GitHub token as password. After that, run the `verify` command (`$ verify`) to verify your settings (authentication, @@ -146,7 +146,7 @@ See `application-local.template` for details. 2+| *Post-release tasks* |Close JIRA tickets and GitHub release tickets. |`$ tracker close $trainIteration` |Create new release versions and tickets for upcoming version |`$ tracker setup-next $trainIteration.next` -|Update versions in Sagan. `$targets` is given as comma separated lists of code names, without spaces. E.g. `Moore,Neumann` |`$ sagan update $releasetrains` +|Update versions in Projects Service. `$targets` is given as comma separated lists of code names, without spaces. E.g. `Moore,Neumann` |`$ projects update $releasetrains` |Create list of docs for release announcements |`$ announcement $trainIteration` |=== diff --git a/src/main/java/org/springframework/data/release/cli/VerifyCommands.java b/src/main/java/org/springframework/data/release/cli/VerifyCommands.java index 5edf02b..60e30fd 100644 --- a/src/main/java/org/springframework/data/release/cli/VerifyCommands.java +++ b/src/main/java/org/springframework/data/release/cli/VerifyCommands.java @@ -26,7 +26,7 @@ import org.springframework.data.release.build.BuildOperations; import org.springframework.data.release.deployment.DeploymentOperations; import org.springframework.data.release.git.GitOperations; import org.springframework.data.release.issues.github.GitHub; -import org.springframework.data.release.sagan.SaganClient; +import org.springframework.data.release.projectservice.ProjectService; import org.springframework.data.release.utils.Logger; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; @@ -46,7 +46,7 @@ class VerifyCommands extends TimedCommand { @NonNull GitHub github; @NonNull DeploymentOperations deployment; @NonNull BuildOperations build; - @NonNull SaganClient saganClient; + @NonNull ProjectService projectService; @NonNull Logger logger; @CliCommand("verify") @@ -72,9 +72,9 @@ class VerifyCommands extends TimedCommand { github.verifyAuthentication(); } - if (ObjectUtils.isEmpty(module) || "sagan".equals(module)) { - // Sagan Verification - saganClient.verifyAuthentication(); + if (ObjectUtils.isEmpty(module) || "projects".equals(module)) { + // Projects Service Verification + projectService.verifyAuthentication(); } logger.log("Verify", "All settings are verified. You can ship a release now."); 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 e741faf..c3856a9 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -25,21 +25,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.TimeZone; -import java.util.UUID; +import java.util.*; import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -54,6 +41,7 @@ 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.EmptyCommitException; +import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.lib.ObjectId; @@ -75,16 +63,7 @@ import org.springframework.data.release.issues.IssueTracker; import org.springframework.data.release.issues.Ticket; import org.springframework.data.release.issues.TicketReference; import org.springframework.data.release.issues.TicketStatus; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Gpg; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ProjectAware; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; +import org.springframework.data.release.model.*; import org.springframework.data.release.utils.ExecutionUtils; import org.springframework.data.release.utils.Logger; import org.springframework.data.util.Pair; @@ -151,6 +130,17 @@ public class GitOperations { * @param update whether to fetch an update from origin. */ public void checkout(Train train, boolean update) { + checkout(train, update, true); + } + + /** + * Checks out all projects of the given {@link Train}. + * + * @param train + * @param update whether to fetch an update from origin. + * @param reset whether to reset HARD. + */ + public void checkout(Train train, boolean update, boolean reset) { Assert.notNull(train, "Train must not be null!"); @@ -158,47 +148,52 @@ public class GitOperations { update(train); } - AtomicBoolean mainSwitch = new AtomicBoolean(); ExecutionUtils.run(executor, train, module -> { Project project = module.getProject(); doWithGit(project, git -> { - ModuleIteration gaIteration = train.getModuleIteration(project, Iteration.GA); - Optional gaTag = findTagFor(project, ArtifactVersion.of(gaIteration)); + Branch branch = getBranch(train, module, project); + checkoutBranch(project, git, branch); - if (!gaTag.isPresent()) { - logger.log(project, "Checking out main branch as no GA release tag could be found!"); + if (reset) { + reset(project, branch); } - - Branch branch = gaTag.isPresent() ? Branch.from(module) : Branch.MAIN; - - CheckoutCommand command = git.checkout().setName(branch.toString()); - - if (!branchExists(project, branch)) { - - logger.log(project, "git checkout -b %s --track origin/%s", branch, branch); - command.setCreateBranch(true)// - .setStartPoint("origin/".concat(branch.toString()))// - .call(); - } else { - - logger.log(project, "git checkout %s", branch); - command.call(); - } - - reset(project, branch); }); }); - if (mainSwitch.get()) { - logger.warn(train, - "Successfully checked out projects. There were switches to main for certain projects. This happens if the train has no branches yet."); - } else { - logger.log(train, "Successfully checked out projects."); + logger.log(train, "Successfully checked out projects."); + + } + + private Branch getBranch(Train train, Module module, Project project) { + + ModuleIteration gaIteration = train.getModuleIteration(project, Iteration.GA); + Optional gaTag = findTagFor(project, ArtifactVersion.of(gaIteration)); + + if (!gaTag.isPresent()) { + logger.log(project, "Checking out main branch as no GA release tag could be found!"); } + return gaTag.isPresent() ? Branch.from(module) : Branch.MAIN; + } + + private void checkoutBranch(Project project, Git git, Branch branch) throws GitAPIException { + + CheckoutCommand command = git.checkout().setName(branch.toString()); + + if (!branchExists(project, branch)) { + + logger.log(project, "git checkout -b %s --track origin/%s", branch, branch); + command.setCreateBranch(true)// + .setStartPoint("origin/".concat(branch.toString()))// + .call(); + } else { + + logger.log(project, "git checkout %s", branch); + command.call(); + } } /** diff --git a/src/main/java/org/springframework/data/release/model/Modules.java b/src/main/java/org/springframework/data/release/model/Modules.java index a8139e2..f9e33b0 100644 --- a/src/main/java/org/springframework/data/release/model/Modules.java +++ b/src/main/java/org/springframework/data/release/model/Modules.java @@ -27,7 +27,7 @@ import org.springframework.data.util.Streamable; * * @author Oliver Gierke */ -class Modules implements Streamable { +public class Modules implements Streamable { private final Collection modules; diff --git a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java index 66b916c..487f337 100644 --- a/src/main/java/org/springframework/data/release/model/ReleaseTrains.java +++ b/src/main/java/org/springframework/data/release/model/ReleaseTrains.java @@ -126,7 +126,7 @@ public class ReleaseTrains { return TRAINS.stream() // .filter(it -> it.getName().equalsIgnoreCase(name)) // .findFirst() // - .orElse(null); + .orElseThrow(() -> new IllegalArgumentException("Cannot resolve " + name + " to a Release Train")); } public static Train getTrainByCalver(Version calver) { diff --git a/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java b/src/main/java/org/springframework/data/release/projectservice/DefaultProjectClient.java similarity index 57% rename from src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java rename to src/main/java/org/springframework/data/release/projectservice/DefaultProjectClient.java index 066af91..4b801c9 100644 --- a/src/main/java/org/springframework/data/release/sagan/DefaultSaganClient.java +++ b/src/main/java/org/springframework/data/release/projectservice/DefaultProjectClient.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.springframework.data.release.model.Project; import org.springframework.data.release.model.Projects; @@ -42,10 +43,10 @@ import com.jayway.jsonpath.JsonPath; */ @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class DefaultSaganClient implements SaganClient { +class DefaultProjectClient implements ProjectService { RestOperations operations; - SaganProperties properties; + ProjectServiceResources configuration; Logger logger; /* @@ -55,7 +56,7 @@ class DefaultSaganClient implements SaganClient { @Override public String getProjectMetadata(MaintainedVersion version) { - URI resource = properties.getProjectMetadataResource(version); + URI resource = configuration.getProjectMetadataResource(version); logger.log(version.getProject(), "Getting project metadata for version %s from %s…", version.getVersion(), resource); @@ -70,9 +71,9 @@ class DefaultSaganClient implements SaganClient { @Override public String getProjectMetadata(Project project) { - URI resource = properties.getProjectReleasesResource(project); + URI resource = configuration.getProjectReleasesResource(project); - logger.log(project, "Getting project releases from %s…", resource); + logger.log(project, "Getting listed project versions from %s…", resource); return operations.getForObject(resource, String.class); } @@ -84,26 +85,38 @@ class DefaultSaganClient implements SaganClient { @Override public void updateProjectMetadata(Project project, MaintainedVersions versions) { - URI resource = properties.getProjectReleasesResource(project); + URI resource = configuration.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) + List versionsToRetain = getVersionsToWrite(versions).map(ProjectMetadata::getVersion) .collect(Collectors.toList()); List versionsInSagan = new ArrayList<>(); - logger.log(project, "Updating project version to %s via %s…", versionsString, resource); - // Delete all existing versions first - deleteExistingVersions(project, versionsToRetain, versionsInSagan); - logger.log(project, "Writing project versions %s.", versionsString); + boolean requiresDelete = requiresDeleteVersions(project, versionsToRetain, versionsInSagan); + boolean requiresWrite = requiresWriteVersions(versions, versionsInSagan); - // Write new ones - createVersions(versions, resource, versionsInSagan); + if (requiresDelete || requiresWrite) { + logger.log(project, "Updating project versions at %s…", resource); + } + + if (requiresDelete) { + + logger.log(project, "Deleting outdated project versions…", versionsString); + deleteExistingVersions(project, versionsToRetain); + } + + if (requiresWrite) { + + logger.log(project, "Writing project versions %s.", versionsString); + createVersions(project, versions, resource, versionsInSagan); + } + + logger.log(project, "Project versions up to date: %s", versionsString); } /* @@ -113,7 +126,7 @@ class DefaultSaganClient implements SaganClient { @Override public void verifyAuthentication() { - URI resource = properties.getProjectReleasesResource(Projects.BUILD); + URI resource = configuration.getProjectReleasesResource(Projects.BUILD); logger.log("Sagan", "Verifying Sagan Authentication…"); @@ -126,22 +139,42 @@ class DefaultSaganClient implements SaganClient { logger.log("Sagan", "Authentication verified!"); } - private void createVersions(MaintainedVersions versions, URI resource, List versionsInSagan) { + private void createVersions(Project project, MaintainedVersions versions, URI resource, + List versionsInSagan) { - versions.stream() // - .map(it -> new ProjectMetadata(it, versions)) // + getVersionsToWrite(versions) // .filter(version -> !versionsInSagan.contains(version.getVersion())) // + .peek(metadata -> logger.log(project, "Creating project version %s…", metadata.getVersion())) // .forEach(payload -> operations.postForObject(resource, payload, String.class)); } - private void deleteExistingVersions(Project project, List versionsToRetain, List versionsInSagan) { + private static boolean requiresWriteVersions(MaintainedVersions versions, List versionsInSagan) { + return getVersionsToWrite(versions) // + .anyMatch(version -> !versionsInSagan.contains(version.getVersion())); + } - Arrays.stream(JsonPath.compile("$..version"). read(getProjectMetadata(project)).toArray())// - .map(Object::toString) // + private static Stream getVersionsToWrite(MaintainedVersions versions) { + return versions.stream() // + .map(it -> new ProjectMetadata(it, versions)); + } + + private boolean requiresDeleteVersions(Project project, List versionsToRetain, List versionsInSagan) { + return getVersionsToDelete(project) // .peek(versionsInSagan::add) // + .anyMatch(version -> !versionsToRetain.contains(version)); + } + + private void deleteExistingVersions(Project project, List versionsToRetain) { + + getVersionsToDelete(project) // .filter(version -> !versionsToRetain.contains(version)) // - .map(version -> properties.getProjectReleaseResource(project, version))// + .map(version -> configuration.getProjectReleaseResource(project, version))// .peek(uri -> logger.log(project, "Deleting existing project version at %s…", uri)) // .forEach(operations::delete); } + + private Stream getVersionsToDelete(Project project) { + return Arrays.stream(JsonPath.compile("$..version"). read(getProjectMetadata(project)).toArray())// + .map(Object::toString); + } } diff --git a/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java b/src/main/java/org/springframework/data/release/projectservice/DummyProjectClient.java similarity index 89% rename from src/main/java/org/springframework/data/release/sagan/DummySaganClient.java rename to src/main/java/org/springframework/data/release/projectservice/DummyProjectClient.java index b56d4ae..5a9a061 100644 --- a/src/main/java/org/springframework/data/release/sagan/DummySaganClient.java +++ b/src/main/java/org/springframework/data/release/projectservice/DummyProjectClient.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -30,7 +30,7 @@ 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 + * Dummy implementation of {@link ProjectService} to just dump the request payloads into {@link System#out} instead of * communicating with a real server. * * @author Oliver Gierke @@ -38,7 +38,7 @@ import com.fasterxml.jackson.databind.ObjectWriter; */ @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class DummySaganClient implements SaganClient { +class DummyProjectClient implements ProjectService { Logger logger; ObjectWriter mapper; diff --git a/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java b/src/main/java/org/springframework/data/release/projectservice/MaintainedVersion.java similarity index 95% rename from src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java rename to src/main/java/org/springframework/data/release/projectservice/MaintainedVersion.java index a38000b..aac3603 100644 --- a/src/main/java/org/springframework/data/release/sagan/MaintainedVersion.java +++ b/src/main/java/org/springframework/data/release/projectservice/MaintainedVersion.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.Value; import lombok.With; diff --git a/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java b/src/main/java/org/springframework/data/release/projectservice/MaintainedVersions.java similarity index 78% rename from src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java rename to src/main/java/org/springframework/data/release/projectservice/MaintainedVersions.java index 4d07c8f..2254a9e 100644 --- a/src/main/java/org/springframework/data/release/sagan/MaintainedVersions.java +++ b/src/main/java/org/springframework/data/release/projectservice/MaintainedVersions.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 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. + */ + /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +27,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -59,7 +75,7 @@ class MaintainedVersions implements Streamable { * @return */ static MaintainedVersions of(MaintainedVersion... versions) { - return MaintainedVersions.of(Arrays.asList(versions)); + return of(Arrays.asList(versions)); } /** diff --git a/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java b/src/main/java/org/springframework/data/release/projectservice/ProjectMetadata.java similarity index 94% rename from src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectMetadata.java index c33fd0f..8eb7978 100644 --- a/src/main/java/org/springframework/data/release/sagan/ProjectMetadata.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectMetadata.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import org.springframework.data.release.model.DocumentationMetadata; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/release/projectservice/ProjectPaths.java b/src/main/java/org/springframework/data/release/projectservice/ProjectPaths.java new file mode 100644 index 0000000..a657992 --- /dev/null +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectPaths.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 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.projectservice; + +import org.springframework.data.release.model.Project; +import org.springframework.data.release.model.Projects; + +/** + * @author Mark Paluch + */ +class ProjectPaths { + + static String getProjectPathSegment(Project project) { + + if (Projects.BUILD.equals(project)) { + return "spring-data"; + } + + if (Projects.RELATIONAL.equals(project) || Projects.JDBC.equals(project)) { + return "spring-data-jdbc"; + } + + return project.getFolderName(); + } +} diff --git a/src/main/java/org/springframework/data/release/sagan/SaganClient.java b/src/main/java/org/springframework/data/release/projectservice/ProjectService.java similarity index 83% rename from src/main/java/org/springframework/data/release/sagan/SaganClient.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectService.java index 0293b91..aef3c2e 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganClient.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectService.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import org.springframework.data.release.model.Project; /** - * Interface to abstract the actual interaction with the Sagan server. + * Interface to abstract the actual interaction with the Projects service server. * * @author Oliver Gierke * @author Mark Paluch */ -public interface SaganClient { +public interface ProjectService { /** * Returns the project metadata for the given {@link MaintainedVersion}. diff --git a/src/main/java/org/springframework/data/release/sagan/SaganCommands.java b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceCommands.java similarity index 61% rename from src/main/java/org/springframework/data/release/sagan/SaganCommands.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectServiceCommands.java index 80df756..15772dc 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganCommands.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceCommands.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,27 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; 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.git.GitOperations; +import org.springframework.data.release.model.Project; import org.springframework.data.release.model.ReleaseTrains; import org.springframework.data.release.model.Train; +import org.springframework.data.release.utils.ExecutionUtils; +import org.springframework.data.util.Streamable; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; import org.springframework.stereotype.Component; /** - * Operations for Sagan interaction. + * Operations for Projects Service interaction. * * @author Oliver Gierke * @author Mark Paluch @@ -42,12 +48,18 @@ import org.springframework.stereotype.Component; @CliComponent @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class SaganCommands extends TimedCommand { +class ProjectServiceCommands extends TimedCommand { - SaganOperations sagan; + ProjectServiceOperations projects; GitOperations git; + ExecutorService executor; @CliCommand("sagan update") + public void saganUpdateProjectInformation(@CliOption(key = "", mandatory = true) String trainNames) { + updateProjectInformation(trainNames); + } + + @CliCommand("projects update") public void updateProjectInformation(@CliOption(key = "", mandatory = true) String trainNames) { List trains = Stream.of(trainNames.split(","))// @@ -55,8 +67,12 @@ class SaganCommands extends TimedCommand { .collect(Collectors.toList()); // ensure we have all git repositories available - trains.forEach(git::checkout); + Set affectedProjects = new HashSet<>(); + trains.stream() // + .flatMap(Streamable::stream) // + .forEach(it -> affectedProjects.add(it.getProject())); + ExecutionUtils.run(executor, Streamable.of(affectedProjects), git::update); - sagan.updateProjectMetadata(trains); + projects.updateProjectMetadata(trains); } } diff --git a/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceConfiguration.java similarity index 71% rename from src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectServiceConfiguration.java index b2a5de6..5e92d80 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganConfiguration.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceConfiguration.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import java.util.concurrent.Executor; @@ -33,23 +33,23 @@ import org.springframework.web.client.RestTemplate; * @author Mark Paluch */ @Configuration(proxyBeanMethods = false) -class SaganConfiguration { +class ProjectServiceConfiguration { @Autowired GitProperties gitProperties; - @Autowired SaganProperties properties; @Autowired Logger logger; @Bean - public SaganOperations saganOperations(GitOperations operations, SaganClient saganClient, Executor executor) { - return new SaganOperations(operations, executor, saganClient, logger); + public ProjectServiceOperations saganOperations(GitOperations operations, ProjectService projectService, + Executor executor) { + return new ProjectServiceOperations(operations, executor, projectService, logger); } @Bean - SaganClient saganClient() { + ProjectService projectsService(ProjectServiceProperties properties) { RestTemplate restTemplate = new RestTemplateBuilder() .basicAuthentication(gitProperties.getUsername(), properties.key).build(); - return new DefaultSaganClient(restTemplate, properties, logger); + return new DefaultProjectClient(restTemplate, properties, logger); } } diff --git a/src/main/java/org/springframework/data/release/sagan/SaganOperations.java b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceOperations.java similarity index 89% rename from src/main/java/org/springframework/data/release/sagan/SaganOperations.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectServiceOperations.java index 1687119..3e7b2f3 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganOperations.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceOperations.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; @@ -49,14 +49,14 @@ import org.springframework.util.Assert; */ @RequiredArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class SaganOperations { +class ProjectServiceOperations { private static final List TO_FILTER = Arrays.asList(Projects.COMMONS, // Projects.KEY_VALUE); GitOperations git; Executor executor; - SaganClient client; + ProjectService client; Logger logger; /** @@ -77,13 +77,33 @@ class SaganOperations { Map versions = findVersions(trains); - ExecutionUtils.run(executor, Streamable.of(versions.entrySet()), entry -> { + Streamable.of(versions.entrySet()).forEach(entry -> { if (entry.getKey() == Projects.BOM) { return; } - client.updateProjectMetadata(entry.getKey(), entry.getValue()); + if(entry.getKey() == Projects.JDBC){ + return; + } + + // Sometimes we see 404 Not Found: [no body], sometimes + // 400 Bad Request: "Release '2.2.13-SNAPSHOT' already present + RuntimeException rethrow = null; + for (int i = 0; i < 5; i++) { + + try { + client.updateProjectMetadata(entry.getKey(), entry.getValue()); + return; + } catch (RuntimeException e) { + rethrow = e; + logger.warn(entry.getKey(), e.toString()); + } + } + + if (rethrow != null) { + throw rethrow; + } }); } diff --git a/src/main/java/org/springframework/data/release/sagan/SaganProperties.java b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceProperties.java similarity index 57% rename from src/main/java/org/springframework/data/release/sagan/SaganProperties.java rename to src/main/java/org/springframework/data/release/projectservice/ProjectServiceProperties.java index 9cca21a..2b6dcd9 100644 --- a/src/main/java/org/springframework/data/release/sagan/SaganProperties.java +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceProperties.java @@ -1,11 +1,11 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2023 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 * - * https://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import lombok.Setter; @@ -21,26 +21,21 @@ import java.net.URI; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; 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. + * Configuration properties for the Projects Service instance to talk to. * - * @author Oliver Gierke + * @author Mark Paluch */ @Component -@ConfigurationProperties(prefix = "sagan") -class SaganProperties { - - 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}"); +@ConfigurationProperties(prefix = "project-service") +class ProjectServiceProperties implements ProjectServiceResources { @Setter String key; + @Setter String base = "https://api.spring.io/"; /** * Returns the URI to the resource exposing the project releases for the given {@link Project}. @@ -48,11 +43,11 @@ class SaganProperties { * @param project must not be {@literal null}. * @return */ - URI getProjectReleasesResource(Project project) { + public URI getProjectReleasesResource(Project project) { Assert.notNull(project, "Project must not be null!"); - return new UriTemplate(SAGAN_RELEASES).expand(getProjectPathSegment(project)); + return new UriTemplate(base + "projects/{project}/releases").expand(ProjectPaths.getProjectPathSegment(project)); } /** @@ -61,11 +56,11 @@ class SaganProperties { * @param project must not be {@literal null}. * @return */ - URI getProjectGenerationsResource(Project project) { + public URI getProjectGenerationsResource(Project project) { Assert.notNull(project, "Project must not be null!"); - return new UriTemplate(SAGAN_GENERATIONS).expand(getProjectPathSegment(project)); + return new UriTemplate(base + "projects/{project}/generations").expand(ProjectPaths.getProjectPathSegment(project)); } /** @@ -76,12 +71,13 @@ class SaganProperties { * @param version must not be {@literal null}. * @return */ - URI getProjectReleaseResource(Project project, String version) { + public 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_RELEASE_VERSION).expand(getProjectPathSegment(project), version); + return new UriTemplate(base + "projects/{project}/releases/{version}") + .expand(ProjectPaths.getProjectPathSegment(project), version); } /** @@ -90,20 +86,7 @@ class SaganProperties { * @param version must not be {@literal null}. * @return */ - URI getProjectMetadataResource(MaintainedVersion version) { + public URI getProjectMetadataResource(MaintainedVersion version) { return getProjectReleaseResource(version.getProject(), version.getVersion().toString()); } - - private static String getProjectPathSegment(Project project) { - - if (Projects.BUILD.equals(project)) { - return "spring-data"; - } - - if (Projects.RELATIONAL.equals(project) || Projects.JDBC.equals(project)) { - return "spring-data-jdbc"; - } - - return project.getFolderName(); - } } diff --git a/src/main/java/org/springframework/data/release/projectservice/ProjectServiceResources.java b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceResources.java new file mode 100644 index 0000000..fcac338 --- /dev/null +++ b/src/main/java/org/springframework/data/release/projectservice/ProjectServiceResources.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.projectservice; + +import java.net.URI; + +import org.springframework.data.release.model.Project; + +/** + * @author Mark Paluch + */ +public interface ProjectServiceResources { + + URI getProjectReleasesResource(Project project); + + URI getProjectReleaseResource(Project project, String version); + + URI getProjectMetadataResource(MaintainedVersion version); +} diff --git a/src/main/resources/application-jenkins.properties b/src/main/resources/application-jenkins.properties index ee5e2f0..f48402a 100644 --- a/src/main/resources/application-jenkins.properties +++ b/src/main/resources/application-jenkins.properties @@ -18,6 +18,7 @@ deployment.maven-central.gpg.passphrase=${MAVEN_SIGNING_KEY_PASSWORD} deployment.maven-central.gpg.executable=/usr/bin/gpg sagan.key=n/a +project-service.key=n/a maven.maven-home=${MAVEN_HOME} maven.console-logger=false diff --git a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java b/src/test/java/org/springframework/data/release/projectservice/MaintainedVersionUnitTests.java similarity index 96% rename from src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java rename to src/test/java/org/springframework/data/release/projectservice/MaintainedVersionUnitTests.java index 77eeaac..f6e8726 100644 --- a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionUnitTests.java +++ b/src/test/java/org/springframework/data/release/projectservice/MaintainedVersionUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java b/src/test/java/org/springframework/data/release/projectservice/MaintainedVersionsUnitTests.java similarity index 96% rename from src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java rename to src/test/java/org/springframework/data/release/projectservice/MaintainedVersionsUnitTests.java index 0e394a9..02d81a6 100644 --- a/src/test/java/org/springframework/data/release/sagan/MaintainedVersionsUnitTests.java +++ b/src/test/java/org/springframework/data/release/projectservice/MaintainedVersionsUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java b/src/test/java/org/springframework/data/release/projectservice/ProjectMetadataSerializationTests.java similarity index 97% rename from src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java rename to src/test/java/org/springframework/data/release/projectservice/ProjectMetadataSerializationTests.java index 8b77cc4..795c4a4 100644 --- a/src/test/java/org/springframework/data/release/sagan/ProjectMetadataSerializationTests.java +++ b/src/test/java/org/springframework/data/release/projectservice/ProjectMetadataSerializationTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import org.junit.jupiter.api.Test; import org.springframework.data.release.model.ArtifactVersion; diff --git a/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java b/src/test/java/org/springframework/data/release/projectservice/ProjectServiceOperationsIntegrationTests.java similarity index 89% rename from src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java rename to src/test/java/org/springframework/data/release/projectservice/ProjectServiceOperationsIntegrationTests.java index 436a2d0..67d635d 100644 --- a/src/test/java/org/springframework/data/release/sagan/SaganOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/release/projectservice/ProjectServiceOperationsIntegrationTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.release.sagan; +package org.springframework.data.release.projectservice; import static org.assertj.core.api.Assertions.*; @@ -22,7 +22,6 @@ 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; @@ -30,15 +29,15 @@ import org.springframework.data.release.model.Projects; import org.springframework.data.release.model.ReleaseTrains; /** - * Integration tests for {@link SaganOperations}. + * Integration tests for {@link ProjectServiceOperations}. * * @author Oliver Gierke */ @Disabled("I will write to production systems") -class SaganOperationsIntegrationTests extends AbstractIntegrationTests { +class ProjectServiceOperationsIntegrationTests extends AbstractIntegrationTests { - @Autowired SaganOperations sagan; - @Autowired SaganClient client; + @Autowired ProjectServiceOperations sagan; + @Autowired ProjectService client; @Test void detectVersionsToUpdate() { @@ -78,7 +77,7 @@ class SaganOperationsIntegrationTests extends AbstractIntegrationTests { @Test void updateJpa() { - MaintainedVersions versions = sagan.findVersions(ReleaseTrains.KAY, ReleaseTrains.INGALLS, ReleaseTrains.HOPPER) + MaintainedVersions versions = sagan.findVersions(ReleaseTrains.Q, ReleaseTrains.RAJ, ReleaseTrains.TURING) .get(Projects.JPA); System.out.println(versions); diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index faa013c..c52cdd4 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -15,3 +15,4 @@ git.author=dummy github.api-url=http://localhost:8888 #Sagan sagan.key=foo +project-service.key=foo