Add support to upgrade Java versions across projects.

Closes #193
This commit is contained in:
Mark Paluch
2022-02-07 09:52:07 +01:00
parent 6c988d1b0e
commit b52381593f
5 changed files with 187 additions and 10 deletions

View File

@@ -82,8 +82,18 @@ Workflow:
* Check for dependency upgrades `$ dependency check $trainIteration`
Reports upgradable dependencies for Build and Modules and creates `dependency-upgrade-build.properties` file.
Edit `dependency-upgrade-build.properties` to specify the dependency version to upgrade. Removing a line will omit that dependency upgrade.
Reports upgradable dependencies for Build and Modules and
creates `dependency-upgrade-build.properties` file.
Edit `dependency-upgrade-build.properties` to specify the dependency version to upgrade.
Removing a line will omit that dependency upgrade.
* Apply dependency upgrade with `$ dependency upgrade $trainIteration`. Applies dependency upgrades currently only to Spring Data Build.
* Report store-specific dependencies to Spring Boot's current upgrade ticket ([sample](https://github.com/spring-projects/spring-boot/issues/24036)) `$ dependency report $trainIteration`
* Apply dependency upgrade with `$ dependency upgrade $trainIteration`. Applies dependency
upgrades currently only to Spring Data Build.
* Report store-specific dependencies to Spring Boot's current upgrade
ticket ([sample](https://github.com/spring-projects/spring-boot/issues/24036)) `$ dependency report $trainIteration`
#### CI Properties Distribution
To distribute `ci/pipeline.properties` across all modules use:
`$ infra distribute ci-properties $trainIteration`

View File

@@ -15,8 +15,10 @@
*/
package org.springframework.data.release.dependency;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -42,7 +44,6 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.data.release.CliComponent;
import org.springframework.data.release.build.Pom;
import org.springframework.data.release.git.GitOperations;
import org.springframework.data.release.io.Workspace;
@@ -59,6 +60,7 @@ import org.springframework.data.release.utils.Logger;
import org.springframework.data.util.Streamable;
import org.springframework.http.ResponseEntity;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestOperations;
@@ -72,8 +74,9 @@ import org.xmlbeam.io.XBStreamInput;
*
* @author Mark Paluch
*/
@CliComponent
@Component
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class DependencyOperations {
public static final Pattern REPO_MAVEN_ORG_DIR_LISTING = Pattern
@@ -341,7 +344,7 @@ public class DependencyOperations {
action.accept(dependency, dependencyVersion);
gitOperations.commit(module, upgradeTicket, upgradeTicketSummary, Optional.empty());
gitOperations.commit(module, upgradeTicket, upgradeTicketSummary, Optional.empty(), true);
ticketsToClose.add(upgradeTicket);
});

View File

@@ -57,6 +57,7 @@ public class InfrastructureCommands extends TimedCommand {
DependencyOperations operations;
ExecutorService executor;
GitOperations git;
InfrastructureOperations infra;
Logger logger;
@CliCommand(value = "infra maven check")
@@ -120,4 +121,15 @@ public class InfrastructureCommands extends TimedCommand {
return DependencyUpgradeProposals.fromProperties(iteration, properties);
}
@CliCommand(value = "infra distribute ci-properties")
public void distributeCiProperties(@CliOption(key = "", mandatory = true) TrainIteration iteration)
throws IOException, InterruptedException {
logger.log(iteration, "Distributing CI properties for Spring Data…");
git.checkout(iteration.getTrain(), true);
infra.distributeCiProperties(iteration);
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2022 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
*
* 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.dependency;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import java.io.File;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import org.apache.commons.io.FileUtils;
import org.springframework.data.release.git.Branch;
import org.springframework.data.release.git.GitOperations;
import org.springframework.data.release.io.Workspace;
import org.springframework.data.release.model.Module;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.model.Projects;
import org.springframework.data.release.model.Train;
import org.springframework.data.release.model.TrainIteration;
import org.springframework.data.release.utils.ExecutionUtils;
import org.springframework.data.util.Streamable;
import org.springframework.stereotype.Component;
/**
* @author Mark Paluch
*/
@Component
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class InfrastructureOperations {
public static final String CI_PROPERTIES = "ci/pipeline.properties";
Workspace workspace;
GitOperations git;
ExecutorService executor;
/**
* Distribute {@link #CI_PROPERTIES} from {@link Projects#BUILD} to all modules within {@link TrainIteration}.
*
* @param iteration
*/
void distributeCiProperties(TrainIteration iteration) {
File master = workspace.getFile(CI_PROPERTIES, Projects.BUILD);
if (!master.exists()) {
throw new IllegalStateException(String.format("CI Properties file %s does not exist", master));
}
ExecutionUtils.run(executor, iteration, module -> {
Project project = module.getProject();
Branch branch = Branch.from(module);
git.update(project);
git.checkout(project, branch);
});
verifyExistingPropertyFiles(iteration.getTrain(), master);
ExecutionUtils.run(executor, Streamable.of(iteration.getModulesExcept(Projects.BUILD)), module -> {
File target = workspace.getFile(CI_PROPERTIES, module.getProject());
target.delete();
FileUtils.copyFile(master, target);
git.add(module.getProject(), CI_PROPERTIES);
git.commit(module, "Update CI properties.", Optional.empty(), false);
// git.push(iteration);
});
}
private void verifyExistingPropertyFiles(Train train, File master) {
for (Module module : train) {
File target = workspace.getFile(CI_PROPERTIES, module.getProject());
if (!target.exists()) {
throw new IllegalStateException(String.format("CI Properties file %s does not exist", master));
}
}
}
}

View File

@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CherryPickResult;
import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
@@ -37,6 +38,7 @@ 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.EmptyCommitException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.lib.ObjectId;
@@ -633,7 +635,7 @@ public class GitOperations {
() -> String.format("No issue tracker found for project %s!", project));
Ticket ticket = tracker.getReleaseTicketFor(module);
commit(module, ticket, summary, details);
commit(module, ticket, summary, details, true);
}
/**
@@ -644,7 +646,28 @@ public class GitOperations {
* @param summary must not be {@literal null} or empty.
* @param details can be {@literal null} or empty.
*/
public void commit(ModuleIteration module, Ticket ticket, String summary, Optional<String> details) {
public void commit(ModuleIteration module, String summary, Optional<String> details, boolean all) {
Assert.notNull(module, "Module iteration must not be null!");
Assert.hasText(summary, "Summary must not be null or empty!");
Project project = module.getProject();
IssueTracker tracker = issueTracker.getRequiredPluginFor(project,
() -> String.format("No issue tracker found for project %s!", project));
Ticket ticket = tracker.getReleaseTicketFor(module);
commit(module, ticket, summary, details, all);
}
/**
* Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit
* message. If no files are given, all pending changes are committed.
*
* @param module must not be {@literal null}.
* @param summary must not be {@literal null} or empty.
* @param details can be {@literal null} or empty.
*/
public void commit(ModuleIteration module, Ticket ticket, String summary, Optional<String> details, boolean all) {
Assert.notNull(module, "Module iteration must not be null!");
Assert.hasText(summary, "Summary must not be null or empty!");
@@ -654,6 +677,7 @@ public class GitOperations {
Commit commit = new Commit(ticket, summary, details);
String author = gitProperties.getAuthor();
String email = gitProperties.getEmail();
boolean allowEmpty = all;
logger.log(module, "git commit -m \"%s\" %s --author=\"%s <%s>\"", commit.getSummary(),
gpg.isGpgAvailable() ? "-S" + gpg.getKeyname() : "", author, email);
@@ -664,7 +688,8 @@ public class GitOperations {
.setMessage(commit.toString())//
.setAuthor(author, email)//
.setCommitter(author, email)//
.setAll(true);
.setAllowEmpty(allowEmpty) //
.setAll(all);
if (gpg.isGpgAvailable()) {
commitCommand.setSign(true).setSigningKey(gpg.getKeyname())
@@ -673,6 +698,31 @@ public class GitOperations {
commitCommand.setSign(false);
}
try {
commitCommand.call();
} catch (EmptyCommitException e) {
// allowed if not all
}
});
}
/**
* Adds the {@code filepattern} to the staging area.
*
* @param project must not be {@literal null}.
* @param filepattern must not be {@literal null} or empty.
*/
public void add(Project project, String filepattern) {
Assert.notNull(project, "Project must not be null!");
logger.log(project, "git add \"filepattern\"");
doWithGit(project, git -> {
AddCommand commitCommand = git.add()//
.addFilepattern(filepattern);
commitCommand.call();
});
}