Added updating of release notes

with this change a template with links to closed milestones will be created

fixes #10
This commit is contained in:
Marcin Grzejszczak
2017-09-06 13:13:19 +02:00
parent f9ca023ca4
commit d16d3dee49
19 changed files with 560 additions and 206 deletions

View File

@@ -39,6 +39,7 @@ why this tool makes it easy to automate the release / dependency update process
- Generates an email template under `target/email.txt` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a blog template under `target/blog.md` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a tweet template under `target/tweet.txt` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a release notes template under `target/notes.md` (ONLY FOR NON-SNAPSHOT VERSIONS)
=== How to run it
@@ -100,6 +101,8 @@ the `spring-cloud-cli.version` to `1.0.0.RELEASE` regardless of what was set in
- `releaser.git.clone-destination-dir` - Where should the Spring Cloud Release repo get cloned to. If null defaults to a temporary directory
- `releaser.git.spring-cloud-release-git-url` - URL to Spring Cloud Release Git repository. Defaults to `https://github.com/spring-cloud/spring-cloud-release`
- `releaser.git.oauth-token` - GitHub OAuth token to be used to interact with GitHub repo.
- `releaser.git.number-of-checked-milestones` - In order not to iterate endlessly over milestones we introduce a threshold of milestones that
we will go through to find the matching milestone. Defaults to `10`
- `releaser.maven.build-command` - Command to be executed to build the project. Defaults to `./mvnw clean install -Pdocs`
- `releaser.maven.deploy-command` - Command to be executed to deploy a built project". Defaults to `./mvnw deploy -DskipTests -Pfast`
- `releaser.maven.publish-docs-commands` - Command to be executed to deploy a built project. If present `{{version}}` will be replaced by the proper version.

View File

@@ -29,6 +29,7 @@ why this tool makes it easy to automate the release / dependency update process
- Generates an email template under `target/email.txt` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a blog template under `target/blog.md` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a tweet template under `target/tweet.txt` (ONLY FOR NON-SNAPSHOT VERSIONS)
- Generates a release notes template under `target/notes.md` (ONLY FOR NON-SNAPSHOT VERSIONS)
=== How to run it
@@ -90,6 +91,8 @@ the `spring-cloud-cli.version` to `1.0.0.RELEASE` regardless of what was set in
- `releaser.git.clone-destination-dir` - Where should the Spring Cloud Release repo get cloned to. If null defaults to a temporary directory
- `releaser.git.spring-cloud-release-git-url` - URL to Spring Cloud Release Git repository. Defaults to `https://github.com/spring-cloud/spring-cloud-release`
- `releaser.git.oauth-token` - GitHub OAuth token to be used to interact with GitHub repo.
- `releaser.git.number-of-checked-milestones` - In order not to iterate endlessly over milestones we introduce a threshold of milestones that
we will go through to find the matching milestone. Defaults to `10`
- `releaser.maven.build-command` - Command to be executed to build the project. Defaults to `./mvnw clean install -Pdocs`
- `releaser.maven.deploy-command` - Command to be executed to deploy a built project". Defaults to `./mvnw deploy -DskipTests -Pfast`
- `releaser.maven.publish-docs-commands` - Command to be executed to deploy a built project. If present `{{version}}` will be replaced by the proper version.

View File

@@ -7,7 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.release.internal.gradle.GradleUpdater;
import org.springframework.cloud.release.internal.template.TemplateGenerator;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.ProjectPomUpdater;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.Projects;
@@ -21,16 +21,16 @@ public class Releaser {
private final ProjectPomUpdater projectPomUpdater;
private final ProjectBuilder projectBuilder;
private final ProjectGitUpdater projectGitUpdater;
private final ProjectGitHandler projectGitHandler;
private final TemplateGenerator templateGenerator;
private final GradleUpdater gradleUpdater;
public Releaser(ProjectPomUpdater projectPomUpdater, ProjectBuilder projectBuilder,
ProjectGitUpdater projectGitUpdater, TemplateGenerator templateGenerator,
ProjectGitHandler projectGitHandler, TemplateGenerator templateGenerator,
GradleUpdater gradleUpdater) {
this.projectPomUpdater = projectPomUpdater;
this.projectBuilder = projectBuilder;
this.projectGitUpdater = projectGitUpdater;
this.projectGitHandler = projectGitHandler;
this.templateGenerator = templateGenerator;
this.gradleUpdater = gradleUpdater;
}
@@ -53,7 +53,7 @@ public class Releaser {
}
public void commitAndPushTags(File project, ProjectVersion changedVersion) {
this.projectGitUpdater.commitAndTagIfApplicable(project, changedVersion);
this.projectGitHandler.commitAndTagIfApplicable(project, changedVersion);
log.info("\nCommit was made and tag was pushed successfully");
}
@@ -72,12 +72,12 @@ public class Releaser {
log.info("\nWon't rollback a snapshot version");
return;
}
this.projectGitUpdater.revertChangesIfApplicable(project, scReleaseVersion);
this.projectGitHandler.revertChangesIfApplicable(project, scReleaseVersion);
ProjectVersion originalVersion = originalVersion(project);
log.info("Original project version is [{}]", originalVersion);
if ((scReleaseVersion.isRelease() || scReleaseVersion.isServiceRelease()) && originalVersion.isSnapshot()) {
this.projectBuilder.bumpVersions(originalVersion.bumpedVersion());
this.projectGitUpdater.commitAfterBumpingVersions(project, originalVersion);
this.projectGitHandler.commitAfterBumpingVersions(project, originalVersion);
log.info("\nSuccessfully reverted the commit and bumped snapshot versions");
} else {
log.info("\nSuccessfully reverted the commit and came back to snapshot versions");
@@ -89,7 +89,7 @@ public class Releaser {
}
public void pushCurrentBranch(File project) {
this.projectGitUpdater.pushCurrentBranch(project);
this.projectGitHandler.pushCurrentBranch(project);
log.info("\nSuccessfully pushed current branch");
}
@@ -98,7 +98,7 @@ public class Releaser {
log.info("\nWon't close a milestone for a SNAPSHOT version");
return;
}
this.projectGitUpdater.closeMilestone(releaseVersion);
this.projectGitHandler.closeMilestone(releaseVersion);
log.info("\nSuccessfully closed milestone");
}
@@ -128,4 +128,13 @@ public class Releaser {
File blog = this.templateGenerator.tweet();
log.info("\nSuccessfully created tweet template at location [{}]", blog);
}
public void createReleaseNotes(ProjectVersion releaseVersion, Projects projects) {
if (releaseVersion.isSnapshot()) {
log.info("\nWon't create release notes for a SNAPSHOT version");
return;
}
File output = this.templateGenerator.releaseNotes(projects);
log.info("\nSuccessfully created release notes at location [{}]", output);
}
}

