Added the ability to run a single step without skipping

fixes #9
This commit is contained in:
Marcin Grzejszczak
2017-04-20 14:51:46 +02:00
parent 42024e1a2b
commit 2131efcdc4
6 changed files with 284 additions and 82 deletions

View File

@@ -58,6 +58,32 @@ The application will start running from your working directory.
TIP: It is important that you clone the repository you are going to release using SSH in order for the
`releaser` to be able to push tags and commit changes automatically.
You will see text similar to this one
[source]
----
=== WHAT DO YOU WANT TO DO? ===
0) Perform a full release of this project without interruptions
1) Perform a full release of this project in a verbose mode (you'll be asked about skipping steps)
2) Update poms with versions from Spring Cloud Release
3) Build the project
4) Commit, tag and push the tag
5) Deploy the artifacts to Artifactory
6) Publish the docs
7) Go back to snapshots and bump originalVersion by patch
8) Push the commits
9) Close the milestone at Github
10) Create email / tweet etc. templates
You can press 'q' to quit
----
Just pick a number and continue! Pick either a full release or single steps.
TIP: Read before picking a number cause it might have changed between tool releases ;)
=== Project options
- `releaser.fixed-versions` - A String to String mapping of manually set versions. E.g. `"spring-cloud-cli" -> "1.0.0.RELEASE"` will set
@@ -88,11 +114,13 @@ folder and run your application like this:
[source,bash]
----
$ mkdir -p target
$ wget http://repo.spring.io/libs-milestone/org/springframework/cloud/internal/spring-cloud-release-tools-spring/1.0.0.M1/spring-cloud-release-tools-spring-1.0.0.M1.jar -O target/spring-cloud-release-tools-spring-1.0.0.M1.jar
$ wget http://repo.spring.io/libs-milestone/org/springframework/cloud/internal/spring-cloud-release-tools-spring/1.0.0.M1/spring-cloud-release-tools-spring-1.0.0.M1.jar -O ../spring-cloud-release-tools-spring-1.0.0.M1.jar
$ java -jar target/spring-cloud-release-tools-spring-1.0.0.M1.jar --spring.config.name=releaser
----
TIP: Notice that we're downloading the jar to a parent folder, not to `target`. That's because `target` get cleaned
during the build process
==== Specifying A Branch
By deafult the releaser will default to using the `master` branch of `spring-cloud-release`.

View File

@@ -48,6 +48,32 @@ The application will start running from your working directory.
TIP: It is important that you clone the repository you are going to release using SSH in order for the
`releaser` to be able to push tags and commit changes automatically.
You will see text similar to this one
[source]
----
=== WHAT DO YOU WANT TO DO? ===
0) Perform a full release of this project without interruptions
1) Perform a full release of this project in a verbose mode (you'll be asked about skipping steps)
2) Update poms with versions from Spring Cloud Release
3) Build the project
4) Commit, tag and push the tag
5) Deploy the artifacts to Artifactory
6) Publish the docs
7) Go back to snapshots and bump originalVersion by patch
8) Push the commits
9) Close the milestone at Github
10) Create email / tweet etc. templates
You can press 'q' to quit
----
Just pick a number and continue! Pick either a full release or single steps.
TIP: Read before picking a number cause it might have changed between tool releases ;)
=== Project options
- `releaser.fixed-versions` - A String to String mapping of manually set versions. E.g. `"spring-cloud-cli" -> "1.0.0.RELEASE"` will set
@@ -78,11 +104,13 @@ folder and run your application like this:
[source,bash]
----
$ mkdir -p target
$ wget http://repo.spring.io/libs-milestone/org/springframework/cloud/internal/spring-cloud-release-tools-spring/1.0.0.M1/spring-cloud-release-tools-spring-1.0.0.M1.jar -O target/spring-cloud-release-tools-spring-1.0.0.M1.jar
$ wget http://repo.spring.io/libs-milestone/org/springframework/cloud/internal/spring-cloud-release-tools-spring/1.0.0.M1/spring-cloud-release-tools-spring-1.0.0.M1.jar -O ../spring-cloud-release-tools-spring-1.0.0.M1.jar
$ java -jar target/spring-cloud-release-tools-spring-1.0.0.M1.jar --spring.config.name=releaser
----
TIP: Notice that we're downloading the jar to a parent folder, not to `target`. That's because `target` get cleaned
during the build process
==== Specifying A Branch
By deafult the releaser will default to using the `master` branch of `spring-cloud-release`.

