diff --git a/src/main/java/org/springframework/data/release/build/BuildOperations.java b/src/main/java/org/springframework/data/release/build/BuildOperations.java index aac318a..44a699c 100644 --- a/src/main/java/org/springframework/data/release/build/BuildOperations.java +++ b/src/main/java/org/springframework/data/release/build/BuildOperations.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import org.assertj.core.util.VisibleForTesting; import org.springframework.data.release.deployment.DeploymentInformation; import org.springframework.data.release.deployment.StagingRepository; +import org.springframework.data.release.git.BranchMapping; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Phase; import org.springframework.data.release.model.ProjectAware; @@ -76,6 +77,16 @@ public class BuildOperations { logger.log(iteration, "Update Project Descriptors done: %s", summary); } + public void updateBuildConfig(TrainIteration iteration, BranchMapping branches) throws Exception { + + Assert.notNull(iteration, "Train iteration must not be null!"); + + BuildExecutor.Summary summary = executor.doWithBuildSystemOrdered(iteration, + (system, it) -> system.updateBuildConfig(it, branches)); + + logger.log(iteration, "Update Build config done: %s", summary); + } + /** * Prepares the versions of the given {@link TrainIteration} depending on the given {@link Phase}. * @@ -414,4 +425,5 @@ public class BuildOperations { return function.apply(buildSystem.withJavaVersion(executor.detectJavaVersion(module.getSupportedProject())), module); } + } diff --git a/src/main/java/org/springframework/data/release/build/BuildSystem.java b/src/main/java/org/springframework/data/release/build/BuildSystem.java index e1039a5..ac2145b 100644 --- a/src/main/java/org/springframework/data/release/build/BuildSystem.java +++ b/src/main/java/org/springframework/data/release/build/BuildSystem.java @@ -17,11 +17,12 @@ package org.springframework.data.release.build; import org.springframework.data.release.deployment.DeploymentInformation; import org.springframework.data.release.deployment.StagingRepository; +import org.springframework.data.release.git.BranchMapping; import org.springframework.data.release.model.JavaVersion; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.SupportedProject; import org.springframework.data.release.model.ProjectAware; +import org.springframework.data.release.model.SupportedProject; import org.springframework.data.release.model.Train; import org.springframework.data.release.model.TrainIteration; import org.springframework.plugin.core.Plugin; @@ -43,6 +44,14 @@ interface BuildSystem extends Plugin { */ M updateProjectDescriptors(M iteration, UpdateInformation updateInformation); + /** + * Updates the project build config for the given {@link ModuleIteration}. + * + * @param iteration must not be {@literal null}. + * @param branches must not be {@literal null}. + */ + ModuleIteration updateBuildConfig(ModuleIteration iteration, BranchMapping branches); + /** * Prepares the project descriptor of the {@link ModuleIteration} for the given release {@link Phase}. * @@ -139,4 +148,5 @@ interface BuildSystem extends Plugin { * @return */ BuildSystem withJavaVersion(JavaVersion javaVersion); + } diff --git a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java index fc2bfdb..e703812 100644 --- a/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java +++ b/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java @@ -21,6 +21,7 @@ import static org.springframework.data.release.model.Projects.*; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; import java.io.BufferedInputStream; @@ -31,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.List; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -51,6 +53,9 @@ import org.springframework.data.release.deployment.DeploymentProperties; import org.springframework.data.release.deployment.DeploymentProperties.Authentication; import org.springframework.data.release.deployment.DeploymentProperties.MavenCentral; import org.springframework.data.release.deployment.StagingRepository; +import org.springframework.data.release.git.Branch; +import org.springframework.data.release.git.BranchMapping; +import org.springframework.data.release.git.GitProject; import org.springframework.data.release.io.Workspace; import org.springframework.data.release.model.*; import org.springframework.data.release.utils.Logger; @@ -126,6 +131,42 @@ class MavenBuildSystem implements BuildSystem { return module; } + @Override + @SneakyThrows + public ModuleIteration updateBuildConfig(ModuleIteration module, BranchMapping branches) { + + File jenkinsfile = workspace.getFile("Jenkinsfile", module.getSupportedProject()); + if (!jenkinsfile.exists()) { + logger.warn(module, "No Jenkinsfile found, skipping Jenkinsfile update."); + } + + byte[] bytes = Files.readAllBytes(jenkinsfile.toPath()); + String content = new String(bytes, StandardCharsets.UTF_8); + + for (ModuleIteration participatingModule : module.getTrainIteration()) { + + Branch source = branches.getSourceBranch(participatingModule.getProject()); + Branch target = branches.getTargetBranch(participatingModule.getProject()); + + if (source == null || target == null) { + continue; + } + + GitProject project = GitProject.of(participatingModule.getSupportedProject()); + + String dependency = String.format("%s/%s", project.getRepositoryName(), source); + String replacement = String.format("%s/%s", project.getRepositoryName(), target); + + content = content.replace(dependency, replacement); + } + + Files.write(jenkinsfile.toPath(), content.getBytes(StandardCharsets.UTF_8)); + + logger.warn(module, "Jenkinsfile updated."); + + return module; + } + /* * (non-Javadoc) * @see org.springframework.data.release.build.BuildSystem#prepareVersion(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.Phase) diff --git a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java index bf31345..3bc6f61 100644 --- a/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ b/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java @@ -30,6 +30,7 @@ import org.springframework.data.release.build.BuildOperations; import org.springframework.data.release.deployment.DeploymentInformation; import org.springframework.data.release.deployment.DeploymentOperations; import org.springframework.data.release.deployment.StagingRepository; +import org.springframework.data.release.git.BranchMapping; import org.springframework.data.release.git.GitOperations; import org.springframework.data.release.issues.IssueTrackerCommands; import org.springframework.data.release.issues.github.GitHubCommands; @@ -188,7 +189,7 @@ class ReleaseCommands extends TimedCommand { git.tagRelease(iteration); if (iteration.getTrain().isAlwaysUseBranch()) { - setupMaintenanceVersions(iteration); + setupMaintenanceVersions(iteration, BranchMapping.NONE); } else { build.prepareVersions(iteration, Phase.CLEANUP); @@ -202,11 +203,14 @@ class ReleaseCommands extends TimedCommand { if (iteration.getIteration().isGAIteration()) { // Create bugfix branches - git.createMaintenanceBranches(iteration); + BranchMapping branches = git.createMaintenanceBranches(iteration, true); // Set project version to maintenance once setupMaintenanceVersions(iteration); } + } + setupMaintenanceVersions(iteration, branches); + } } } @@ -229,10 +233,9 @@ class ReleaseCommands extends TimedCommand { gitHub.triggerAntoraWorkflow(project); } }); - } - private void setupMaintenanceVersions(TrainIteration iteration) throws Exception { + private void setupMaintenanceVersions(TrainIteration iteration, BranchMapping branches) throws Exception { // Set project version to maintenance once build.prepareVersions(iteration, Phase.MAINTENANCE); @@ -240,6 +243,11 @@ class ReleaseCommands extends TimedCommand { // Update inter-project dependencies and repositories build.updateProjectDescriptors(iteration, Phase.MAINTENANCE); + + if (branches.hasBranches()) { + build.updateBuildConfig(iteration, branches); + } + git.commit(iteration, "After release cleanups."); // Back to main branch diff --git a/src/main/java/org/springframework/data/release/git/BranchMapping.java b/src/main/java/org/springframework/data/release/git/BranchMapping.java new file mode 100644 index 0000000..ec2bdc0 --- /dev/null +++ b/src/main/java/org/springframework/data/release/git/BranchMapping.java @@ -0,0 +1,63 @@ +/* + * Copyright 2024 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.git; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.data.release.model.Project; + +/** + * Value object to capture source and target branches for a {@link Project} + * + * @author Mark Paluch + */ +public class BranchMapping { + + public static final BranchMapping NONE = new BranchMapping(Collections.emptyMap(), Collections.emptyMap()); + + private final Map sourceBranches; + + private final Map targetBranches; + + public BranchMapping() { + this(new HashMap<>(), new HashMap<>()); + } + + private BranchMapping(Map sourceBranches, Map targetBranches) { + this.sourceBranches = sourceBranches; + this.targetBranches = targetBranches; + } + + public void add(Project project, Branch sourceBranch, Branch targetBranch) { + sourceBranches.put(project, sourceBranch); + targetBranches.put(project, targetBranch); + } + + public Branch getSourceBranch(Project project) { + return sourceBranches.get(project); + } + + public Branch getTargetBranch(Project project) { + return targetBranches.get(project); + } + + public boolean hasBranches() { + return !sourceBranches.isEmpty() && !targetBranches.isEmpty(); + } + +} 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 2b70f4a..32073e4 100644 --- a/src/main/java/org/springframework/data/release/git/GitOperations.java +++ b/src/main/java/org/springframework/data/release/git/GitOperations.java @@ -39,6 +39,7 @@ import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.GitCommand; +import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.TransportCommand; @@ -925,18 +926,41 @@ public class GitOperations { logger.log(project, "Checkout done!"); } - public void createMaintenanceBranches(TrainIteration iteration) { + public BranchMapping createMaintenanceBranches(TrainIteration iteration, boolean checkout) { - if (!iteration.getIteration().isGAIteration()) { - return; + if (checkout && !iteration.getIteration().isGAIteration()) { + return BranchMapping.NONE; } - checkout(iteration); + if (checkout) { + checkout(iteration); + } + + return createBranch(iteration); + } + + public BranchMapping createBranch(TrainIteration iteration) { + + BranchMapping branches = new BranchMapping(); ExecutionUtils.run(executor, iteration, module -> { - Branch branch = createMaintenanceBranch(module); - checkout(module.getSupportedProject(), branch, BranchCheckoutMode.CREATE_ONLY); + Branch current = getCurrentBranch(module); + Branch newBranch = createBranch(module); + checkout(module.getSupportedProject(), newBranch, BranchCheckoutMode.CREATE_ONLY); + + synchronized (branches) { + branches.add(module.getProject(), current, newBranch); + } + }); + + return branches; + } + + private Branch getCurrentBranch(ModuleIteration module) { + + return doWithGit(module.getSupportedProject(), git -> { + return Branch.from(git.getRepository().getBranch()); }); } @@ -1012,13 +1036,26 @@ public class GitOperations { * @param module must not be {@literal null}. * @return */ - private Branch createMaintenanceBranch(ModuleIteration module) { + private Branch createBranch(ModuleIteration module) { Assert.notNull(module, "Module iteration must not be null!"); Branch branch = Branch.from(module.getVersion()); doWithGit(module.getSupportedProject(), git -> { + + List existingBranches = git.branchList().setListMode(ListBranchCommand.ListMode.ALL) // + .call() // + .stream() // + .filter(ref -> ref.getName().startsWith("refs/heads/")) // + .map(it -> it.getName().substring(11)) // + .collect(Collectors.toList()); + + if (existingBranches.contains(branch.toString())) { + logger.log(module, "Branch %s already exists, skipping creation.", branch); + return; + } + logger.log(module, "git checkout -b %s", branch); git.branchCreate().setName(branch.toString()).call(); }); 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 bb8b16c..40476e4 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,8 @@ public class ReleaseTrains { .withCalver("2025.0"); Z = Y.next("Z", Transition.MAJOR) // - .withCalver("2025.1"); + .withCalver("2025.1") // + .withAlwaysUseBranch(true); // Trains