View File

@@ -62,6 +62,12 @@ public class ReleaserProperties {
*/
private String oauthToken;
/**
* 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;
public String getSpringCloudReleaseGitUrl() {
return this.springCloudReleaseGitUrl;
}
@@ -86,6 +92,13 @@ public class ReleaserProperties {
this.oauthToken = oauthToken;
}
public Integer getNumberOfCheckedMilestones() {
return this.numberOfCheckedMilestones;
}
public void setNumberOfCheckedMilestones(Integer numberOfCheckedMilestones) {
this.numberOfCheckedMilestones = numberOfCheckedMilestones;
}
}
public static class Pom {

View File

@@ -0,0 +1,160 @@
package org.springframework.cloud.release.internal.git;
import com.jcabi.github.Coordinates;
import com.jcabi.github.Github;
import com.jcabi.github.Milestone;
import com.jcabi.github.RtGithub;
import com.jcabi.http.wire.RetryWire;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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;
/**
* @author Marcin Grzejszczak
*/
class GithubMilestones {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Github github;
private final ReleaserProperties properties;
GithubMilestones(ReleaserProperties properties) {
this.github = new RtGithub(new RtGithub(
properties.getGit().getOauthToken()).entry().through(RetryWire.class));
this.properties = properties;
}
GithubMilestones(Github github, ReleaserProperties properties) {
this.github = github;
this.properties = properties;
}
void closeMilestone(ProjectVersion version) {
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, openMilestones(version));
if (foundMilestone != null) {
try {
log.info("Found a matching milestone - closing it");
foundMilestone.close();
log.info("Closed the [{}] milestone", tagVersion);
}
catch (IOException e) {
log.error("Exception occurred while trying to retrieve the milestone", e);
}
} else {
log.warn("No matching milestone was found");
}
}
Milestone.Smart matchingMilestone(String tagVersion,
Iterable<Milestone> milestones) {
log.debug("Successfully received list of milestones [{}]", milestones);
log.info("Will try to match against tag version [{}]", tagVersion);
try {
int counter = 0;
for (Milestone milestone : milestones) {
if (counter++ >= this.properties.getGit().getNumberOfCheckedMilestones()) {
log.warn("No matching milestones were found within the provided threshold [{}] of checked milestones",
this.properties.getGit().getNumberOfCheckedMilestones());
return null;
}
Milestone.Smart smartMilestone = new Milestone.Smart(milestone);
String title = milestoneTitle(smartMilestone);
if (tagVersion.equals(title) || numericVersion(tagVersion)
.equals(title)) {
log.info("Found a matching milestone [{}]", smartMilestone.number());
return smartMilestone;
}
}
} catch (AssertionError | IOException e) {
log.error("Exception occurred while trying to retrieve the milestone", e);
return null;
}
log.warn("No matching milestones were found");
return null;
}
String milestoneUrl(ProjectVersion version) {
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));
if (foundMilestone != null) {
try {
URL url = foundMilestoneUrl(foundMilestone);
log.info("Found a matching milestone with issues URL [{}]", url);
return url.toString();
}
catch (IOException e) {
throw new IllegalStateException(e);
}
catch (Exception e) {
log.error("Exception occurred while trying to find milestone", e);
return "";
}
}
return "";
}
private String numericVersion(String version) {
return version.contains("RELEASE") ?
version.substring(0, version.lastIndexOf(".")) : "";
}
String milestoneTitle(Milestone.Smart milestone) throws IOException {
return milestone.title();
}
URL foundMilestoneUrl(Milestone.Smart milestone) throws IOException {
return milestone.url();
}
private Iterable<Milestone> getMilestones(ProjectVersion version,
Map<String, String> map) {
try {
return this.github.repos()
.get(new Coordinates.Simple(org(), version.projectName))
.milestones().iterate(map);
} catch (AssertionError e) {
log.error("Exception occurred while trying to fetch milestones", e);
return new ArrayList<>();
}
}
private Iterable<Milestone> openMilestones(ProjectVersion version) {
return getMilestones(version, openMilestones());
}
private Iterable<Milestone> closedMilestones(ProjectVersion version) {
return getMilestones(version, closedMilestones());
}
String org() {
return "spring-cloud";
}
private Map<String, String> openMilestones() {
Map<String, String> params = new HashMap<>();
params.put("state", "open");
return params;
}
private Map<String, String> closedMilestones() {
Map<String, String> params = new HashMap<>();
params.put("state", "closed");
return params;
}
}

