ENhanced blog post generation; fixes gh-94

This commit is contained in:
Marcin Grzejszczak
2018-10-29 10:46:28 +01:00
parent 21d5ccc1c8
commit dacba0314e
10 changed files with 140 additions and 128 deletions

View File

@@ -210,7 +210,7 @@ public class ReleaserProperties implements Serializable {
* In order not to iterate endlessly over milestones we introduce a threshold of milestones
* that we will go through to find the matching milestone
*/
private Integer numberOfCheckedMilestones = 10;
private Integer numberOfCheckedMilestones = 50;
/**
* If {@code false}, will not update the documentation repository.

View File

@@ -12,12 +12,14 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Marcin Grzejszczak
@@ -28,6 +30,7 @@ class GithubMilestones {
private final Github github;
private final ReleaserProperties properties;
static final Map<ProjectVersion, String> CACHE = new ConcurrentHashMap<>();
GithubMilestones(ReleaserProperties properties) {
this.github = new RtGithub(new RtGithub(
@@ -88,15 +91,20 @@ class GithubMilestones {
}
String milestoneUrl(ProjectVersion version) {
String cachedUrl = CACHE.get(version);
if (StringUtils.hasText(cachedUrl)) {
return cachedUrl;
}
Assert.hasText(this.properties.getGit().getOauthToken(),
"You have to pass Github OAuth token for milestone closing to be operational");
String tagVersion = version.version;
Milestone.Smart foundMilestone = matchingMilestone(tagVersion, closedMilestones(version));
String foundUrl = "";
if (foundMilestone != null) {
try {
URL url = foundMilestoneUrl(foundMilestone);
log.info("Found a matching milestone with issues URL [{}]", url);
return url.toString()
foundUrl = url.toString()
.replace("https://api.github.com/repos", "https://github.com")
.replace("milestones", "milestone")+ "?closed=1";
}
@@ -105,10 +113,10 @@ class GithubMilestones {
}
catch (Exception e) {
log.error("Exception occurred while trying to find milestone", e);
return "";
}
}
return "";
CACHE.put(version, foundUrl);
return foundUrl;
}
private String numericVersion(String version) {
@@ -151,12 +159,16 @@ class GithubMilestones {
private Map<String, String> openMilestones() {
Map<String, String> params = new HashMap<>();
params.put("state", "open");
params.put("sort", "due_on");
params.put("direction", "desc");
return params;
}
private Map<String, String> closedMilestones() {
Map<String, String> params = new HashMap<>();
params.put("state", "closed");
params.put("sort", "due_on");
params.put("direction", "desc");
return params;
}
}

View File

@@ -11,6 +11,8 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.Projects;
import org.springframework.util.StringUtils;
@@ -32,13 +34,15 @@ class BlogTemplateGenerator {
private final String releaseVersion;
private final File blogOutput;
private final Projects projects;
private final NotesGenerator notesGenerator;
BlogTemplateGenerator(Template template, String releaseVersion, File blogOutput,
Projects projects) {
Projects projects, ProjectGitHandler handler) {
this.template = template;
this.releaseVersion = releaseVersion;
this.blogOutput = blogOutput;
this.projects = projects;
this.notesGenerator = new NotesGenerator(handler);
}
File blog() {
@@ -59,7 +63,7 @@ class BlogTemplateGenerator {
.put("releaseName", releaseName)
.put("releaseLink", releaseLink)
.put("releaseVersion", this.releaseVersion)
.put("projects", fromProjects())
.put("projects", this.notesGenerator.fromProjects(this.projects))
.put("nonRelease", nonRelease)
.build();
String blog = this.template.apply(map);
@@ -72,16 +76,6 @@ class BlogTemplateGenerator {
}
}
private Set<BlogTuple> fromProjects() {
return this.projects.stream().map(projectVersion -> {
String name = projectVersion.projectName;
String version = projectVersion.version;
String convertedName = Arrays.stream(name.split("-")).map(
StringUtils::capitalize).collect(Collectors.joining(" "));
return new BlogTuple(convertedName, version);
}).collect(Collectors.toSet());
}
private String parsedReleaseName(String version) {
return version.substring(0, version.indexOf("."));
}
@@ -121,38 +115,3 @@ class BlogTemplateGenerator {
return "[Maven Central](http://repo1.maven.org/maven2/org/springframework/cloud/spring-cloud-dependencies/" + this.releaseVersion + "/)";
}
}
class BlogTuple {
private final String name;
private final String version;
BlogTuple(String name, String version) {
this.name = name;
this.version = version;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BlogTuple blogTuple = (BlogTuple) o;
if (name != null ? !name.equals(blogTuple.name) : blogTuple.name != null)
return false;
return version != null ? version.equals(blogTuple.version) : blogTuple.version == null;
}
@Override public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (version != null ? version.hashCode() : 0);
return result;
}
}

View File

@@ -0,0 +1,77 @@
package org.springframework.cloud.release.internal.template;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.Projects;
import org.springframework.util.StringUtils;
/**
* @author Marcin Grzejszczak
*/
class NotesGenerator {
private final ProjectGitHandler handler;
NotesGenerator(ProjectGitHandler handler) {
this.handler = handler;
}
Set<Notes> fromProjects(Projects projects) {
return projects.stream().filter(projectVersion ->
!projectVersion.projectName.toLowerCase().contains("boot")
).map(projectVersion -> {
String name = projectVersion.projectName;
String version = projectVersion.version;
String closedMilestoneUrl = this.handler.milestoneUrl(projectVersion);
String convertedName = Arrays.stream(name.split("-"))
.map(StringUtils::capitalize).collect(Collectors.joining(" "));
return new Notes(convertedName, version, closedMilestoneUrl);
}).collect(Collectors.toSet());
}
}
class Notes {
private final String name;
private final String version;
private final String closedMilestoneUrl;
Notes(String name, String version, String closedMilestoneUrl) {
this.name = name;
this.version = version;
this.closedMilestoneUrl = closedMilestoneUrl;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getClosedMilestoneUrl() {
return closedMilestoneUrl;
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Notes notes = (Notes) o;
if (name != null ? !name.equals(notes.name) : notes.name != null)
return false;
return version != null ?
version.equals(notes.version) :
notes.version == null;
}
@Override public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (version != null ? version.hashCode() : 0);
return result;
}
}

View File

@@ -30,7 +30,7 @@ class ReleaseNotesTemplateGenerator {
private final String releaseVersion;
private final File blogOutput;
private final Projects projects;
private final ProjectGitHandler handler;
private final NotesGenerator notesGenerator;
ReleaseNotesTemplateGenerator(Template template, String releaseVersion,
File blogOutput, Projects projects, ProjectGitHandler handler) {
@@ -38,7 +38,7 @@ class ReleaseNotesTemplateGenerator {
this.releaseVersion = releaseVersion;
this.blogOutput = blogOutput;
this.projects = projects;
this.handler = handler;
this.notesGenerator = new NotesGenerator(handler);
}
File releseNotes() {
@@ -46,7 +46,7 @@ class ReleaseNotesTemplateGenerator {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("date", LocalDate.now().format(DateTimeFormatter.ISO_DATE))
.put("releaseVersion", this.releaseVersion)
.put("projects", fromProjects())
.put("projects", this.notesGenerator.fromProjects(this.projects))
.build();
String blog = this.template.apply(map);
Files.write(this.blogOutput.toPath(), blog.getBytes());
@@ -57,60 +57,4 @@ class ReleaseNotesTemplateGenerator {
return null;
}
}
private Set<Notes> fromProjects() {
return this.projects.stream().filter(projectVersion ->
!projectVersion.projectName.toLowerCase().contains("boot")
).map(projectVersion -> {
String name = projectVersion.projectName;
String version = projectVersion.version;
String closedMilestoneUrl = this.handler.milestoneUrl(projectVersion);
String convertedName = Arrays.stream(name.split("-"))
.map(StringUtils::capitalize).collect(Collectors.joining(" "));
return new Notes(convertedName, version, closedMilestoneUrl);
}).collect(Collectors.toSet());
}
}
class Notes {
private final String name;
private final String version;
private final String closedMilestoneUrl;
Notes(String name, String version, String closedMilestoneUrl) {
this.name = name;
this.version = version;
this.closedMilestoneUrl = closedMilestoneUrl;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getClosedMilestoneUrl() {
return closedMilestoneUrl;
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Notes notes = (Notes) o;
if (name != null ? !name.equals(notes.name) : notes.name != null)
return false;
return version != null ?
version.equals(notes.version) :
notes.version == null;
}
@Override public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (version != null ? version.hashCode() : 0);
return result;
}
}

View File

@@ -72,7 +72,7 @@ public class TemplateGenerator implements ReleaserPropertiesAware {
File blogOutput = file(this.blogOutput);
String releaseVersion = parsedVersion(projects);
Template template = template(BLOG_TEMPLATE);
return new BlogTemplateGenerator(template, releaseVersion, blogOutput, projects).blog();
return new BlogTemplateGenerator(template, releaseVersion, blogOutput, projects, handler).blog();
}
public File tweet(Projects projects) {

View File

@@ -9,9 +9,9 @@ Some text related to project
The following modules were updated as part of {{ releaseVersion }}:
| Module | Version |
|--- |--- |
{{#each projects}}| {{name}} | {{version}} |
| Module | Version | Issues
|--- |--- |--- |---
{{#each projects}}| {{name}} | {{version}} | {{#if closedMilestoneUrl}}([issues]({{{ closedMilestoneUrl }}})){{/if}}
{{/each}}
As always, we welcome feedback on [GitHub](https://github.com/spring-cloud/), on [Gitter](https://gitter.im/spring-cloud/spring-cloud), on [Stack Overflow](http://stackoverflow.com/questions/tagged/spring-cloud), or on [Twitter](https://twitter.com/SpringCloud).

View File

@@ -121,6 +121,16 @@ public class GithubMilestonesTests {
then(url).isEqualTo("https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
}
@Test
public void should_fetch_url_of_a_closed_matching_milestone_from_cache() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
GithubMilestones.CACHE.put(gaSleuthProject(), "https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
String url = milestones.milestoneUrl(gaSleuthProject());
then(url).isEqualTo("https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
}
@Test
public void should_return_null_if_no_matching_milestone_was_found() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {

View File

@@ -19,7 +19,12 @@ import static org.assertj.core.api.BDDAssertions.then;
public class TemplateGeneratorTests {
ReleaserProperties props = new ReleaserProperties();
ProjectGitHandler handler = new ProjectGitHandler(this.props);
ProjectGitHandler handler = new ProjectGitHandler(this.props) {
@Override
public String milestoneUrl(ProjectVersion releaseVersion) {
return "http://foo.bar.com";
}
};
@Test
public void should_generate_email_from_template_for_tag_with_v_prefix() {
@@ -84,7 +89,7 @@ public class TemplateGeneratorTests {
.contains("General Availability (RELEASE) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Maven Central]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RELEASE \t|")
.contains("| Spring Cloud Sleuth | 1.0.0.RELEASE | ([issues](http://foo.bar.com))")
.contains("<version>Dalston.RELEASE</version>")
.contains("mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE'");
}
@@ -106,7 +111,7 @@ public class TemplateGeneratorTests {
.contains("General Availability (RELEASE) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Maven Central]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RELEASE \t|")
.contains("| Spring Cloud Sleuth\t| 1.0.0.RELEASE\t| ([issues](http://foo.bar.com))")
.contains("<version>Dalston.RELEASE</version>")
.contains("mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE'");
}
@@ -128,7 +133,7 @@ public class TemplateGeneratorTests {
.contains("Service Release 1 (SR1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Maven Central]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RELEASE \t|")
.contains("| Spring Cloud Sleuth | 1.0.0.RELEASE | ([issues](http://foo.bar.com))")
.contains("<version>Dalston.SR1</version>")
.contains("mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.SR1'");
}
@@ -150,7 +155,7 @@ public class TemplateGeneratorTests {
.contains("Service Release 1 (SR1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Maven Central]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RELEASE \t|")
.contains("| Spring Cloud Sleuth | 1.0.0.RELEASE | ([issues](http://foo.bar.com))")
.contains("<version>Dalston.SR1</version>")
.contains("mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.SR1'");
}
@@ -172,7 +177,7 @@ public class TemplateGeneratorTests {
.contains("Milestone 1 (M1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Spring Milestone]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.M1 \t|")
.contains("| Spring Cloud Sleuth\t| 1.0.0.M1\t| ([issues](http://foo.bar.com))")
.contains("<id>spring-milestones</id>")
.contains("url 'http://repo.spring.io/milestone'")
.contains("<version>Dalston.M1</version>")
@@ -196,7 +201,7 @@ public class TemplateGeneratorTests {
.contains("Milestone 1 (M1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Spring Milestone]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.M1 \t|")
.contains("| Spring Cloud Sleuth\t| 1.0.0.M1\t| ([issues](http://foo.bar.com))")
.contains("<id>spring-milestones</id>")
.contains("url 'http://repo.spring.io/milestone'")
.contains("<version>Dalston.M1</version>")
@@ -220,7 +225,7 @@ public class TemplateGeneratorTests {
.contains("Release Candidate 1 (RC1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Spring Milestone]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RC1 \t|")
.contains("| Spring Cloud Sleuth\t| 1.0.0.RC1\t| ([issues](http://foo.bar.com))")
.contains("<id>spring-milestones</id>")
.contains("url 'http://repo.spring.io/milestone'")
.contains("<version>Dalston.RC1</version>")
@@ -244,7 +249,7 @@ public class TemplateGeneratorTests {
.contains("Release Candidate 1 (RC1) of the [Spring Cloud Dalston]")
.contains("The release can be found in [Spring Milestone]")
.contains("### Spring Cloud Sleuth")
.contains("| Spring Cloud Sleuth \t| 1.0.0.RC1 \t|")
.contains("| Spring Cloud Sleuth\t| 1.0.0.RC1\t| ([issues](http://foo.bar.com))")
.contains("<id>spring-milestones</id>")
.contains("url 'http://repo.spring.io/milestone'")
.contains("<version>Dalston.RC1</version>")

View File

@@ -32,9 +32,14 @@ public class ReleaserApplication implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(ReleaserApplication.class);
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ReleaserApplication.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
try {
SpringApplication application = new SpringApplication(ReleaserApplication.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
} catch (Throwable e) {
log.error("Exception occurred for the releaser", e);
throw e;
}
}
@Autowired SpringReleaser releaser;
@@ -44,8 +49,8 @@ public class ReleaserApplication implements CommandLineRunner {
Options options = this.parser.parse(strings);
try {
this.releaser.release(options);
} catch (Exception e) {
log.error("Exception occurred for the releaser. Picked options were [" + options + "]", e);
} catch (Throwable e) {
log.error("Exception occurred for the releaser. Picked options were [" + options + "]");
throw e;
}
System.exit(0);