View File

@@ -35,11 +35,10 @@ public class Releaser {
return this.projectPomUpdater.retrieveVersionsFromSCRelease();
}
public ProjectVersion updateProjectFromScRelease(File project, Projects versions) {
public void updateProjectFromScRelease(File project, Projects versions) {
this.projectPomUpdater.updateProjectFromSCRelease(project, versions);
ProjectVersion changedVersion = new ProjectVersion(project);
log.info("\n\nProject was successfully updated to [{}]", changedVersion);
return changedVersion;
}
public void buildProject() {
@@ -62,9 +61,13 @@ public class Releaser {
log.info("\nThe docs were published successfully");
}
public void rollbackReleaseVersion(File project, ProjectVersion originalVersion, ProjectVersion changedVersion) {
this.projectGitUpdater.revertChangesIfApplicable(project, changedVersion);
if ((changedVersion.isRelease() || changedVersion.isServiceRelease()) && originalVersion.isSnapshot()) {
public void rollbackReleaseVersion(File project, ProjectVersion originalVersion, ProjectVersion scReleaseVersion) {
if (scReleaseVersion.isSnapshot()) {
log.info("\nWon't rollback a snapshot version");
return;
}
this.projectGitUpdater.revertChangesIfApplicable(project, scReleaseVersion);
if ((scReleaseVersion.isRelease() || scReleaseVersion.isServiceRelease()) && originalVersion.isSnapshot()) {
this.projectBuilder.bumpVersions(originalVersion.bumpedVersion());
this.projectGitUpdater.commitAfterBumpingVersions(project, originalVersion);
log.info("\nSuccessfully reverted the commit and bumped snapshot versions");
@@ -79,6 +82,10 @@ public class Releaser {
}
public void closeMilestone(ProjectVersion releaseVersion) {
if (releaseVersion.isSnapshot()) {
log.info("\nWon't close a milestone for a SNAPSHOT version");
return;
}
this.projectGitUpdater.closeMilestone(releaseVersion);
log.info("\nSuccessfully closed milestone");
}
@@ -86,18 +93,18 @@ public class Releaser {
public void createEmail(ProjectVersion releaseVersion) {
if (releaseVersion.isSnapshot()) {
log.info("\nWon't create email template for a SNAPSHOT version");
} else {
File email = this.templateGenerator.email();
log.info("\nSuccessfully created email template at location [{}]", email);
return;
}
File email = this.templateGenerator.email();
log.info("\nSuccessfully created email template at location [{}]", email);
}
public void createBlog(ProjectVersion releaseVersion, Projects projects) {
if (releaseVersion.isSnapshot()) {
log.info("\nWon't create blog template for a SNAPSHOT version");
} else {
File blog = this.templateGenerator.blog(projects);
log.info("\nSuccessfully created blog template at location [{}]", blog);
return;
}
File blog = this.templateGenerator.blog(projects);
log.info("\nSuccessfully created blog template at location [{}]", blog);
}
}

View File

@@ -17,6 +17,7 @@ import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.project.ProjectBuilder;
import static org.mockito.BDDMockito.then;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.never;
@@ -80,4 +81,18 @@ public class ReleaserTests {
then(this.templateGenerator).should().email();
}
@Test
public void should_not_close_milestone_for_snapshots() throws Exception {
this.releaser.closeMilestone(new ProjectVersion("original", "1.0.0.BUILD-SNAPSHOT"));
then(this.projectGitUpdater).should(never()).closeMilestone(any(ProjectVersion.class));
}
@Test
public void should_not_rollback_for_snapshots() throws Exception {
this.releaser.rollbackReleaseVersion(null, null, new ProjectVersion("original", "1.0.0.BUILD-SNAPSHOT"));
then(this.projectGitUpdater).should(never()).revertChangesIfApplicable(any(File.class), any(ProjectVersion.class));
}
}

View File

@@ -2,6 +2,10 @@ package org.springframework.cloud.release.internal.spring;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -10,6 +14,8 @@ import org.springframework.cloud.release.internal.ReleaserProperties;
import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.Projects;
import static org.springframework.cloud.release.internal.spring.Task.task;
/**
* Releaser that gets input from console
*
@@ -18,10 +24,6 @@ import org.springframework.cloud.release.internal.pom.Projects;
public class SpringReleaser {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String MSG = "'q' to quit and 's' to skip\n\n";
private static final String SKIP = "s";
private static final String QUIT = "q";
private final Releaser releaser;
private final ReleaserProperties properties;
@@ -30,80 +32,168 @@ public class SpringReleaser {
this.properties = properties;
}
private final List<Task> TASKS = Stream.of(
task("UPDATING POMS",
"Update poms with versions from Spring Cloud Release",
args -> args.releaser.updateProjectFromScRelease(args.project, args.projects)),
task("BUILD PROJECT",
"Build the project",
args -> args.releaser.buildProject()),
task("COMMITTING (ALL) AND PUSHING TAGS (NON-SNAPSHOTS)",
"Commit, tag and push the tag",
args -> args.releaser.commitAndPushTags(args.project, args.versionFromScRelease)),
task("ARTIFACT DEPLOYMENT",
"Deploy the artifacts to Artifactory",
args -> args.releaser.deploy()),
task("PUBLISHING DOCS",
"Publish the docs",
args -> args.releaser.publishDocs(args.versionFromScRelease)),
task("REVERTING CHANGES & BUMPING VERSION (RELEASE ONLY)",
"Go back to snapshots and bump originalVersion by patch",
args -> args.releaser.rollbackReleaseVersion(args.project, args.originalVersion, args.versionFromScRelease)),
task("PUSHING CHANGES",
"Push the commits",
args -> args.releaser.pushCurrentBranch(args.project)),
task("CLOSING MILESTONE",
"Close the milestone at Github",
args -> args.releaser.closeMilestone(args.versionFromScRelease)),
task("CREATING TEMPLATES",
"Create email / tweet etc. templates",
args -> {
args.releaser.createEmail(args.versionFromScRelease);
args.releaser.createBlog(args.versionFromScRelease, args.projects);
})
).collect(Collectors.toList());
private final List<Task> COMPOSITE_TASKS = Stream.of(
task("FULL RELEASE",
"Perform a full release of this project without interruptions",
args -> TASKS.forEach(task -> task.execute(args))),
task("FULL VERBOSE RELEASE",
"Perform a full release of this project in a verbose mode (you'll be asked about skipping steps)",
args -> TASKS.forEach(task -> task.execute(args)))
).collect(Collectors.toList());
private final List<Task> ALL_TASKS = Stream.of(
COMPOSITE_TASKS,
TASKS
).flatMap(List::stream).collect(Collectors.toList());
public void release() {
log.info("\n\n\n=== RETRIEVING VERSIONS ===\n\nWill clone Spring Cloud Release"
+ " to retrieve all versions for the branch [{}]", this.properties.getPom().getBranch());
log.info(buildOptionsText().toString());
printVersionRetreival();
String workingDir = this.properties.getWorkingDir();
File project = new File(workingDir);
ProjectVersion originalVersion = new ProjectVersion(project);
Projects projects = this.releaser.retrieveVersionsFromSCRelease();
ProjectVersion versionFromScRelease = projects.forFile(project);
log.info("\n\n\n=== UPDATING POMS ===\n\nWill run the application "
+ "for root folder [{}]. \n\nPress ENTER to continue {}", workingDir, MSG);
boolean skipPoms = skipStep();
if (!skipPoms) {
versionFromScRelease = this.releaser.updateProjectFromScRelease(project, projects);
int chosenOption = chosenOption();
log.info("\n\n\nYou chose [{}]: [{}]\n\n\n", chosenOption, ALL_TASKS.get(chosenOption).description);
boolean verbose = chosenOption == 1;
Task task = taskFromOption(chosenOption);
Args args = new Args(this.releaser, project, projects, originalVersion, versionFromScRelease,
this.properties, verbose);
task.consumer.accept(args);
}
private StringBuilder buildOptionsText() {
StringBuilder msg = new StringBuilder();
msg.append("\n\n\n=== WHAT DO YOU WANT TO DO? ===\n\n");
for (int i = 0; i < ALL_TASKS.size(); i++) {
msg.append(i).append(") ").append(ALL_TASKS.get(i).description).append("\n");
}
log.info("\n\n\n=== BUILD PROJECT ===\n\nPress ENTER to build the project {}", MSG);
boolean skipBuild = skipStep();
if (!skipBuild) {
this.releaser.buildProject();
msg.append("\n\n").append("You can press 'q' to quit\n\n");
return msg;
}
private void printVersionRetreival() {
log.info("\n\n\n=== RETRIEVING VERSIONS ===\n\nWill clone Spring Cloud Release"
+ " to retrieve all versions for the branch [{}]", this.properties.getPom().getBranch());
}
private Task taskFromOption(int option) {
return ALL_TASKS.get(option);
}
int chosenOption() {
String input = System.console().readLine();
switch (input.toLowerCase()) {
case "q":
System.exit(0);
default:
return Integer.parseInt(input);
}
log.info("\n\n\n=== COMMITTING (ALL) AND PUSHING TAGS (NON-SNAPSHOTS) ===\n\nPress ENTER to commit, tag and push the tag {}", MSG);
boolean skipCommit = skipStep();
if (!skipCommit) {
this.releaser.commitAndPushTags(project, versionFromScRelease);
}
log.info("\n\n\n=== ARTIFACT DEPLOYMENT ===\n\nPress ENTER to deploy the artifacts {}", MSG);
boolean skipDeployment = skipStep();
if (!skipDeployment) {
this.releaser.deploy();
}
log.info("\n\n\n=== PUBLISHING DOCS ===\n\nPress ENTER to publish the docs {}", MSG);
boolean skipDocs = skipStep();
if (!skipDocs) {
this.releaser.publishDocs(versionFromScRelease);
}
if (!versionFromScRelease.isSnapshot()) {
log.info("\n\n\n=== REVERTING CHANGES & BUMPING VERSION (RELEASE ONLY)===\n\nPress ENTER to go "
+ "back to snapshots and bump originalVersion by patch {}", MSG);
boolean skipRevert = skipStep();
if (!skipRevert) {
this.releaser.rollbackReleaseVersion(project, originalVersion, versionFromScRelease);
}
}
log.info("\n\n\n=== PUSHING CHANGES===\n\nPress ENTER to push the commits {}", MSG);
boolean skipPush = skipStep();
if (!skipPush) {
this.releaser.pushCurrentBranch(project);
}
if (!versionFromScRelease.isSnapshot()) {
log.info("\n\n\n=== CLOSING MILESTONE===\n\nPress ENTER to close the milestone at Github {}", MSG);
boolean skipMilestone = skipStep();
if (!skipMilestone) {
this.releaser.closeMilestone(versionFromScRelease);
}
}
if (!versionFromScRelease.isSnapshot()) {
log.info("\n\n\n=== CREATING TEMPLATES===\n\nPress ENTER to create email / tweet etc. templates {}", MSG);
boolean skipTemplates = skipStep();
if (!skipTemplates) {
this.releaser.createEmail(versionFromScRelease);
this.releaser.createBlog(versionFromScRelease, projects);
}
}
class Task {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String MSG = "'q' to quit and 's' to skip\n\n";
final String header;
final String description;
final Consumer<Args> consumer;
Task(String header, String description, Consumer<Args> consumer) {
this.header = header;
this.description = description;
this.consumer = consumer;
}
void execute(Args args) {
boolean verbose = args.verbose;
printLog(verbose);
if (verbose) {
boolean skipStep = skipStep();
if (!skipStep) {
consumer.accept(args);
}
} else {
consumer.accept(args);
}
}
private void printLog(boolean shouldSkip) {
log.info("\n\n\n=== {} ===\n\n{} {}\n\n", header, description, shouldSkip ? MSG : "");
}
boolean skipStep() {
String input = System.console().readLine();
switch (input.toLowerCase()) {
case SKIP:
case "s":
return true;
case QUIT:
case "q":
System.exit(0);
return true;
default:
return false;
}
}
static Task task(String header, String description, Consumer<Args> function) {
return new Task(header, description, function);
}
}
class Args {
final Releaser releaser;
final File project;
final Projects projects;
final ProjectVersion originalVersion;
final ProjectVersion versionFromScRelease;
final ReleaserProperties properties;
final boolean verbose;
Args(Releaser releaser, File project, Projects projects, ProjectVersion originalVersion,
ProjectVersion versionFromScRelease, ReleaserProperties properties,
boolean verbose) {
this.releaser = releaser;
this.project = project;
this.projects = projects;
this.originalVersion = originalVersion;
this.versionFromScRelease = versionFromScRelease;
this.properties = properties;
this.verbose = verbose;
}
}

View File

@@ -7,7 +7,6 @@ import java.nio.file.Files;
import java.util.Iterator;
import org.apache.maven.model.Model;
import org.assertj.core.api.BDDAssertions;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
@@ -16,7 +15,6 @@ import org.junit.Test;
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.template.TemplateGenerator;
import org.springframework.cloud.release.internal.git.GitTestUtils;
import org.springframework.cloud.release.internal.git.ProjectGitUpdater;
import org.springframework.cloud.release.internal.pom.ProjectPomUpdater;
@@ -24,6 +22,7 @@ import org.springframework.cloud.release.internal.pom.ProjectVersion;
import org.springframework.cloud.release.internal.pom.TestPomReader;
import org.springframework.cloud.release.internal.pom.TestUtils;
import org.springframework.cloud.release.internal.project.ProjectBuilder;
import org.springframework.cloud.release.internal.template.TemplateGenerator;
import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.BDDAssertions.then;
@@ -105,6 +104,27 @@ public class AcceptanceTests {
.contains("I am pleased to announce that the Release Candidate 1 (RC1)");
}
@Test
public void should_generate_templates_only() throws Exception {
File origin = GitTestUtils.clonedProject(this.tmp.newFolder(), this.springCloudConsulProject);
pomVersionIsEqualTo(origin, "1.2.0.BUILD-SNAPSHOT");
pomParentVersionIsEqualTo(origin, "1.2.0.BUILD-SNAPSHOT");
File project = GitTestUtils.clonedProject(this.tmp.newFolder(), tmpFile("spring-cloud-consul"));
GitTestUtils.setOriginOnProjectToTmp(origin, project);
SpringReleaser releaser = templateOnlyReleaser(project, "Dalston.RC1", "1.2.0.RC1");
releaser.release();
then(this.gitUpdater.executed).isFalse();
then(emailTemplate()).exists();
then(emailTemplateContents())
.contains("Spring Cloud Dalston.RC1 available")
.contains("Spring Cloud Dalston RC1 Train release");
then(blogTemplate()).exists();
then(blogTemplateContents())
.contains("I am pleased to announce that the Release Candidate 1 (RC1)");
}
private Iterable<RevCommit> listOfCommits(File project) throws GitAPIException {
return GitTestUtils.openGitProject(project).log().call();
}
@@ -132,8 +152,7 @@ public class AcceptanceTests {
}
private void tagIsPresentInOrigin(File origin, String expectedTag) throws GitAPIException {
BDDAssertions
.then(GitTestUtils.openGitProject(origin).tagList()
then(GitTestUtils.openGitProject(origin).tagList()
.call().iterator().next().getName()).endsWith(expectedTag);
}
@@ -159,6 +178,25 @@ public class AcceptanceTests {
private SpringReleaser releaser(File projectFile, String branch, String expectedVersion) throws Exception {
ReleaserProperties properties = releaserProperties(projectFile, branch);
Releaser releaser = defaultReleaser(expectedVersion, properties);
return new SpringReleaser(releaser, properties) {
@Override int chosenOption() {
return 0;
}
};
}
private SpringReleaser templateOnlyReleaser(File projectFile, String branch, String expectedVersion) throws Exception {
ReleaserProperties properties = releaserProperties(projectFile, branch);
Releaser releaser = defaultReleaser(expectedVersion, properties);
return new SpringReleaser(releaser, properties) {
@Override int chosenOption() {
return 10;
}
};
}
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,
@@ -167,11 +205,7 @@ public class AcceptanceTests {
Releaser releaser = new Releaser(pomUpdater, projectBuilder, gitUpdater,
templateGenerator);
this.gitUpdater = gitUpdater;
return new SpringReleaser(releaser, properties) {
@Override boolean skipStep() {
return false;
}
};
return releaser;
}
private ReleaserProperties releaserProperties(File project, String branch) throws URISyntaxException {