Updating samples after the meta-release
fixes gh-95
This commit is contained in:
@@ -241,4 +241,13 @@ public class Releaser {
|
||||
log.warn("\nUnable to update and generate release train documentation", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAllSamples(Projects projects) {
|
||||
try {
|
||||
this.postReleaseActions.updateAllTestSamples(projects);
|
||||
log.info("\nSuccessfully updated all samples");
|
||||
} catch (Exception e) {
|
||||
log.warn("\nUnable to update all samples", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +238,25 @@ public class ReleaserProperties implements Serializable {
|
||||
*/
|
||||
private boolean updateReleaseTrainDocs = true;
|
||||
|
||||
/**
|
||||
* If set to {@code false}, will not clone and update the samples for all projects
|
||||
*/
|
||||
private boolean updateAllTestSamples = true;
|
||||
|
||||
private Map<String, List<String>> allTestSampleUrls = new HashMap<String, List<String>>() {
|
||||
{
|
||||
this.put("spring-cloud-sleuth" , Arrays.asList(
|
||||
"https://github.com/spring-cloud-samples/sleuth-issues/",
|
||||
"https://github.com/spring-cloud-samples/sleuth-documentation-apps/")
|
||||
);
|
||||
this.put("spring-cloud-contract" , Arrays.asList(
|
||||
"https://github.com/spring-cloud-samples/spring-cloud-contract-samples/",
|
||||
"https://github.com/spring-cloud-samples/the-legacy-app/",
|
||||
"https://github.com/spring-cloud-samples/sc-contract-car-rental/")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public String getReleaseTrainBomUrl() {
|
||||
return this.releaseTrainBomUrl;
|
||||
}
|
||||
@@ -306,6 +325,14 @@ public class ReleaserProperties implements Serializable {
|
||||
return this.runUpdatedSamples;
|
||||
}
|
||||
|
||||
public boolean isUpdateAllTestSamples() {
|
||||
return this.updateAllTestSamples;
|
||||
}
|
||||
|
||||
public void setUpdateAllTestSamples(boolean updateAllTestSamples) {
|
||||
this.updateAllTestSamples = updateAllTestSamples;
|
||||
}
|
||||
|
||||
public void setRunUpdatedSamples(boolean runUpdatedSamples) {
|
||||
this.runUpdatedSamples = runUpdatedSamples;
|
||||
}
|
||||
@@ -398,6 +425,14 @@ public class ReleaserProperties implements Serializable {
|
||||
this.updateReleaseTrainDocs = updateReleaseTrainDocs;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAllTestSampleUrls() {
|
||||
return this.allTestSampleUrls;
|
||||
}
|
||||
|
||||
public void setAllTestSampleUrls(Map<String, List<String>> allTestSampleUrls) {
|
||||
this.allTestSampleUrls = allTestSampleUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Git{" +
|
||||
@@ -413,6 +448,7 @@ public class ReleaserProperties implements Serializable {
|
||||
", numberOfCheckedMilestones=" + this.numberOfCheckedMilestones +
|
||||
", updateSpringGuides=" + this.updateSpringGuides +
|
||||
", updateSpringProject=" + this.updateSpringProject +
|
||||
", sampleUrlsSize=" + this.allTestSampleUrls.size() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -612,7 +648,14 @@ public class ReleaserProperties implements Serializable {
|
||||
* version. Then it's enough to do the mapping like this for this Releaser's property:
|
||||
* {@code verifierVersion=spring-cloud-contract}
|
||||
*/
|
||||
private Map<String, String> gradlePropsSubstitution = new HashMap<>();
|
||||
private Map<String, String> gradlePropsSubstitution = new HashMap<String, String>() {
|
||||
{
|
||||
this.put("bootVersion", "spring-boot");
|
||||
this.put("BOOT_VERSION", "spring-boot");
|
||||
this.put("bomVersion", "spring-cloud-release");
|
||||
this.put("BOM_VERSION", "spring-cloud-release");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* List of regular expressions of ignored gradle props.
|
||||
|
||||
@@ -146,7 +146,11 @@ class GitRepo {
|
||||
try(Git git = this.gitFactory.open(file(this.basedir))) {
|
||||
List<Ref> refs = git.branchList().setListMode(ListBranchCommand.ListMode.ALL)
|
||||
.call();
|
||||
return refs.stream().anyMatch(ref -> branch.equals(nameOfBranch(ref.getName())));
|
||||
boolean present = refs.stream().anyMatch(ref -> branch.equals(nameOfBranch(ref.getName())));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Branch [{}] is present [{}]", branch, present);
|
||||
}
|
||||
return present;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.springframework.cloud.release.internal.git;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
import org.slf4j.Logger;
|
||||
@@ -119,14 +120,44 @@ public class ProjectGitHandler implements ReleaserPropertiesAware {
|
||||
if (StringUtils.isEmpty(version)) {
|
||||
throw new IllegalStateException("You haven't provided a version for project [" + projectName + "]");
|
||||
}
|
||||
String branchFromVersion = branchFromVersion(version);
|
||||
boolean branchExists = gitRepo(clonedProject).hasBranch(branchFromVersion);
|
||||
if (!branchExists) {
|
||||
log.info("Branch [{}] does not exist. Assuming that should work with master branch", branchFromVersion);
|
||||
return findAndCheckOutBranchForVersion(clonedProject, new String[] { version });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* From the version analyzes the branch and checks it out. E.g.
|
||||
* - for spring-cloud-release’s `Finchley.RELEASE version will resolve either Finchley
|
||||
* branch or will fallback to master if there’s no Finchley branch
|
||||
* - for spring-cloud-sleuth’s `2.1.0.RELEASE version will resolve 2.1.x branch
|
||||
*
|
||||
* @param url - of project to clone
|
||||
* @param versions - list of versions to check against, if none matches, will fallback to master
|
||||
* @return location of the cloned project
|
||||
*/
|
||||
public File cloneAndGuessBranch(String url, String... versions) {
|
||||
File clonedProject = cloneProject(url);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Successfully cloned the project to [{}]", clonedProject);
|
||||
}
|
||||
return findAndCheckOutBranchForVersion(clonedProject, versions);
|
||||
}
|
||||
|
||||
private File findAndCheckOutBranchForVersion(File clonedProject, String[] versions) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Checking versions {} for project [{}]", versions, clonedProject);
|
||||
}
|
||||
String branchToCheckout = Arrays.stream(versions)
|
||||
.map(this::branchFromVersion)
|
||||
.map(version -> gitRepo(clonedProject).hasBranch(version) ? version : "")
|
||||
.filter(StringUtils::hasText)
|
||||
.findFirst()
|
||||
.orElse("master");
|
||||
if ("master".equals(branchToCheckout)) {
|
||||
log.info("None of the versions {} matches a branch. Assuming that should work with master branch", (Object) versions);
|
||||
return clonedProject;
|
||||
}
|
||||
log.info("Branch [{}] exists. Will check it out", branchFromVersion);
|
||||
checkout(clonedProject, branchFromVersion);
|
||||
log.info("Branch [{}] exists. Will check it out", branchToCheckout);
|
||||
checkout(clonedProject, branchToCheckout);
|
||||
return clonedProject;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,19 @@ public class ProjectVersion {
|
||||
}
|
||||
|
||||
public String bumpedVersion() {
|
||||
return bumpedVersion(assertVersion());
|
||||
}
|
||||
|
||||
private String bumpedVersion(String[] splitVersion) {
|
||||
if (splitVersion.length == 2 && !isNumeric(splitVersion[0])) {
|
||||
return this.version;
|
||||
}
|
||||
Integer incrementedPatch = Integer.valueOf(splitVersion[2]) + 1;
|
||||
return String.format("%s.%s.%s.%s", splitVersion[0], splitVersion[1], incrementedPatch, splitVersion[3]);
|
||||
}
|
||||
|
||||
|
||||
private String[] assertVersion() {
|
||||
if (this.version == null) {
|
||||
throw new IllegalStateException("Version can't be null!");
|
||||
}
|
||||
@@ -49,11 +62,26 @@ public class ProjectVersion {
|
||||
if (splitVersion.length < 4 && isNumeric(splitVersion[0])) {
|
||||
throw new IllegalStateException("Version is invalid. Should be of format [1.2.3.A]");
|
||||
}
|
||||
if (splitVersion.length == 2 && !isNumeric(splitVersion[0])) {
|
||||
return this.version;
|
||||
return splitVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* For GA and SR will bump the snapshots
|
||||
* in the rest of cases will return snapshot of the current version
|
||||
* @return
|
||||
*/
|
||||
public String postReleaseSnapshotVersion() {
|
||||
String[] strings = assertVersion();
|
||||
if (isReleaseOrServiceRelease()) {
|
||||
String bumpedVersion = bumpedVersion(strings);
|
||||
return appendBuildSnapshot(bumpedVersion);
|
||||
}
|
||||
Integer incrementedPatch = Integer.valueOf(splitVersion[2]) + 1;
|
||||
return String.format("%s.%s.%s.%s", splitVersion[0], splitVersion[1], incrementedPatch, splitVersion[3]);
|
||||
return appendBuildSnapshot(this.version);
|
||||
}
|
||||
|
||||
private String appendBuildSnapshot(String bumpedVersion) {
|
||||
int lastIndexOfDot = bumpedVersion.lastIndexOf(".");
|
||||
return bumpedVersion.substring(0, lastIndexOfDot) + ".BUILD-SNAPSHOT";
|
||||
}
|
||||
|
||||
private boolean isNumeric(String string) {
|
||||
|
||||
@@ -38,6 +38,24 @@ public class Projects extends HashSet<ProjectVersion> {
|
||||
return super.add(projectVersion);
|
||||
}
|
||||
|
||||
public Projects filter(List<String> projectsToSkip) {
|
||||
return this.stream()
|
||||
.filter(v -> !projectsToSkip.contains(v.projectName))
|
||||
.collect(Collectors.toCollection(Projects::new));
|
||||
}
|
||||
|
||||
public Projects postReleaseSnapshotVersion(List<String> projectsToSkip) {
|
||||
Projects projects = this.stream()
|
||||
.filter(v -> projectsToSkip.contains(v.projectName))
|
||||
.collect(Collectors.toCollection(Projects::new));
|
||||
Projects bumped = this.stream()
|
||||
.map(v -> new ProjectVersion(v.projectName, v.postReleaseSnapshotVersion()))
|
||||
.collect(Collectors.toCollection(Projects::new));
|
||||
Projects merged = new Projects(projects);
|
||||
merged.addAll(bumped);
|
||||
return merged;
|
||||
}
|
||||
|
||||
public void remove(String projectName) {
|
||||
ProjectVersion projectVersion = forName(projectName);
|
||||
remove(projectVersion);
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
package org.springframework.cloud.release.internal.post;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -15,10 +25,12 @@ import org.springframework.cloud.release.internal.project.ProjectBuilder;
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class PostReleaseActions {
|
||||
public class PostReleaseActions implements Closeable {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PostReleaseActions.class);
|
||||
|
||||
private static final ExecutorService SERVICE = Executors.newCachedThreadPool();
|
||||
|
||||
private final ProjectGitHandler projectGitHandler;
|
||||
private final ProjectPomUpdater projectPomUpdater;
|
||||
private final ProjectBuilder projectBuilder;
|
||||
@@ -55,6 +67,105 @@ public class PostReleaseActions {
|
||||
this.projectBuilder.build(projectVersion, file.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones all samples for the given project. For each of them, checks out the proper
|
||||
* branch, updates all the poms with the new, bumped versions of release train projects,
|
||||
* commits the changes and pushes them.
|
||||
*
|
||||
* @param projects - set of project with versions to assert against
|
||||
*/
|
||||
public void updateAllTestSamples(Projects projects) {
|
||||
if (!this.properties.getGit().isUpdateAllTestSamples() ||
|
||||
!this.properties.getMetaRelease().isEnabled()) {
|
||||
log.info("Will not update all test samples, since the switch to do so "
|
||||
+ "is off. Set [releaser.git.update-all-test-samples] to [true] to change that");
|
||||
return;
|
||||
}
|
||||
List<ProjectAndException> projectAndExceptions = this.properties.getGit()
|
||||
.getAllTestSampleUrls()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> updateAllProjects(projects, e))
|
||||
.map(this::getResult)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
log.info("Updated all samples!");
|
||||
List<String> exceptionMessages = projectAndExceptions.stream()
|
||||
.filter(ProjectAndException::hasException)
|
||||
.map(e -> "Project [" + e.key + "] for url [" + e.url + "] has exception [" + e.ex + "]")
|
||||
.collect(Collectors.toList());
|
||||
if (!exceptionMessages.isEmpty()) {
|
||||
log.warn("Exceptions were found while updating samples");
|
||||
log.warn(String.join("\n", exceptionMessages));
|
||||
} else {
|
||||
log.info("No exceptions were found while updating the samples");
|
||||
}
|
||||
}
|
||||
|
||||
private Future<List<ProjectAndException>> updateAllProjects(Projects projects, Map.Entry<String, List<String>> e) {
|
||||
return SERVICE.submit(() -> {
|
||||
String key = e.getKey();
|
||||
List<String> value = e.getValue();
|
||||
log.info("Running version update for project [{}] and samples {}", key, value);
|
||||
ProjectVersion projectVersionForReleaseTrain = projects.forName(key);
|
||||
Projects postRelease = projects
|
||||
.postReleaseSnapshotVersion(this.properties.getMetaRelease().getProjectsToSkip());
|
||||
log.info("Versions to update the samples with \n" + postRelease.stream()
|
||||
.map(v -> "[" + v.projectName + ":" + v.version + "]")
|
||||
.collect(Collectors.joining("\n")));
|
||||
return value.stream()
|
||||
.map(url -> run(key, url, () ->
|
||||
commitUpdatedProject(projects, key, projectVersionForReleaseTrain, postRelease, url)))
|
||||
.map(this::getResult)
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
private void commitUpdatedProject(Projects projects, String key, ProjectVersion projectVersionForReleaseTrain, Projects postRelease, String url) {
|
||||
String releaseTrainVersion = projects
|
||||
.forName(this.properties.getMetaRelease().getReleaseTrainProjectName()).version;
|
||||
String projectVersion = projects.forName(key).version;
|
||||
log.info("Running version update for project [{}], url [{}], "
|
||||
+ "release train version [{}] and project version [{}]", key, url,
|
||||
releaseTrainVersion, projectVersion);
|
||||
File file = this.projectGitHandler
|
||||
.cloneAndGuessBranch(url, releaseTrainVersion, projectVersion);
|
||||
Projects newPostRelease = new Projects(postRelease);
|
||||
newPostRelease.add(new ProjectVersion(file));
|
||||
this.projectPomUpdater
|
||||
.updateProjectFromReleaseTrain(file, newPostRelease,
|
||||
new ProjectVersion(file), false);
|
||||
this.projectGitHandler
|
||||
.commit(file, "Updated versions after [" + releaseTrainVersion + "] "
|
||||
+ "release train and [" + projectVersionForReleaseTrain.version + "] ["
|
||||
+ key + "] project release");
|
||||
}
|
||||
|
||||
private ProjectAndFuture run(String key, String url, Runnable runnable) {
|
||||
return new ProjectAndFuture(key, url, SERVICE.submit(runnable));
|
||||
}
|
||||
|
||||
private ProjectAndException getResult(ProjectAndFuture projectAndFuture) {
|
||||
Exception e = null;
|
||||
try {
|
||||
projectAndFuture.future.get(10, TimeUnit.MINUTES);
|
||||
log.info("Done!");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
e = ex;
|
||||
}
|
||||
return new ProjectAndException(projectAndFuture.key, projectAndFuture.url, e);
|
||||
}
|
||||
|
||||
private List<ProjectAndException> getResult(Future<List<ProjectAndException>> future) {
|
||||
try {
|
||||
return future.get(10, TimeUnit.MINUTES);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the release train documentation project
|
||||
*
|
||||
@@ -83,4 +194,38 @@ public class PostReleaseActions {
|
||||
newProjects.add(new ProjectVersion(projectVersion.projectName, releaseTrainVersion));
|
||||
return newProjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
SERVICE.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ProjectAndFuture {
|
||||
final String key;
|
||||
final String url;
|
||||
final Future future;
|
||||
|
||||
ProjectAndFuture(String key, String url, Future future) {
|
||||
this.key = key;
|
||||
this.url = url;
|
||||
this.future = future;
|
||||
}
|
||||
}
|
||||
|
||||
class ProjectAndException {
|
||||
final String key;
|
||||
final String url;
|
||||
final Exception ex;
|
||||
|
||||
ProjectAndException(String key, String url, Exception ex) {
|
||||
this.key = key;
|
||||
this.url = url;
|
||||
this.ex = ex;
|
||||
}
|
||||
|
||||
boolean hasException() {
|
||||
return ex != null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user