View File

@@ -1,93 +0,0 @@
package org.springframework.cloud.release.internal.git;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
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 com.jcabi.github.Coordinates;
import com.jcabi.github.Github;
import com.jcabi.github.Milestone;
import com.jcabi.github.RtGithub;
import com.jcabi.http.wire.RetryWire;
/**
* @author Marcin Grzejszczak
*/
class MilestoneCloser {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Github github;
private final ReleaserProperties properties;
MilestoneCloser(ReleaserProperties properties) {
this.github = new RtGithub(new RtGithub(
properties.getGit().getOauthToken()).entry().through(RetryWire.class));
this.properties = properties;
}
MilestoneCloser(Github github, ReleaserProperties properties) {
this.github = github;
this.properties = properties;
}
void closeMilestone(ProjectVersion version) {
Assert.hasText(this.properties.getGit().getOauthToken(),
"You have to pass Github OAuth token for milestone closing to be operational");
Iterable<Milestone> milestones = milestones(version);
log.info("Successfully received list of milestones");
String tagVersion = version.version;
log.info("Will try to match against tag version [{}]", tagVersion);
boolean matchingMilestone = false;
for (Milestone milestone : milestones) {
Milestone.Smart smartMilestone = new Milestone.Smart(milestone);
try {
String title = milestoneTitle(smartMilestone);
if (tagVersion.equals(title) || numericVersion(tagVersion).equals(title)) {
log.info("Found a matching milestone - closing it");
smartMilestone.close();
matchingMilestone = true;
log.info("Closed the [{}] milestone", tagVersion);
}
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
if (!matchingMilestone) {
log.warn("No matching milestone was found");
}
}
private String numericVersion(String version) {
return version.contains("RELEASE") ?
version.substring(0, version.lastIndexOf(".")) : "";
}
String milestoneTitle(Milestone.Smart milestone) throws IOException {
return milestone.title();
}
private Iterable<Milestone> milestones(ProjectVersion version) {
return this.github.repos()
.get(new Coordinates.Simple(org(), version.projectName))
.milestones().iterate(openMilestones());
}
String org() {
return "spring-cloud";
}
private Map<String, String> openMilestones() {
Map<String, String> params = new HashMap<>();
params.put("state", "open");
return params;
}
}

View File

@@ -11,11 +11,11 @@ import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
/**
* Contains business logic around Git operations
* Contains business logic around Git & Github operations
*
* @author Marcin Grzejszczak
*/
public class ProjectGitUpdater {
public class ProjectGitHandler {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -25,11 +25,11 @@ public class ProjectGitUpdater {
private static final String POST_RELEASE_BUMP_MSG = "Bumping versions to %s after release";
private final ReleaserProperties properties;
private final MilestoneCloser milestoneCloser;
private final GithubMilestones githubMilestones;
public ProjectGitUpdater(ReleaserProperties properties) {
public ProjectGitHandler(ReleaserProperties properties) {
this.properties = properties;
this.milestoneCloser = new MilestoneCloser(properties);
this.githubMilestones = new GithubMilestones(properties);
}
public void commitAndTagIfApplicable(File project, ProjectVersion version) {
@@ -87,7 +87,11 @@ public class ProjectGitUpdater {
}
public void closeMilestone(ProjectVersion releaseVersion) {
this.milestoneCloser.closeMilestone(releaseVersion);
this.githubMilestones.closeMilestone(releaseVersion);
}
public String milestoneUrl(ProjectVersion releaseVersion) {
return this.githubMilestones.milestoneUrl(releaseVersion);
}
GitRepo gitRepo(File workingDir) {

View File

@@ -30,7 +30,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
/**
* @author Marcin Grzejszczak
@@ -40,12 +40,12 @@ public class ProjectPomUpdater {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final ReleaserProperties properties;
private final ProjectGitUpdater gitRepo;
private final ProjectGitHandler gitRepo;
private final PomUpdater pomUpdater = new PomUpdater();
public ProjectPomUpdater(ReleaserProperties properties) {
this.properties = properties;
this.gitRepo = new ProjectGitUpdater(properties);
this.gitRepo = new ProjectGitHandler(properties);
}
/**

View File

@@ -1,5 +1,8 @@
package org.springframework.cloud.release.internal.template;
import com.github.jknack.handlebars.Template;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -13,9 +16,6 @@ import java.util.stream.Collectors;
import org.springframework.cloud.release.internal.pom.Projects;
import org.springframework.util.StringUtils;
import com.github.jknack.handlebars.Template;
import com.google.common.collect.ImmutableMap;
/**
* @author Marcin Grzejszczak
*/
@@ -68,13 +68,13 @@ class BlogTemplateGenerator {
}
}
private Set<Tuple> fromProjects() {
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 Tuple(convertedName, version);
return new BlogTuple(convertedName, version);
}).collect(Collectors.toSet());
}
@@ -111,11 +111,11 @@ class BlogTemplateGenerator {
}
}
class Tuple {
class BlogTuple {
private final String name;
private final String version;
Tuple(String name, String version) {
BlogTuple(String name, String version) {
this.name = name;
this.version = version;
}
@@ -133,10 +133,10 @@ class Tuple {
return true;
if (o == null || getClass() != o.getClass())
return false;
Tuple tuple = (Tuple) o;
if (name != null ? !name.equals(tuple.name) : tuple.name != null)
BlogTuple blogTuple = (BlogTuple) o;
if (name != null ? !name.equals(blogTuple.name) : blogTuple.name != null)
return false;
return version != null ? version.equals(tuple.version) : tuple.version == null;
return version != null ? version.equals(blogTuple.version) : blogTuple.version == null;
}
@Override public int hashCode() {

View File

@@ -0,0 +1,111 @@
package org.springframework.cloud.release.internal.template;
import com.github.jknack.handlebars.Template;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Map;
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 ReleaseNotesTemplateGenerator {
private final Template template;
private final String releaseVersion;
private final File blogOutput;
private final Projects projects;
private final ProjectGitHandler handler;
ReleaseNotesTemplateGenerator(Template template, String releaseVersion,
File blogOutput, Projects projects, ProjectGitHandler handler) {
this.template = template;
this.releaseVersion = releaseVersion;
this.blogOutput = blogOutput;
this.projects = projects;
this.handler = handler;
}
File releseNotes() {
try {
Map<String, Object> map = ImmutableMap.<String, Object>builder()
.put("date", LocalDate.now().format(DateTimeFormatter.ISO_DATE))
.put("releaseVersion", this.releaseVersion)
.put("projects", fromProjects())
.build();
String blog = this.template.apply(map);
Files.write(this.blogOutput.toPath(), blog.getBytes());
return this.blogOutput;
}
catch (IOException e) {
throw new IllegalStateException(e);
}
}
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

@@ -9,6 +9,7 @@ import java.io.File;
import java.io.IOException;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.Projects;
/**
@@ -19,23 +20,30 @@ public class TemplateGenerator {
private static final String EMAIL_TEMPLATE = "email";
private static final String BLOG_TEMPLATE = "blog";
private static final String TWITTER_TEMPLATE = "tweet";
private static final String RELEASE_NOTES_TEMPLATE = "notes";
private final File emailOutput;
private final File blogOutput;
private final File tweetOutput;
private final File releaseNotesOutput;
private final ReleaserProperties props;
private final ProjectGitHandler handler;
public TemplateGenerator(ReleaserProperties props) {
public TemplateGenerator(ReleaserProperties props, ProjectGitHandler handler) {
this.props = props;
this.handler = handler;
this.emailOutput = new File("target/email.txt");
this.blogOutput = new File("target/blog.md");
this.tweetOutput = new File("target/tweet.txt");
this.releaseNotesOutput = new File("target/notes.md");
}
TemplateGenerator(ReleaserProperties props, File output) {
TemplateGenerator(ReleaserProperties props, File output, ProjectGitHandler handler) {
this.props = props;
this.emailOutput = output;
this.blogOutput = output;
this.tweetOutput = output;
this.releaseNotesOutput = output;
this.handler = handler;
}
public File email() {
@@ -75,6 +83,14 @@ public class TemplateGenerator {
return new TwitterTemplateGenerator(template, releaseVersion, output).tweet();
}
public File releaseNotes(Projects projects) {
File output = file(this.releaseNotesOutput);
String releaseVersion = parsedVersion();
Template template = template(RELEASE_NOTES_TEMPLATE);
return new ReleaseNotesTemplateGenerator(template, releaseVersion,
output, projects, this.handler).releseNotes();
}
private String parsedVersion() {
String version = this.props.getPom().getBranch();
if (version.startsWith("v")) {

View File

@@ -0,0 +1,6 @@
# {{ releaseVersion }}
{{ date }}
{{#each projects}} - {{name}} `{{version}}` ([issues]({{ closedMilestoneUrl }}))
{{/each}}

View File

@@ -10,7 +10,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.gradle.GradleUpdater;
import org.springframework.cloud.release.internal.pom.ProjectPomUpdater;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
@@ -30,7 +30,7 @@ public class ReleaserTests {
@Mock ProjectPomUpdater projectPomUpdater;
@Mock ProjectBuilder projectBuilder;
@Mock ProjectGitUpdater projectGitUpdater;
@Mock ProjectGitHandler projectGitHandler;
@Mock TemplateGenerator templateGenerator;
@Mock GradleUpdater gradleUpdater;
File pom;
@@ -43,7 +43,7 @@ public class ReleaserTests {
Releaser releaser(Supplier<ProjectVersion> originalVersionSupplier) {
return new Releaser(this.projectPomUpdater, this.projectBuilder,
this.projectGitUpdater, this.templateGenerator, this.gradleUpdater) {
this.projectGitHandler, this.templateGenerator, this.gradleUpdater) {
@Override ProjectVersion originalVersion(File project) {
return originalVersionSupplier.get();
}
@@ -52,7 +52,7 @@ public class ReleaserTests {
Releaser releaser() {
return new Releaser(this.projectPomUpdater, this.projectBuilder,
this.projectGitUpdater, this.templateGenerator, this.gradleUpdater);
this.projectGitHandler, this.templateGenerator, this.gradleUpdater);
}
@Test
@@ -100,7 +100,7 @@ public class ReleaserTests {
public void should_not_close_milestone_for_snapshots() throws Exception {
releaser().closeMilestone(new ProjectVersion("original", "1.0.0.BUILD-SNAPSHOT"));
then(this.projectGitUpdater).should(never()).closeMilestone(any(ProjectVersion.class));
then(this.projectGitHandler).should(never()).closeMilestone(any(ProjectVersion.class));
}
@Test
@@ -108,7 +108,7 @@ public class ReleaserTests {
releaser(() -> new ProjectVersion("original", "1.0.0.BUILD-SNAPSHOT"))
.rollbackReleaseVersion(null, new ProjectVersion("original", "1.0.0.BUILD-SNAPSHOT"));
then(this.projectGitUpdater).should(never()).revertChangesIfApplicable(any(File.class), any(ProjectVersion.class));
then(this.projectGitHandler).should(never()).revertChangesIfApplicable(any(File.class), any(ProjectVersion.class));
}
}

View File

@@ -1,30 +1,33 @@
package org.springframework.cloud.release.internal.git;
import com.jcabi.github.Milestone;
import com.jcabi.github.Repo;
import com.jcabi.github.mock.MkGithub;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import javax.json.Json;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import com.jcabi.github.Milestone;
import com.jcabi.github.Repo;
import com.jcabi.github.mock.MkGithub;
import static org.assertj.core.api.BDDAssertions.then;
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
/**
* @author Marcin Grzejszczak
*/
public class MilestoneCloserTests {
public class GithubMilestonesTests {
MkGithub github;
Repo repo;
@Rule public TemporaryFolder folder = new TemporaryFolder();
@Rule public OutputCapture capture = new OutputCapture();
@Before
@@ -35,7 +38,7 @@ public class MilestoneCloserTests {
@Test
public void should_close_milestone_if_there_is_one() throws IOException {
MilestoneCloser closer = new MilestoneCloser(this.github, withToken()) {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@@ -47,14 +50,14 @@ public class MilestoneCloserTests {
};
repo.milestones().create("0.2.0.BUILD-SNAPSHOT");
closer.closeMilestone(nonGaSleuthProject());
milestones.closeMilestone(nonGaSleuthProject());
then(this.capture.toString()).doesNotContain("No matching milestone was found");
}
@Test
public void should_close_milestone_when_the_milestone_contains_numeric_version_only_and_version_is_ga() throws IOException {
MilestoneCloser closer = new MilestoneCloser(this.github, withToken()) {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@@ -66,7 +69,7 @@ public class MilestoneCloserTests {
};
repo.milestones().create("0.2.0");
closer.closeMilestone(gaSleuthProject());
milestones.closeMilestone(gaSleuthProject());
then(this.capture.toString()).doesNotContain("No matching milestone was found");
}
@@ -77,7 +80,7 @@ public class MilestoneCloserTests {
@Test
public void should_not_close_milestone_when_the_milestone_contains_numeric_version_only() throws IOException {
MilestoneCloser closer = new MilestoneCloser(this.github, withToken()) {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@@ -89,18 +92,84 @@ public class MilestoneCloserTests {
};
repo.milestones().create("0.2.0");
closer.closeMilestone(nonGaSleuthProject());
milestones.closeMilestone(nonGaSleuthProject());
then(this.capture.toString()).contains("No matching milestone was found");
}
@Test
public void should_fetch_url_of_a_closed_matching_milestone() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@Override String milestoneTitle(Milestone.Smart milestone)
throws IOException {
return "0.2.0.RELEASE";
}
@Override URL foundMilestoneUrl(Milestone.Smart milestone)
throws IOException {
return new URL("http://foo.com/bar");
}
};
repo.milestones().create("0.2.0.RELEASE");
String url = milestones.milestoneUrl(gaSleuthProject());
then(url).isEqualTo("http://foo.com/bar");
}
@Test
public void should_return_null_if_no_matching_milestone_was_found() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@Override String milestoneTitle(Milestone.Smart milestone)
throws IOException {
return "0.9.0.RELEASE";
}
@Override URL foundMilestoneUrl(Milestone.Smart milestone)
throws IOException {
return new URL("http://foo.com/bar");
}
};
String url = milestones.milestoneUrl(gaSleuthProject());
then(url).isEmpty();
}
@Test
public void should_return_null_if_no_matching_milestone_was_found_within_threshold() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withThreshold()) {
@Override String org() {
return repo.coordinates().user();
}
@Override String milestoneTitle(Milestone.Smart milestone)
throws IOException {
return "0.2.0";
}
};
repo.milestones().create("0.2.0");
milestones.closeMilestone(gaSleuthProject());
then(this.capture.toString()).contains("No matching milestones were found within the provided threshold [0]");
}
private ProjectVersion nonGaSleuthProject() {
return new ProjectVersion("spring-cloud-sleuth", "0.2.0.BUILD-SNAPSHOT");
}
@Test
public void should_throw_exception_when_there_is_no_matching_milestone() throws IOException {
MilestoneCloser closer = new MilestoneCloser(this.github, withToken()) {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@@ -112,13 +181,13 @@ public class MilestoneCloserTests {
};
repo.milestones().create("v0.2.0.BUILD-SNAPSHOT");
closer.closeMilestone(nonGaSleuthProject());
milestones.closeMilestone(nonGaSleuthProject());
then(this.capture.toString()).contains("No matching milestone was found");
}
@Test
public void should_throw_exception_when_io_problems_occurred() throws IOException {
MilestoneCloser closer = new MilestoneCloser(this.github, withToken()) {
public void should_print_that_no_milestones_were_found_when_io_problems_occurred() throws IOException {
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
@Override String org() {
return repo.coordinates().user();
}
@@ -130,9 +199,9 @@ public class MilestoneCloserTests {
};
repo.milestones().create("v0.2.0.BUILD-SNAPSHOT");
thenThrownBy(() -> closer.closeMilestone(nonGaSleuthProject()))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("foo");
milestones.closeMilestone(nonGaSleuthProject());
then(this.capture.toString()).contains("No matching milestone was found");
}
private Repo createSleuthRepo(MkGithub github) throws IOException {
@@ -146,9 +215,9 @@ public class MilestoneCloserTests {
@Test
public void should_throw_exception_when_no_token_was_passed() {
MilestoneCloser closer = new MilestoneCloser(new ReleaserProperties());
GithubMilestones milestones = new GithubMilestones(new ReleaserProperties());
thenThrownBy(() -> closer.closeMilestone(nonGaSleuthProject()))
thenThrownBy(() -> milestones.closeMilestone(nonGaSleuthProject()))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("You have to pass Github OAuth token for milestone closing to be operational");
}
@@ -158,4 +227,10 @@ public class MilestoneCloserTests {
properties.getGit().setOauthToken("foo");
return properties;
}
ReleaserProperties withThreshold() {
ReleaserProperties properties = withToken();
properties.getGit().setNumberOfCheckedMilestones(0);
return properties;
}
}

View File

@@ -19,12 +19,12 @@ import static org.mockito.Mockito.never;
* @author Marcin Grzejszczak
*/
@RunWith(MockitoJUnitRunner.class)
public class ProjectGitUpdaterTests {
public class ProjectGitHandlerTests {
@Mock GitRepo gitRepo;
ProjectGitUpdater updater = new ProjectGitUpdater(new ReleaserProperties()) {
ProjectGitHandler updater = new ProjectGitHandler(new ReleaserProperties()) {
@Override GitRepo gitRepo(File workingDir) {
return ProjectGitUpdaterTests.this.gitRepo;
return ProjectGitHandlerTests.this.gitRepo;
}
};
File file = new File("");

View File

@@ -7,6 +7,7 @@ import java.util.HashSet;
import org.junit.Test;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.Projects;
@@ -17,52 +18,51 @@ import static org.assertj.core.api.BDDAssertions.then;
*/
public class TemplateGeneratorTests {
ReleaserProperties props = new ReleaserProperties();
ProjectGitHandler handler = new ProjectGitHandler(this.props);
@Test
public void should_generate_email_from_template_for_tag_with_v_prefix() {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.RELEASE");
this.props.getPom().setBranch("vDalston.RELEASE");
File generatedMail = new TemplateGenerator(props).email();
File generatedMail = new TemplateGenerator(this.props, this.handler).email();
then(generatedMail).hasContent(expectedEmail());
}
@Test
public void should_generate_email_from_template_when_output_folder_is_missing() {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.RELEASE");
this.props.getPom().setBranch("vDalston.RELEASE");
File generatedMail = new TemplateGenerator(props, new File("target/foo/bar/baz/template.txt")).email();
File generatedMail = new TemplateGenerator(this.props, new File("target/foo/bar/baz/template.txt"),
handler).email();
then(generatedMail).hasContent(expectedEmail());
}
@Test
public void should_generate_email_from_template_for_tag_without_v_prefix() {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.RELEASE");
this.props.getPom().setBranch("Dalston.RELEASE");
File generatedMail = new TemplateGenerator(props).email();
File generatedMail = new TemplateGenerator(this.props, this.handler).email();
then(generatedMail).hasContent(expectedEmail());
}
@Test
public void should_generate_tweet_from_template_for_tag_with_v_prefix() {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.RELEASE");
this.props.getPom().setBranch("vDalston.RELEASE");
File generatedTweet = new TemplateGenerator(props).tweet();
File generatedTweet = new TemplateGenerator(this.props, this.handler).tweet();
then(generatedTweet).hasContent(expectedTweet());
}
@Test
public void should_generate_tweet_from_template_for_tag_without_v_prefix() {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.RELEASE");
this.props.getPom().setBranch("Dalston.RELEASE");
File generatedTweet = new TemplateGenerator(props).tweet();
File generatedTweet = new TemplateGenerator(this.props, this.handler).tweet();
then(generatedTweet).hasContent(expectedTweet());
}
@@ -70,8 +70,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_blog_from_template_for_tag_with_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.RELEASE");
this.props.getPom().setBranch("vDalston.RELEASE");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RELEASE"));
@@ -79,7 +78,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("General Availability (RELEASE) of the [Spring Cloud Dalston]")
@@ -93,8 +92,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_blog_from_template_for_tag_without_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.RELEASE");
this.props.getPom().setBranch("Dalston.RELEASE");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RELEASE"));
@@ -102,7 +100,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("General Availability (RELEASE) of the [Spring Cloud Dalston]")
@@ -116,8 +114,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_sr_blog_from_template_for_tag_with_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.SR1");
this.props.getPom().setBranch("vDalston.SR1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RELEASE"));
@@ -125,7 +122,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Service Release 1 (SR1) of the [Spring Cloud Dalston]")
@@ -139,8 +136,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_sr_blog_from_template_for_tag_without_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.SR1");
this.props.getPom().setBranch("Dalston.SR1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RELEASE"));
@@ -148,7 +144,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Service Release 1 (SR1) of the [Spring Cloud Dalston]")
@@ -162,8 +158,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_milestone_blog_from_template_for_tag_with_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.M1");
this.props.getPom().setBranch("vDalston.M1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.M1"));
@@ -171,7 +166,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Milestone 1 (M1) of the [Spring Cloud Dalston]")
@@ -187,8 +182,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_milestone_blog_from_template_for_tag_without_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.M1");
this.props.getPom().setBranch("Dalston.M1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.M1"));
@@ -196,7 +190,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Milestone 1 (M1) of the [Spring Cloud Dalston]")
@@ -212,8 +206,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_rc_blog_from_template_for_tag_with_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("vDalston.RC1");
this.props.getPom().setBranch("vDalston.RC1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RC1"));
@@ -221,7 +214,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Release Candidate 1 (RC1) of the [Spring Cloud Dalston]")
@@ -237,8 +230,7 @@ public class TemplateGeneratorTests {
@Test
public void should_generate_rc_blog_from_template_for_tag_without_v_prefix_release()
throws IOException {
ReleaserProperties props = new ReleaserProperties();
props.getPom().setBranch("Dalston.RC1");
this.props.getPom().setBranch("Dalston.RC1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RC1"));
@@ -246,7 +238,7 @@ public class TemplateGeneratorTests {
}}
);
File generatedBlog = new TemplateGenerator(props).blog(projects);
File generatedBlog = new TemplateGenerator(this.props, this.handler).blog(projects);
then(content(generatedBlog))
.contains("Release Candidate 1 (RC1) of the [Spring Cloud Dalston]")
@@ -259,6 +251,32 @@ public class TemplateGeneratorTests {
.contains("mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.RC1'");
}
@Test
public void should_generate_release_notes_template_when_url_exists()
throws IOException {
ProjectGitHandler handler = new ProjectGitHandler(this.props) {
@Override public String milestoneUrl(ProjectVersion releaseVersion) {
return "http://foo.bar.com";
}
};
this.props.getPom().setBranch("Dalston.RC1");
Projects projects = new Projects(
new HashSet<ProjectVersion>() {{
add(new ProjectVersion("spring-cloud-sleuth", "1.0.0.RC1"));
add(new ProjectVersion("spring-cloud-consul", "1.0.1.RC1"));
add(new ProjectVersion("spring-boot-dependencies", "1.0.1.RC1"));
}}
);
File generatedOutput = new TemplateGenerator(this.props, handler).releaseNotes(projects);
then(content(generatedOutput))
.contains("# Dalston.RC1")
.contains("Spring Cloud Sleuth `1.0.0.RC1` ([issues](http://foo.bar.com))")
.contains("Spring Cloud Consul `1.0.1.RC1` ([issues](http://foo.bar.com))")
.doesNotContain("Boot");
}
private String content(File file) throws IOException {
return new String(Files.readAllBytes(file.toPath()));
}

View File

@@ -21,7 +21,7 @@ import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.gradle.GradleUpdater;
import org.springframework.cloud.release.internal.template.TemplateGenerator;
import org.springframework.cloud.release.internal.project.ProjectBuilder;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.pom.ProjectPomUpdater;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -32,8 +32,9 @@ class ReleaserConfiguration {
@Bean SpringReleaser releaser(ReleaserProperties properties) {
ProjectPomUpdater pomUpdater = new ProjectPomUpdater(properties);
ProjectGitHandler handler = new ProjectGitHandler(properties);
return new SpringReleaser(new Releaser(pomUpdater, new ProjectBuilder(properties, pomUpdater),
new ProjectGitUpdater(properties), new TemplateGenerator(properties),
handler, new TemplateGenerator(properties, handler),
new GradleUpdater(properties)), properties);
}
}

View File

@@ -63,6 +63,7 @@ public class SpringReleaser {
args.releaser.createEmail(args.versionFromScRelease);
args.releaser.createBlog(args.versionFromScRelease, args.projects);
args.releaser.createTweet(args.versionFromScRelease);
args.releaser.createReleaseNotes(args.versionFromScRelease, args.projects);
})
).collect(Collectors.toList());

View File

@@ -17,7 +17,7 @@ import org.junit.rules.TemporaryFolder;
import org.springframework.cloud.release.internal.Releaser;
import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.git.GitTestUtils;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
import org.springframework.cloud.release.internal.gradle.GradleUpdater;
import org.springframework.cloud.release.internal.pom.ProjectPomUpdater;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
@@ -38,7 +38,7 @@ public class AcceptanceTests {
TestPomReader testPomReader = new TestPomReader();
File springCloudConsulProject;
File temporaryFolder;
TestProjectGitUpdater gitUpdater;
TestProjectGitHandler gitHandler;
@Before
public void setup() throws Exception {
@@ -80,7 +80,7 @@ public class AcceptanceTests {
commitIsPresent(iterator, "Update SNAPSHOT to 1.1.2.RELEASE");
pomVersionIsEqualTo(project, "1.2.1.BUILD-SNAPSHOT");
pomParentVersionIsEqualTo(project, "1.2.1.BUILD-SNAPSHOT");
then(this.gitUpdater.executed).isTrue();
then(this.gitHandler.closedMilestones).isTrue();
then(emailTemplate()).exists();
then(emailTemplateContents())
.contains("Spring Cloud Camden.SR5 available")
@@ -88,6 +88,11 @@ public class AcceptanceTests {
then(blogTemplate()).exists();
then(blogTemplateContents())
.contains("I am pleased to announce that the Service Release 5 (SR5)");
then(releaseNotesTemplate()).exists();
then(releaseNotesTemplateContents())
.contains("Camden.SR5")
.contains("- Spring Cloud Config `1.2.2.RELEASE` ([issues](http://foo.bar.com/1.2.2.RELEASE))")
.contains("- Spring Cloud Aws `1.1.3.RELEASE` ([issues](http://foo.bar.com/1.1.3.RELEASE))");
}
@Test
@@ -109,7 +114,7 @@ public class AcceptanceTests {
commitIsPresent(iterator, "Update SNAPSHOT to 1.2.0.RC1");
pomVersionIsEqualTo(project, "1.2.0.BUILD-SNAPSHOT");
pomParentVersionIsEqualTo(project, "1.2.0.BUILD-SNAPSHOT");
then(this.gitUpdater.executed).isTrue();
then(this.gitHandler.closedMilestones).isTrue();
then(emailTemplate()).exists();
then(emailTemplateContents())
.contains("Spring Cloud Dalston.RC1 available")
@@ -120,6 +125,11 @@ public class AcceptanceTests {
then(tweetTemplate()).exists();
then(tweetTemplateContents())
.contains("The Dalston.RC1 version of @springcloud has been released!");
then(releaseNotesTemplate()).exists();
then(releaseNotesTemplateContents())
.contains("Dalston.RC1")
.contains("- Spring Cloud Build `1.3.1.RELEASE` ([issues](http://foo.bar.com/1.3.1.RELEASE))")
.contains("- Spring Cloud Bus `1.3.0.M1` ([issues](http://foo.bar.com/1.3.0.M1))");
}
@Test
@@ -133,7 +143,7 @@ public class AcceptanceTests {
releaser.release();
then(this.gitUpdater.executed).isFalse();
then(this.gitHandler.closedMilestones).isFalse();
then(emailTemplate()).exists();
then(emailTemplateContents())
.contains("Spring Cloud Dalston.RC1 available")
@@ -144,6 +154,11 @@ public class AcceptanceTests {
then(tweetTemplate()).exists();
then(tweetTemplateContents())
.contains("The Dalston.RC1 version of @springcloud has been released!");
then(releaseNotesTemplate()).exists();
then(releaseNotesTemplateContents())
.contains("Dalston.RC1")
.contains("- Spring Cloud Build `1.3.1.RELEASE` ([issues](http://foo.bar.com/1.3.1.RELEASE))")
.contains("- Spring Cloud Bus `1.3.0.M1` ([issues](http://foo.bar.com/1.3.0.M1)");
}
private Iterable<RevCommit> listOfCommits(File project) throws GitAPIException {
@@ -197,6 +212,10 @@ public class AcceptanceTests {
return new File("target/tweet.txt");
}
private File releaseNotesTemplate() throws URISyntaxException {
return new File("target/notes.md");
}
private String blogTemplateContents() throws URISyntaxException, IOException {
return new String(Files.readAllBytes(blogTemplate().toPath()));
}
@@ -205,6 +224,10 @@ public class AcceptanceTests {
return new String(Files.readAllBytes(tweetTemplate().toPath()));
}
private String releaseNotesTemplateContents() throws URISyntaxException, IOException {
return new String(Files.readAllBytes(releaseNotesTemplate().toPath()));
}
private SpringReleaser releaser(File projectFile, String branch, String expectedVersion) throws Exception {
ReleaserProperties properties = releaserProperties(projectFile, branch);
return releaserWithFullDeployment(expectedVersion, properties);
@@ -238,13 +261,13 @@ public class AcceptanceTests {
private Releaser defaultReleaser(String expectedVersion, ReleaserProperties properties) throws Exception {
ProjectPomUpdater pomUpdater = new ProjectPomUpdater(properties);
ProjectBuilder projectBuilder = new ProjectBuilder(properties, pomUpdater);
TestProjectGitUpdater gitUpdater = new TestProjectGitUpdater(properties,
TestProjectGitHandler handler = new TestProjectGitHandler(properties,
expectedVersion);
TemplateGenerator templateGenerator = new TemplateGenerator(properties);
TemplateGenerator templateGenerator = new TemplateGenerator(properties, handler);
GradleUpdater gradleUpdater = new GradleUpdater(properties);
Releaser releaser = new Releaser(pomUpdater, projectBuilder, gitUpdater,
Releaser releaser = new Releaser(pomUpdater, projectBuilder, handler,
templateGenerator, gradleUpdater);
this.gitUpdater = gitUpdater;
this.gitHandler = handler;
return releaser;
}
@@ -265,12 +288,12 @@ public class AcceptanceTests {
return releaserProperties;
}
class TestProjectGitUpdater extends ProjectGitUpdater {
class TestProjectGitHandler extends ProjectGitHandler {
boolean executed = false;
boolean closedMilestones = false;
final String expectedVersion;
public TestProjectGitUpdater(ReleaserProperties properties,
public TestProjectGitHandler(ReleaserProperties properties,
String expectedVersion) {
super(properties);
this.expectedVersion = expectedVersion;
@@ -279,7 +302,11 @@ public class AcceptanceTests {
@Override public void closeMilestone(ProjectVersion releaseVersion) {
then(releaseVersion.projectName).isEqualTo("spring-cloud-consul");
then(releaseVersion.version).isEqualTo(this.expectedVersion);
this.executed = true;
this.closedMilestones = true;
}
@Override public String milestoneUrl(ProjectVersion releaseVersion) {
return "http://foo.bar.com/" + releaseVersion.toString();
}
}