creates release notes in wiki; fixes gh-92

This commit is contained in:
Marcin Grzejszczak
2018-10-29 13:30:16 +01:00
parent 553a2e7695
commit 874824be16
86 changed files with 3395 additions and 29 deletions

View File

@@ -250,4 +250,13 @@ public class Releaser {
log.warn("\nUnable to update all samples", e);
}
}
public void updateReleaseTrainWiki(Projects projects) {
try {
this.documentationUpdater.updateReleaseTrainWiki(projects);
log.info("\nSuccessfully updated project wiki");
} catch (Exception e) {
log.warn("\nUnable to update project wiki", e);
}
}
}

View File

@@ -161,6 +161,11 @@ public class ReleaserProperties implements Serializable {
*/
private String releaseTrainDocsUrl = "https://github.com/spring-cloud-samples/scripts";
/**
* URL to the release train wiki
*/
private String releaseTrainWikiUrl = "https://github.com/spring-projects/spring-cloud.wiki.git";
/**
* Branch to check out for the documentation project
*/
@@ -181,6 +186,12 @@ public class ReleaserProperties implements Serializable {
*/
private String releaseTrainDocsBranch = "master";
/**
* Page prefix for the release train wiki. E.g. for [Spring-Cloud-Finchley-Release-Notes]
* it would be [Spring-Cloud].
*/
private String releaseTrainWikiPagePrefix = "Spring-Cloud";
/**
* Where should the Spring Cloud Release repo get cloned to. If {@code null} defaults to a temporary directory
*/
@@ -238,6 +249,11 @@ public class ReleaserProperties implements Serializable {
*/
private boolean updateReleaseTrainDocs = true;
/**
* If set to {@code false}, will not clone and update the release train wiki
*/
private boolean updateReleaseTrainWiki = true;
/**
* If set to {@code false}, will not clone and update the samples for all projects
*/
@@ -433,15 +449,41 @@ public class ReleaserProperties implements Serializable {
this.allTestSampleUrls = allTestSampleUrls;
}
public String getReleaseTrainWikiUrl() {
return this.releaseTrainWikiUrl;
}
public void setReleaseTrainWikiUrl(String releaseTrainWikiUrl) {
this.releaseTrainWikiUrl = releaseTrainWikiUrl;
}
public boolean isUpdateReleaseTrainWiki() {
return this.updateReleaseTrainWiki;
}
public void setUpdateReleaseTrainWiki(boolean updateReleaseTrainWiki) {
this.updateReleaseTrainWiki = updateReleaseTrainWiki;
}
public String getReleaseTrainWikiPagePrefix() {
return this.releaseTrainWikiPagePrefix;
}
public void setReleaseTrainWikiPagePrefix(String releaseTrainWikiPagePrefix) {
this.releaseTrainWikiPagePrefix = releaseTrainWikiPagePrefix;
}
@Override
public String toString() {
return "Git{" +
"releaseTrainBomUrl='" + this.releaseTrainBomUrl + '\'' +
", documentationUrl='" + this.documentationUrl + '\'' +
", documentationBranch='" + this.documentationBranch + '\'' +
", releaseTrainWikiUrl='" + this.releaseTrainWikiUrl + '\'' +
", updateDocumentationRepo=" + this.updateDocumentationRepo +
", springProjectUrl=" + this.springProjectUrl+
", springProjectBranch=" + this.springProjectBranch +
", releaseTrainWikiPagePrefix=" + this.releaseTrainWikiPagePrefix +
", cloneDestinationDir='" + this.cloneDestinationDir + '\'' +
", fetchVersionsFromGit=" + this.fetchVersionsFromGit +
", oauthToken='" + this.oauthToken + '\'' +

View File

@@ -7,6 +7,7 @@ import org.springframework.cloud.release.internal.ReleaserPropertiesAware;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.Projects;
import org.springframework.cloud.release.internal.template.TemplateGenerator;
/**
* @author Marcin Grzejszczak
@@ -17,10 +18,12 @@ public class DocumentationUpdater implements ReleaserPropertiesAware {
private final ReleaseTrainContentsUpdater releaseTrainContentsUpdater;
private ReleaserProperties properties;
public DocumentationUpdater(ProjectGitHandler gitHandler, ReleaserProperties properties) {
public DocumentationUpdater(ProjectGitHandler gitHandler, ReleaserProperties properties,
TemplateGenerator templateGenerator) {
this.properties = properties;
this.projectDocumentationUpdater = new ProjectDocumentationUpdater(this.properties, gitHandler);
this.releaseTrainContentsUpdater = new ReleaseTrainContentsUpdater(this.properties, gitHandler);
this.releaseTrainContentsUpdater = new ReleaseTrainContentsUpdater(this.properties, gitHandler,
templateGenerator);
}
DocumentationUpdater(ReleaserProperties properties, ProjectDocumentationUpdater updater,
@@ -54,6 +57,16 @@ public class DocumentationUpdater implements ReleaserPropertiesAware {
return this.releaseTrainContentsUpdater.updateProjectRepo(projects);
}
/**
* Updates the release train wiki page
*
* @param projects
* @return {@link File cloned temporary directory} - {@code null} if wrong version is used or the switch is turned off
*/
public File updateReleaseTrainWiki(Projects projects) {
return this.releaseTrainContentsUpdater.updateReleaseTrainWiki(projects);
}
@Override
public void setReleaserProperties(ReleaserProperties properties) {
this.properties = properties;

View File

@@ -6,6 +6,7 @@ import java.nio.file.Files;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import com.github.jknack.handlebars.Template;
@@ -19,6 +20,7 @@ import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.Projects;
import org.springframework.cloud.release.internal.tech.HandlebarsHelper;
import org.springframework.cloud.release.internal.template.TemplateGenerator;
import org.springframework.util.StringUtils;
/**
@@ -33,10 +35,12 @@ class ReleaseTrainContentsUpdater implements ReleaserPropertiesAware {
private final ReleaseTrainContentsGitHandler handler;
private final ReleaseTrainContentsParser parser;
private final ReleaseTrainContentsGenerator generator;
private final TemplateGenerator templateGenerator;
ReleaseTrainContentsUpdater(ReleaserProperties properties, ProjectGitHandler handler) {
ReleaseTrainContentsUpdater(ReleaserProperties properties, ProjectGitHandler handler, TemplateGenerator templateGenerator) {
this.properties = properties;
this.handler = new ReleaseTrainContentsGitHandler(handler);
this.templateGenerator = templateGenerator;
this.parser = new ReleaseTrainContentsParser();
this.generator = new ReleaseTrainContentsGenerator(properties);
}
@@ -55,7 +59,7 @@ class ReleaseTrainContentsUpdater implements ReleaserPropertiesAware {
}
File releaseTrainProject = this.handler.cloneSpringDocProject();
File index = new File(releaseTrainProject, "index.html");
ReleaseTrainContents contents = this.parser.parse(index);
ReleaseTrainContents contents = this.parser.parseProjectPage(index);
String newContents = this.generator.releaseTrainContents(contents, projects);
if (StringUtils.isEmpty(newContents)) {
log.info("No changes to commit to the Spring Project page.");
@@ -79,6 +83,94 @@ class ReleaseTrainContentsUpdater implements ReleaserPropertiesAware {
}
}
/**
* Clones the test project, updates it and runs tests
*
* @param projects - set of project with versions to assert against
*/
File updateReleaseTrainWiki(Projects projects) {
if (!this.properties.getGit().isUpdateReleaseTrainWiki() ||
!this.properties.getMetaRelease().isEnabled()) {
log.info("Will not clone and update the release train wiki, since the switch to do so "
+ "is off or it's not a meta-release. Set [releaser.git.update-release-train-wiki] to [true] to change that");
return null;
}
File releaseTrainWiki = this.handler.cloneReleaseTrainWiki();
ProjectVersion releaseTrain = projects.releaseTrain(this.properties);
String releaseTrainName = releaseTrain.major();
String wikiPagePrefix = this.properties.getGit().getReleaseTrainWikiPagePrefix();
String releaseTrainDocFileName = releaseTrainDocFileName(releaseTrainName, wikiPagePrefix);
log.info("Reading the file [{}] for the current release train", releaseTrainDocFileName);
File releaseTrainDocFile = releaseTrainDocFile(releaseTrainWiki, releaseTrainDocFileName);
String releaseVersionFromCurrentFile = this.parser.latestReleaseTrainFromWiki(releaseTrainDocFile);
log.info("Latest release train version in the file is [{}]", releaseVersionFromCurrentFile);
if (!isThisReleaseTrainVersionNewer(releaseTrain, releaseVersionFromCurrentFile)) {
log.info("Current release train version [{}] is not "
+ "newer than the version taken from the wiki [{}]",
releaseTrain.version, releaseVersionFromCurrentFile);
return releaseTrainWiki;
}
return generateNewWikiEntry(projects, releaseTrainWiki, releaseTrain,
releaseTrainName, releaseTrainDocFile, releaseVersionFromCurrentFile);
}
private File generateNewWikiEntry(Projects projects, File releaseTrainWiki, ProjectVersion releaseTrain, String releaseTrainName, File releaseTrainDocFile, String releaseVersionFromCurrentFile) {
File releaseNotes = this.templateGenerator.releaseNotes(projects);
try {
List<String> lines = Files.readAllLines(releaseTrainDocFile.toPath());
int lineIndex;
for (lineIndex = 0; lineIndex < lines.size(); lineIndex++) {
if (lines.get(lineIndex).contains(releaseVersionFromCurrentFile)) {
break;
}
}
return insertNewWikiContentBeforeTheLatestRelease(releaseTrainWiki, releaseTrain,
releaseTrainName, releaseTrainDocFile, releaseNotes, lines, lineIndex);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
private File insertNewWikiContentBeforeTheLatestRelease(File releaseTrainWiki, ProjectVersion releaseTrain, String releaseTrainName, File releaseTrainDocFile, File releaseNotes, List<String> lines, int lineIndex) throws IOException {
String newContent = new StringJoiner("\n")
.add(String.join("\n", lines.subList(0, lineIndex)))
.add("\n").add(new String(Files.readAllBytes(releaseNotes.toPath())))
.add(String.join("\n", lines.subList(lineIndex, lines.size())))
.toString();
Files.write(releaseTrainDocFile.toPath(), newContent.getBytes());
log.info("Successfully stored new wiki contents for release train [{}]", releaseTrainName);
this.handler.commitAndPushChanges(releaseTrainWiki, releaseTrain);
return releaseTrainWiki;
}
private String releaseTrainDocFileName(String releaseTrainName, String wikiPagePrefix) {
return new StringJoiner("-")
.add(wikiPagePrefix).add(releaseTrainName).add("Release-Notes.md").toString();
}
private boolean isThisReleaseTrainVersionNewer(ProjectVersion releaseTrain, String releaseVersionFromCurrentFile) {
if (StringUtils.hasText(releaseVersionFromCurrentFile)) {
return releaseTrain.compareToReleaseTrainName(releaseVersionFromCurrentFile) > 0;
}
return true;
}
private File releaseTrainDocFile(File releaseTrainWiki, String releaseTrainDocFileName) {
File releaseTrainDocFile = new File(releaseTrainWiki, releaseTrainDocFileName);
if (!releaseTrainDocFile.exists()) {
try {
if (!releaseTrainDocFile.createNewFile()) {
throw new IllegalStateException("Failed to create releae train doc file");
}
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
return releaseTrainDocFile;
}
@Override
public void setReleaserProperties(ReleaserProperties properties) {
this.properties = properties;
@@ -121,7 +213,7 @@ class ReleaseTrainContentsGenerator implements ReleaserPropertiesAware {
}
ProjectVersion currentReleaseTrainProject(Projects projects) {
return projects.forName(this.properties.getMetaRelease().getReleaseTrainProjectName());
return projects.releaseTrain(this.properties);
}
private String generate(File contentOutput, Template template, ReleaseTrainContents releaseTrainContents) {
@@ -205,11 +297,14 @@ class ReleaseTrainContentsGitHandler {
this.handler = handler;
}
// clones the project and checks out the branch
File cloneSpringDocProject() {
return this.handler.cloneSpringDocProject();
}
File cloneReleaseTrainWiki() {
return this.handler.cloneReleaseTrainWiki();
}
void commitAndPushChanges(File repo, ProjectVersion releaseTrain) {
log.debug("Committing and pushing changes");
this.handler.commit(repo, String.format(PROJECT_PAGE_UPDATED_COMMIT_MSG, releaseTrain.version));
@@ -223,7 +318,7 @@ class ReleaseTrainContentsParser {
private static final Logger log = LoggerFactory
.getLogger(ReleaseTrainContentsParser.class);
ReleaseTrainContents parse(File rawHtml) {
ReleaseTrainContents parseProjectPage(File rawHtml) {
try {
String contents = new String(Files.readAllBytes(rawHtml.toPath()));
String[] split = contents.split("<!-- (BEGIN|END) COMPONENTS -->");
@@ -246,4 +341,18 @@ class ReleaseTrainContentsParser {
throw new IllegalStateException(e);
}
}
String latestReleaseTrainFromWiki(File rawMd) {
try {
return Files.readAllLines(rawMd.toPath()).stream()
.filter(s -> s.trim().startsWith("#"))
.map(s -> s.substring(1).trim())
.filter(s -> new ProjectVersion("foo", s).isValid())
.findFirst()
.orElse("");
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -73,6 +73,10 @@ public class ProjectGitHandler implements ReleaserPropertiesAware {
this.properties.getGit().getTestSamplesBranch());
}
public File cloneReleaseTrainWiki() {
return cloneProject(this.properties.getGit().getReleaseTrainWikiUrl());
}
public File cloneReleaseTrainProject() {
return cloneProject(this.properties.getGit().getReleaseTrainBomUrl());
}

View File

@@ -59,7 +59,8 @@ public class ProjectVersion {
}
// 1.0.0.BUILD-SNAPSHOT
String[] splitVersion = this.version.split("\\.");
if (splitVersion.length < 4 && isNumeric(splitVersion[0])) {
if (splitVersion.length < 4 && isNumeric(splitVersion[0]) ||
splitVersion.length == 1 && !isNumeric(splitVersion[0])) {
throw new IllegalStateException("Version is invalid. Should be of format [1.2.3.A]");
}
return splitVersion;
@@ -101,6 +102,19 @@ public class ProjectVersion {
return "";
}
public boolean isValid() {
try {
assertVersion();
return true;
} catch (IllegalStateException e) {
return false;
}
}
public String major() {
return this.assertVersion()[0];
}
public boolean isSnapshot() {
return this.version != null && this.version.contains("SNAPSHOT");
}

View File

@@ -8,6 +8,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.cloud.release.internal.ReleaserProperties;
/**
* Abstraction over collection of projects
*
@@ -31,6 +33,10 @@ public class Projects extends HashSet<ProjectVersion> {
return newProjects;
}
public ProjectVersion releaseTrain(ReleaserProperties properties) {
return this.forName(properties.getMetaRelease().getReleaseTrainProjectName());
}
@Override public boolean add(ProjectVersion projectVersion) {
if (projectVersion == null) {
return false;

View File

@@ -59,8 +59,7 @@ public class PostReleaseActions implements Closeable {
}
File file = this.projectGitHandler.cloneTestSamplesProject();
ProjectVersion projectVersion = new ProjectVersion(file);
String releaseTrainVersion =
projects.forName(this.properties.getMetaRelease().getReleaseTrainProjectName()).version;
String releaseTrainVersion = projects.releaseTrain(this.properties).version;
Projects newProjects = addVersionForTestsProject(projects, projectVersion, releaseTrainVersion);
this.projectPomUpdater
.updateProjectFromReleaseTrain(file, newProjects, projectVersion, false);
@@ -180,8 +179,7 @@ public class PostReleaseActions implements Closeable {
}
File file = this.projectGitHandler.cloneReleaseTrainDocumentationProject();
ProjectVersion projectVersion = new ProjectVersion(file);
String releaseTrainVersion =
projects.forName(this.properties.getMetaRelease().getReleaseTrainProjectName()).version;
String releaseTrainVersion = projects.releaseTrain(this.properties).version;
Projects newProjects = addVersionForTestsProject(projects, projectVersion, releaseTrainVersion);
this.projectPomUpdater
.updateProjectFromReleaseTrain(file, newProjects, projectVersion, false);

View File

@@ -13,6 +13,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
@@ -32,6 +33,8 @@ class ReleaseNotesTemplateGenerator {
private final Projects projects;
private final NotesGenerator notesGenerator;
static final Map<String, File> CACHE = new ConcurrentHashMap<>();
ReleaseNotesTemplateGenerator(Template template, String releaseVersion,
File blogOutput, Projects projects, ProjectGitHandler handler) {
this.template = template;
@@ -41,7 +44,13 @@ class ReleaseNotesTemplateGenerator {
this.notesGenerator = new NotesGenerator(handler);
}
File releseNotes() {
File releaseNotes() {
File cached = CACHE.get(this.releaseVersion);
if (cached != null) {
log.info("Found an existing entry [{}] in the cache "
+ "for version [{}]", cached, this.releaseVersion);
return cached;
}
try {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("date", LocalDate.now().format(DateTimeFormatter.ISO_DATE))
@@ -50,7 +59,9 @@ class ReleaseNotesTemplateGenerator {
.build();
String blog = this.template.apply(map);
Files.write(this.blogOutput.toPath(), blog.getBytes());
return this.blogOutput;
File output = this.blogOutput;
CACHE.put(this.releaseVersion, output);
return output;
}
catch (IOException e) {
log.warn("Exception occurred while trying to generate release notes", e);

View File

@@ -87,12 +87,12 @@ public class TemplateGenerator implements ReleaserPropertiesAware {
String releaseVersion = parsedVersion(projects);
Template template = template(RELEASE_NOTES_TEMPLATE);
return new ReleaseNotesTemplateGenerator(template, releaseVersion,
output, projects, this.handler).releseNotes();
output, projects, this.handler).releaseNotes();
}
private String parsedVersion(Projects projects) {
if (this.props.getMetaRelease().isEnabled()) {
return projects.forName(this.props.getMetaRelease().getReleaseTrainProjectName()).version;
return projects.releaseTrain(this.props).version;
}
String version = this.props.getPom().getBranch();
if (version.startsWith("v")) {