Updating samples after the meta-release

fixes gh-95
This commit is contained in:
Marcin Grzejszczak
2018-10-29 10:14:53 +01:00
parent c48058df6d
commit 21d5ccc1c8
12 changed files with 487 additions and 15 deletions

View File

@@ -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);
}
}
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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-releases `Finchley.RELEASE version will resolve either Finchley
* branch or will fallback to master if theres no Finchley branch
* - for spring-cloud-sleuths `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;
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;
}
}