@@ -223,6 +223,15 @@ To distribute `ci/pipeline.properties` from Spring Data Build across all modules
|
||||
$ infra distribute ci-properties $trainIteration
|
||||
----
|
||||
|
||||
===== GitHub Workflow Distribution
|
||||
|
||||
To distribute `.github/workflows/project.yml` from Spring Data Build across all modules:
|
||||
|
||||
----
|
||||
$ infra distribute gh-workflow $trainIteration
|
||||
----
|
||||
|
||||
Note that your GitHub token to authenticate against GitHub must have the `workflow` permission.
|
||||
|
||||
===== Broken Link Report
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@ import org.eclipse.jgit.transport.CredentialItem;
|
||||
import org.eclipse.jgit.transport.CredentialItem.CharArrayType;
|
||||
import org.eclipse.jgit.transport.CredentialItem.InformationalMessage;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.eclipse.jgit.transport.TagOpt;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
|
||||
@@ -76,6 +78,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Component to execute Git related operations.
|
||||
@@ -270,10 +273,35 @@ public class GitOperations {
|
||||
|
||||
call(git.push() //
|
||||
.setRemote("origin") //
|
||||
.setRefSpecs(new RefSpec(ref.getName())));
|
||||
.setRefSpecs(new RefSpec(ref.getName()))).forEach(pushResult -> {
|
||||
handlePushResult(module, pushResult);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void handlePushResult(ModuleIteration module, PushResult pushResult) {
|
||||
|
||||
Set<RemoteRefUpdate.Status> success = new HashSet<>(Arrays.asList(RemoteRefUpdate.Status.AWAITING_REPORT,
|
||||
RemoteRefUpdate.Status.NOT_ATTEMPTED, RemoteRefUpdate.Status.OK, RemoteRefUpdate.Status.UP_TO_DATE));
|
||||
|
||||
if (StringUtils.hasText(pushResult.getMessages())) {
|
||||
logger.log(module, pushResult.getMessages());
|
||||
}
|
||||
|
||||
for (RemoteRefUpdate remoteUpdate : pushResult.getRemoteUpdates()) {
|
||||
|
||||
if (success.contains(remoteUpdate.getStatus())) {
|
||||
|
||||
logger.log(module, String.format("✅️ Push done: %s %s", remoteUpdate.getStatus(),
|
||||
StringUtils.hasText(remoteUpdate.getMessage()) ? remoteUpdate.getMessage() : ""));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.warn(module, String.format("⚠️ Push failed: %s %s", remoteUpdate.getStatus(), remoteUpdate.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public void pushTags(Train train) {
|
||||
|
||||
ExecutionUtils.run(executor, train.getModules(), module -> {
|
||||
@@ -793,7 +821,7 @@ public class GitOperations {
|
||||
|
||||
Assert.notNull(project, "Project must not be null!");
|
||||
|
||||
logger.log(project, "git add \"filepattern\"");
|
||||
logger.log(project, "git add \"%s\"", filepattern);
|
||||
|
||||
doWithGit(project, git -> {
|
||||
|
||||
|
||||
@@ -107,4 +107,22 @@ public class InfrastructureCommands extends TimedCommand {
|
||||
infra.distributeCiProperties(iteration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute GH workflows across all modules using Build as template.
|
||||
*
|
||||
* @param iteration
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
@CliCommand(value = "infra distribute gh-workflow")
|
||||
public void distributeGhWorkflow(@CliOption(key = "", mandatory = true) TrainIteration iteration)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
logger.log(iteration, "Distributing GH Workflow for Spring Data…");
|
||||
|
||||
git.prepare(iteration);
|
||||
|
||||
infra.distributeGhWorkflow(iteration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import org.springframework.data.release.TimedCommand;
|
||||
import org.springframework.data.release.git.Branch;
|
||||
import org.springframework.data.release.git.GitOperations;
|
||||
@@ -39,10 +41,10 @@ import org.springframework.data.release.model.ModuleIteration;
|
||||
import org.springframework.data.release.model.Project;
|
||||
import org.springframework.data.release.model.Projects;
|
||||
import org.springframework.data.release.model.SupportedProject;
|
||||
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.Predicates;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -70,11 +72,19 @@ public class InfrastructureOperations extends TimedCommand {
|
||||
* @param iteration
|
||||
*/
|
||||
void distributeCiProperties(TrainIteration iteration) {
|
||||
distributeFile(iteration, "ci/pipeline.properties", "CI Properties", Predicates.isTrue());
|
||||
}
|
||||
|
||||
File master = workspace.getFile(CI_PROPERTIES, iteration.getSupportedProject(Projects.BUILD));
|
||||
void distributeGhWorkflow(TrainIteration iteration) {
|
||||
distributeFile(iteration, ".github/workflows/project.yml", "GitHub Actions", project -> project != Projects.BOM);
|
||||
}
|
||||
|
||||
private void distributeFile(TrainIteration iteration, String file, String description,
|
||||
Predicate<Project> projectFilter) {
|
||||
File master = workspace.getFile(file, iteration.getSupportedProject(Projects.BUILD));
|
||||
|
||||
if (!master.exists()) {
|
||||
throw new IllegalStateException(String.format("CI Properties file %s does not exist", master));
|
||||
throw new IllegalStateException(String.format("%s file %s does not exist", description, master));
|
||||
}
|
||||
|
||||
ExecutionUtils.run(executor, iteration, module -> {
|
||||
@@ -86,29 +96,33 @@ public class InfrastructureOperations extends TimedCommand {
|
||||
git.checkout(project, branch);
|
||||
});
|
||||
|
||||
verifyExistingPropertyFiles(iteration.getTrain(), master);
|
||||
Streamable<ModuleIteration> projects = Streamable.of(iteration.getModulesExcept(Projects.BUILD))
|
||||
.filter(it -> projectFilter.test(it.getProject())).filter(it -> it.getProject().getMaintainer().isCore());
|
||||
|
||||
ExecutionUtils.run(executor, Streamable.of(iteration.getModulesExcept(Projects.BUILD)), module -> {
|
||||
verifyExistingFiles(projects, file, description);
|
||||
|
||||
File target = workspace.getFile(CI_PROPERTIES, module.getSupportedProject());
|
||||
ExecutionUtils.run(executor, projects, module -> {
|
||||
|
||||
File target = workspace.getFile(file, module.getSupportedProject());
|
||||
target.delete();
|
||||
|
||||
FileUtils.copyFile(master, target);
|
||||
|
||||
git.add(module.getSupportedProject(), CI_PROPERTIES);
|
||||
git.commit(module, "Update CI properties.", Optional.empty(), false);
|
||||
git.add(module.getSupportedProject(), file);
|
||||
git.commit(module, String.format("Update %s.", description), Optional.empty(), false);
|
||||
git.push(module);
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyExistingPropertyFiles(Train train, File master) {
|
||||
private void verifyExistingFiles(Streamable<ModuleIteration> train, String file, String description) {
|
||||
|
||||
for (SupportedProject project : train) {
|
||||
for (ModuleIteration moduleIteration : train) {
|
||||
|
||||
File target = workspace.getFile(CI_PROPERTIES, project);
|
||||
File target = workspace.getFile(file, moduleIteration.getSupportedProject());
|
||||
|
||||
if (!target.exists()) {
|
||||
throw new IllegalStateException(String.format("CI Properties file %s does not exist", master));
|
||||
throw new IllegalStateException(
|
||||
String.format("%s file %s does not exist in %s", description, file, moduleIteration.getSupportedProject()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public class Project implements Comparable<Project>, Named {
|
||||
private final @With ArtifactCoordinates additionalArtifacts;
|
||||
private final @With boolean skipTests;
|
||||
private final @Getter @With boolean useShortVersionMilestones; // use a short version 2.3.0-RC1 instead of 2.3 RC1 if
|
||||
private final @Getter @With ProjectMaintainer maintainer;
|
||||
// true
|
||||
|
||||
Project(String key, String name, Tracker tracker) {
|
||||
@@ -53,13 +54,14 @@ public class Project implements Comparable<Project>, Named {
|
||||
|
||||
private Project(String key, String name, String fullName, Tracker tracker) {
|
||||
this(new ProjectKey(key), name, fullName, Collections.emptySet(), tracker, ArtifactCoordinates.SPRING_DATA, true,
|
||||
false);
|
||||
false, ProjectMaintainer.CORE);
|
||||
}
|
||||
|
||||
@java.beans.ConstructorProperties({ "key", "name", "fullName", "dependencies", "tracker", "additionalArtifacts",
|
||||
"skipTests", "plainVersionMilestones" })
|
||||
"skipTests", "plainVersionMilestones", "owner" })
|
||||
private Project(ProjectKey key, String name, String fullName, Collection<Project> dependencies, Tracker tracker,
|
||||
ArtifactCoordinates additionalArtifacts, boolean skipTests, boolean useShortVersionMilestones) {
|
||||
ArtifactCoordinates additionalArtifacts, boolean skipTests, boolean useShortVersionMilestones,
|
||||
ProjectMaintainer maintainer) {
|
||||
|
||||
this.key = key;
|
||||
this.name = name;
|
||||
@@ -69,6 +71,7 @@ public class Project implements Comparable<Project>, Named {
|
||||
this.additionalArtifacts = additionalArtifacts;
|
||||
this.skipTests = skipTests;
|
||||
this.useShortVersionMilestones = useShortVersionMilestones;
|
||||
this.maintainer = maintainer;
|
||||
}
|
||||
|
||||
public boolean uses(Tracker tracker) {
|
||||
@@ -110,7 +113,7 @@ public class Project implements Comparable<Project>, Named {
|
||||
|
||||
public Project withDependencies(Project... project) {
|
||||
return new Project(key, name, fullName, Arrays.asList(project), tracker, additionalArtifacts, skipTests,
|
||||
useShortVersionMilestones);
|
||||
useShortVersionMilestones, maintainer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public enum ProjectMaintainer {
|
||||
|
||||
CORE, COMMUNITY;
|
||||
|
||||
public boolean isCore() {
|
||||
return this == CORE;
|
||||
}
|
||||
|
||||
public boolean isCommunity() {
|
||||
return this == COMMUNITY;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -67,20 +67,23 @@ public class Projects {
|
||||
.withAdditionalArtifacts(
|
||||
ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-mongodb-cross-store", "spring-data-mongodb-log4j"));
|
||||
|
||||
NEO4J = new Project("DATAGRAPH", "Neo4j", Tracker.GITHUB).withDependencies(COMMONS);
|
||||
NEO4J = new Project("DATAGRAPH", "Neo4j", Tracker.GITHUB).withDependencies(COMMONS)
|
||||
.withMaintainer(ProjectMaintainer.COMMUNITY);
|
||||
|
||||
SOLR = new Project("DATASOLR", "Solr", Tracker.GITHUB) //
|
||||
.withDependencies(COMMONS) //
|
||||
.withFullName("Spring Data for Apache Solr");
|
||||
|
||||
COUCHBASE = new Project("DATACOUCH", "Couchbase", Tracker.GITHUB).withDependencies(COMMONS);
|
||||
COUCHBASE = new Project("DATACOUCH", "Couchbase", Tracker.GITHUB).withDependencies(COMMONS)
|
||||
.withMaintainer(ProjectMaintainer.COMMUNITY);
|
||||
|
||||
CASSANDRA = new Project("DATACASS", "Cassandra", Tracker.GITHUB) //
|
||||
.withDependencies(COMMONS) //
|
||||
.withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-cql"))
|
||||
.withFullName("Spring Data for Apache Cassandra");
|
||||
|
||||
ELASTICSEARCH = new Project("DATAES", "Elasticsearch", Tracker.GITHUB).withDependencies(COMMONS);
|
||||
ELASTICSEARCH = new Project("DATAES", "Elasticsearch", Tracker.GITHUB).withDependencies(COMMONS)
|
||||
.withMaintainer(ProjectMaintainer.COMMUNITY);
|
||||
|
||||
KEY_VALUE = new Project("DATAKV", "KeyValue", Tracker.GITHUB).withDependencies(COMMONS);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user