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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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")) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# {{ releaseVersion }}
|
||||
|
||||
{{ date }}
|
||||
|
||||
{{#each projects}} - {{name}} `{{version}}` ([issues]({{ closedMilestoneUrl }}))
|
||||
{{/each}}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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("");
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user