Introduction of non interactive mode (#45)
without this change the only way to execute tasks is to provide their number from the console with this change we can provide execution options from the command line, we can execute full releases, single steps, multiple steps, bounded and unbounded ranges fixes #37 fixes #44 prerequisite for #30
This commit is contained in:
committed by
GitHub
parent
d16d3dee49
commit
fb44e65e62
122
README.adoc
122
README.adoc
@@ -41,7 +41,7 @@ why this tool makes it easy to automate the release / dependency update process
|
||||
- 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
|
||||
=== What should I do first?
|
||||
|
||||
Members of the Spring Cloud Team typically use this tool as follows. They first
|
||||
clone the releaser locally and build the jar manually
|
||||
@@ -53,6 +53,8 @@ $ cd spring-cloud-release-tools
|
||||
$ ./mvnw clean install
|
||||
----
|
||||
|
||||
=== How to run it (interactive mode)
|
||||
|
||||
Go to your project (e.g. Spring Cloud Sleuth)
|
||||
|
||||
[source,bash]
|
||||
@@ -79,21 +81,133 @@ You will see text similar to this one
|
||||
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
|
||||
5) Deploy the artifacts
|
||||
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
|
||||
10) Create email / blog / tweet etc. templates
|
||||
|
||||
You can pick a range of options by using the hyphen - e.g. '2-4' will execute jobs [2,3,4]
|
||||
You can execute all tasks starting from a job by using a hyphen and providing only one number - e.g. '8-' will execute jobs [8,9,10]
|
||||
You can execute given tasks by providing a colon separated list of tasks - e.g. '3,7,8' will execute jobs [3,7,8]
|
||||
|
||||
You can press 'q' to quit
|
||||
----
|
||||
|
||||
Just pick a number and continue! Pick either a full release or single steps.
|
||||
Just pick a number and continue! Pick either a full release or single steps. You can also pick
|
||||
ranges or multiple steps. You can also provide the range only with the starting step
|
||||
- that you will execute all steps starting from the given one.
|
||||
|
||||
TIP: Read before picking a number cause it might have changed between tool releases ;)
|
||||
|
||||
=== How to run it (automatic mode)
|
||||
|
||||
Go to your project (e.g. Spring Cloud Sleuth) and execute the application with `-h` or `--help`
|
||||
flag.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone git@github.com:spring-cloud/spring-cloud-sleuth.git
|
||||
$ cd spring-cloud-sleuth
|
||||
$ # example of running the releaser agains Dalston.SR1 tag with 1.0.0.BUILD-SNAPSHOT version of the releaser
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -h
|
||||
----
|
||||
|
||||
You will see a help screen looking like more or less like this
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
Here you can find the list of tasks in order
|
||||
|
||||
[updatePoms,build,commit,deploy,docs,snapshots,push,closeMilestone,createTemplates]
|
||||
|
||||
|
||||
Option Description
|
||||
------ -----------
|
||||
-a, --start-from <String> Starts all release task starting from the given
|
||||
task. Requires passing the task name (either
|
||||
one letter or the full name)
|
||||
-b, --build [String] Build the project
|
||||
-c, --commit [String] Commit, tag and push the tag
|
||||
-d, --deploy [String] Deploy the artifacts
|
||||
-f, --full-release [Boolean] Do you want to do the full release (default:
|
||||
false)
|
||||
-h, --help [String]
|
||||
-i, --interactive <Boolean> Do you want to set the properties from the
|
||||
command line (default: true)
|
||||
-m, --closeMilestone [String] Close the milestone at Github
|
||||
-o, --docs [String] Publish the docs
|
||||
-p, --push [String] Push the commits
|
||||
-r, --range <String> Runs release tasks from the given range.
|
||||
Requires passing the task names with a
|
||||
hyphen. The first task is inclusive, the
|
||||
second inclusive. E.g. 's-m' would mean
|
||||
running 'snapshot', 'push' and 'milestone'
|
||||
tasks
|
||||
-s, --snapshots [String] Go back to snapshots and bump originalVersion
|
||||
by patch
|
||||
-t, --createTemplates [String] Create email / blog / tweet etc. templates
|
||||
-u, --updatePoms [String] Update poms with versions from Spring Cloud
|
||||
Release
|
||||
|
||||
Examples of usage:
|
||||
|
||||
Run 'build' & 'commit' & 'deploy'
|
||||
java -jar releaser.jar -b -c -d
|
||||
|
||||
Start from 'push'
|
||||
java -jar releaser.jar -a push
|
||||
|
||||
Range 'docs' -> 'push'
|
||||
java -jar releaser.jar -r o-p
|
||||
----
|
||||
|
||||
The Releaser can use two sets of options. The configuration options like `releaser.pom.branch`
|
||||
and the task switches. For the tasks you can use either the full names or short switches. For example
|
||||
providing range of tasks via switches `o-p` is equivalent to full name `docs-push`.
|
||||
|
||||
A couple of examples:
|
||||
|
||||
.Doing the full release in interactive mode (asking for skipping steps)
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone git@github.com:spring-cloud/spring-cloud-sleuth.git
|
||||
$ cd spring-cloud-sleuth
|
||||
$ # example of running the releaser agains Dalston.SR1 tag with 1.0.0.BUILD-SNAPSHOT version of the releaser
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --full-release
|
||||
----
|
||||
|
||||
.Doing the full release in non interactive mode (automatic release)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --full-release --interactive=false
|
||||
----
|
||||
|
||||
.Updating pom, closing milestone & createTemplates in interactive mode
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -u -m -t
|
||||
----
|
||||
|
||||
.Running all tasks starting from 'push' (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -a push -i=false
|
||||
----
|
||||
|
||||
.Running tasks from 'docs' (inclusive) to 'push' (inclusive) (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -r d-p -i=false
|
||||
----
|
||||
|
||||
.Running single task 'closeMilestone' (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --closeMilestone -i=false
|
||||
----
|
||||
|
||||
=== 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
|
||||
|
||||
@@ -31,7 +31,7 @@ why this tool makes it easy to automate the release / dependency update process
|
||||
- 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
|
||||
=== What should I do first?
|
||||
|
||||
Members of the Spring Cloud Team typically use this tool as follows. They first
|
||||
clone the releaser locally and build the jar manually
|
||||
@@ -43,6 +43,8 @@ $ cd spring-cloud-release-tools
|
||||
$ ./mvnw clean install
|
||||
----
|
||||
|
||||
=== How to run it (interactive mode)
|
||||
|
||||
Go to your project (e.g. Spring Cloud Sleuth)
|
||||
|
||||
[source,bash]
|
||||
@@ -69,21 +71,133 @@ You will see text similar to this one
|
||||
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
|
||||
5) Deploy the artifacts
|
||||
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
|
||||
10) Create email / blog / tweet etc. templates
|
||||
|
||||
You can pick a range of options by using the hyphen - e.g. '2-4' will execute jobs [2,3,4]
|
||||
You can execute all tasks starting from a job by using a hyphen and providing only one number - e.g. '8-' will execute jobs [8,9,10]
|
||||
You can execute given tasks by providing a colon separated list of tasks - e.g. '3,7,8' will execute jobs [3,7,8]
|
||||
|
||||
You can press 'q' to quit
|
||||
----
|
||||
|
||||
Just pick a number and continue! Pick either a full release or single steps.
|
||||
Just pick a number and continue! Pick either a full release or single steps. You can also pick
|
||||
ranges or multiple steps. You can also provide the range only with the starting step
|
||||
- that you will execute all steps starting from the given one.
|
||||
|
||||
TIP: Read before picking a number cause it might have changed between tool releases ;)
|
||||
|
||||
=== How to run it (automatic mode)
|
||||
|
||||
Go to your project (e.g. Spring Cloud Sleuth) and execute the application with `-h` or `--help`
|
||||
flag.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone git@github.com:spring-cloud/spring-cloud-sleuth.git
|
||||
$ cd spring-cloud-sleuth
|
||||
$ # example of running the releaser agains Dalston.SR1 tag with 1.0.0.BUILD-SNAPSHOT version of the releaser
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -h
|
||||
----
|
||||
|
||||
You will see a help screen looking like more or less like this
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
Here you can find the list of tasks in order
|
||||
|
||||
[updatePoms,build,commit,deploy,docs,snapshots,push,closeMilestone,createTemplates]
|
||||
|
||||
|
||||
Option Description
|
||||
------ -----------
|
||||
-a, --start-from <String> Starts all release task starting from the given
|
||||
task. Requires passing the task name (either
|
||||
one letter or the full name)
|
||||
-b, --build [String] Build the project
|
||||
-c, --commit [String] Commit, tag and push the tag
|
||||
-d, --deploy [String] Deploy the artifacts
|
||||
-f, --full-release [Boolean] Do you want to do the full release (default:
|
||||
false)
|
||||
-h, --help [String]
|
||||
-i, --interactive <Boolean> Do you want to set the properties from the
|
||||
command line (default: true)
|
||||
-m, --closeMilestone [String] Close the milestone at Github
|
||||
-o, --docs [String] Publish the docs
|
||||
-p, --push [String] Push the commits
|
||||
-r, --range <String> Runs release tasks from the given range.
|
||||
Requires passing the task names with a
|
||||
hyphen. The first task is inclusive, the
|
||||
second inclusive. E.g. 's-m' would mean
|
||||
running 'snapshot', 'push' and 'milestone'
|
||||
tasks
|
||||
-s, --snapshots [String] Go back to snapshots and bump originalVersion
|
||||
by patch
|
||||
-t, --createTemplates [String] Create email / blog / tweet etc. templates
|
||||
-u, --updatePoms [String] Update poms with versions from Spring Cloud
|
||||
Release
|
||||
|
||||
Examples of usage:
|
||||
|
||||
Run 'build' & 'commit' & 'deploy'
|
||||
java -jar releaser.jar -b -c -d
|
||||
|
||||
Start from 'push'
|
||||
java -jar releaser.jar -a push
|
||||
|
||||
Range 'docs' -> 'push'
|
||||
java -jar releaser.jar -r o-p
|
||||
----
|
||||
|
||||
The Releaser can use two sets of options. The configuration options like `releaser.pom.branch`
|
||||
and the task switches. For the tasks you can use either the full names or short switches. For example
|
||||
providing range of tasks via switches `o-p` is equivalent to full name `docs-push`.
|
||||
|
||||
A couple of examples:
|
||||
|
||||
.Doing the full release in interactive mode (asking for skipping steps)
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone git@github.com:spring-cloud/spring-cloud-sleuth.git
|
||||
$ cd spring-cloud-sleuth
|
||||
$ # example of running the releaser agains Dalston.SR1 tag with 1.0.0.BUILD-SNAPSHOT version of the releaser
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --full-release
|
||||
----
|
||||
|
||||
.Doing the full release in non interactive mode (automatic release)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --full-release --interactive=false
|
||||
----
|
||||
|
||||
.Updating pom, closing milestone & createTemplates in interactive mode
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -u -m -t
|
||||
----
|
||||
|
||||
.Running all tasks starting from 'push' (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -a push -i=false
|
||||
----
|
||||
|
||||
.Running tasks from 'docs' (inclusive) to 'push' (inclusive) (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser -r d-p -i=false
|
||||
----
|
||||
|
||||
.Running single task 'closeMilestone' (automatic)
|
||||
[source,bash]
|
||||
----
|
||||
$ java -jar ~/repo/spring-cloud-release-tools/spring-cloud-release-tools-spring/target/spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar --releaser.pom.branch=vDalston.SR1 --spring.config.name=releaser --closeMilestone -i=false
|
||||
----
|
||||
|
||||
=== 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
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
<artifactId>spring-cloud-release-tools-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
<version>5.0.3</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -19,6 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.release.internal.options.Options;
|
||||
import org.springframework.cloud.release.internal.options.Parser;
|
||||
import org.springframework.cloud.release.internal.spring.SpringReleaser;
|
||||
|
||||
@SpringBootApplication
|
||||
@@ -29,9 +31,12 @@ public class ReleaserApplication implements CommandLineRunner {
|
||||
}
|
||||
|
||||
@Autowired SpringReleaser releaser;
|
||||
@Autowired Parser parser;
|
||||
|
||||
@Override public void run(String... strings) throws Exception {
|
||||
this.releaser.release();
|
||||
Options options = this.parser.parse(strings);
|
||||
this.releaser.release(options);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.cloud.release.internal.options;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class Options {
|
||||
public Boolean fullRelease = true;
|
||||
public Boolean interactive = true;
|
||||
public List<String> taskNames = new ArrayList<>();
|
||||
public String startFrom = "";
|
||||
public String range = "";
|
||||
|
||||
Options(Boolean fullRelease, Boolean interactive, List<String> taskNames, String startFrom,
|
||||
String range) {
|
||||
this.fullRelease = fullRelease;
|
||||
this.interactive = interactive;
|
||||
this.taskNames = taskNames;
|
||||
this.startFrom = startFrom;
|
||||
this.range = range;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.springframework.cloud.release.internal.options;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OptionsBuilder {
|
||||
private Boolean fullRelease = false;
|
||||
private Boolean interactive = true;
|
||||
private List<String> taskNames = new ArrayList<>();
|
||||
private String startFrom = "";
|
||||
private String range = "";
|
||||
|
||||
public OptionsBuilder fullRelease(Boolean fullRelease) {
|
||||
this.fullRelease = fullRelease;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder interactive(Boolean interactive) {
|
||||
this.interactive = interactive;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder taskNames(List<String> taskNames) {
|
||||
this.taskNames = taskNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder startFrom(String startFrom) {
|
||||
this.startFrom = startFrom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder range(String range) {
|
||||
this.range = range;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Options options() {
|
||||
return new Options(this.fullRelease, this.interactive, this.taskNames, this.startFrom,
|
||||
this.range);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.springframework.cloud.release.internal.options;
|
||||
|
||||
/**
|
||||
* Converts input arguments to a {@link Options}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public interface Parser {
|
||||
Options parse(String[] args);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.cloud.release.internal.Releaser;
|
||||
import org.springframework.cloud.release.internal.ReleaserProperties;
|
||||
import org.springframework.cloud.release.internal.pom.ProjectVersion;
|
||||
import org.springframework.cloud.release.internal.pom.Projects;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class Args {
|
||||
final Releaser releaser;
|
||||
final File project;
|
||||
final Projects projects;
|
||||
final ProjectVersion originalVersion;
|
||||
final ProjectVersion versionFromScRelease;
|
||||
final ReleaserProperties properties;
|
||||
final boolean interactive;
|
||||
|
||||
Args(Releaser releaser, File project, Projects projects, ProjectVersion originalVersion,
|
||||
ProjectVersion versionFromScRelease, ReleaserProperties properties,
|
||||
boolean interactive) {
|
||||
this.releaser = releaser;
|
||||
this.project = project;
|
||||
this.projects = projects;
|
||||
this.originalVersion = originalVersion;
|
||||
this.versionFromScRelease = versionFromScRelease;
|
||||
this.properties = properties;
|
||||
this.interactive = interactive;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import joptsimple.ArgumentAcceptingOptionSpec;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.cloud.release.internal.options.Options;
|
||||
import org.springframework.cloud.release.internal.options.OptionsBuilder;
|
||||
import org.springframework.cloud.release.internal.options.Parser;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class OptionsParser implements Parser {
|
||||
|
||||
@Override
|
||||
public Options parse(String[] args) {
|
||||
OptionParser parser = new OptionParser();
|
||||
parser.allowsUnrecognizedOptions();
|
||||
try {
|
||||
ArgumentAcceptingOptionSpec<Boolean> fullReleaseOpt = parser
|
||||
.acceptsAll(Arrays.asList("f", "full-release"),
|
||||
"Do you want to do the full release")
|
||||
.withOptionalArg().ofType(Boolean.class).defaultsTo(false);
|
||||
ArgumentAcceptingOptionSpec<Boolean> interactiveOpt = parser
|
||||
.acceptsAll(Arrays.asList("i", "interactive"),
|
||||
"Do you want to set the properties from the command line")
|
||||
.withRequiredArg().ofType(Boolean.class).defaultsTo(true);
|
||||
Tasks.ALL_TASKS.forEach(task ->
|
||||
parser.acceptsAll(Arrays.asList(task.shortName, task.name),
|
||||
task.description)
|
||||
.withOptionalArg());
|
||||
ArgumentAcceptingOptionSpec<String> startFromOpt = parser
|
||||
.acceptsAll(Arrays.asList("a", "start-from"),
|
||||
"Starts all release task starting from the given task. Requires passing the task name (either one letter or the full name)")
|
||||
.withRequiredArg().ofType(String.class);
|
||||
ArgumentAcceptingOptionSpec<String> rangeOpt = parser.acceptsAll(Arrays.asList("r", "range"),
|
||||
"Runs release tasks from the given range. Requires passing the task names with a hyphen. The first task is inclusive, the second inclusive. E.g. 's-m' would mean running 'snapshot', 'push' and 'milestone' tasks")
|
||||
.withRequiredArg().ofType(String.class);
|
||||
parser.acceptsAll(Arrays.asList("h", "help"))
|
||||
.withOptionalArg();
|
||||
OptionSet options = parser.parse(args);
|
||||
if (options.has("h")) {
|
||||
printHelpMessage(parser);
|
||||
System.exit(0);
|
||||
}
|
||||
Boolean interactive = options.valueOf(interactiveOpt);
|
||||
Boolean fullRelease = options.has(fullReleaseOpt);
|
||||
List<String> taskNames = Tasks.ALL_TASKS.stream()
|
||||
.filter(task -> options.has(task.name)).map(task -> task.name)
|
||||
.collect(Collectors.toList());
|
||||
String startFrom = options.valueOf(startFromOpt);
|
||||
String range = options.valueOf(rangeOpt);
|
||||
return new OptionsBuilder()
|
||||
.fullRelease(fullRelease)
|
||||
.interactive(interactive)
|
||||
.taskNames(taskNames)
|
||||
.startFrom(startFrom)
|
||||
.range(range)
|
||||
.options();
|
||||
}
|
||||
catch (Exception e) {
|
||||
printErrorMessage(e, parser);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void printErrorMessage(Exception e, OptionParser parser) {
|
||||
System.err.println(e.getMessage());
|
||||
System.err.println(intro());
|
||||
System.err.println(
|
||||
"java -jar spring-cloud-release-tools-spring-1.0.0.BUILD-SNAPSHOT.jar [options...] ");
|
||||
try {
|
||||
parser.printHelpOn(System.err);
|
||||
} catch (IOException e1) {
|
||||
throw new IllegalStateException(e1);
|
||||
}
|
||||
System.err.println(examples());
|
||||
}
|
||||
|
||||
private void printHelpMessage(OptionParser parser) {
|
||||
try {
|
||||
System.out.println(intro());
|
||||
parser.printHelpOn(System.out);
|
||||
System.out.println(examples());
|
||||
} catch (IOException e1) {
|
||||
throw new IllegalStateException(e1);
|
||||
}
|
||||
}
|
||||
|
||||
private String intro() {
|
||||
return "\nHere you can find the list of tasks in order\n\n[" + Tasks.tasksInOrder() + "]\n\n";
|
||||
}
|
||||
|
||||
private String examples() {
|
||||
return "\nExamples of usage:\n\n"
|
||||
+ "Run 'build' & 'commit' & 'deploy'\n"
|
||||
+ "java -jar jar.jar -b -c -d\n\n"
|
||||
+ "Start from 'push'\n"
|
||||
+ "java -jar releaser.jar -a push\n\n"
|
||||
+ "Range 'docs' -> 'push'\n"
|
||||
+ "java -jar releaser.jar -r o-p\n\n"
|
||||
+ "\n\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.release.internal.Releaser;
|
||||
import org.springframework.cloud.release.internal.ReleaserProperties;
|
||||
import org.springframework.cloud.release.internal.options.Options;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class OptionsProcessor {
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final Releaser releaser;
|
||||
private final ReleaserProperties properties;
|
||||
private final List<Task> allTasks;
|
||||
|
||||
OptionsProcessor(Releaser releaser, ReleaserProperties properties) {
|
||||
this(releaser, properties, Tasks.ALL_TASKS);
|
||||
}
|
||||
|
||||
OptionsProcessor(Releaser releaser, ReleaserProperties properties, List<Task> allTasks) {
|
||||
this.releaser = releaser;
|
||||
this.properties = properties;
|
||||
this.allTasks = allTasks;
|
||||
}
|
||||
|
||||
void processOptions(Options options, Args defaultArgs) {
|
||||
Args args = args(defaultArgs, options.interactive);
|
||||
if (options.fullRelease && !options.interactive) {
|
||||
log.info("Executing a full release in non-interactive mode");
|
||||
releaseTask().execute(args);
|
||||
} else if (options.fullRelease && options.interactive) {
|
||||
log.info("Executing a full release in interactive mode");
|
||||
releaseVerboseTask().execute(args);
|
||||
} else if (StringUtils.hasText(options.startFrom)) {
|
||||
startFrom(options, args);
|
||||
} else if (StringUtils.hasText(options.range)) {
|
||||
range(options.range, args);
|
||||
} else if (!options.taskNames.isEmpty()) {
|
||||
tasks(options.taskNames, args);
|
||||
} else if (options.interactive) {
|
||||
interactiveOnly(args);
|
||||
} else {
|
||||
throw new IllegalStateException("You haven't picked any recognizable option");
|
||||
}
|
||||
}
|
||||
|
||||
Task releaseTask() {
|
||||
return Tasks.RELEASE;
|
||||
}
|
||||
|
||||
Task releaseVerboseTask() {
|
||||
return Tasks.RELEASE_VERBOSE;
|
||||
}
|
||||
|
||||
private void interactiveOnly(Args defaultArgs) {
|
||||
log.info(buildOptionsText().toString());
|
||||
executeTaskFromOption(defaultArgs);
|
||||
}
|
||||
|
||||
private void tasks(List<String> taskNames, Args defaultArgs) {
|
||||
Tasks.forNames(this.allTasks, taskNames).forEach(task -> task.execute(defaultArgs));
|
||||
}
|
||||
|
||||
private void range(String range, Args defaultArgs) {
|
||||
String[] splitRange = range.split("-");
|
||||
String start = splitRange[0];
|
||||
String stop = "";
|
||||
if (splitRange.length == 2) {
|
||||
stop = splitRange[1];
|
||||
}
|
||||
boolean started = false;
|
||||
boolean sameRange = start.equals(stop);
|
||||
for (Task task : this.allTasks) {
|
||||
if (start.equals(task.name) || start.equals(task.shortName)) {
|
||||
started = true;
|
||||
task.execute(defaultArgs);
|
||||
if (sameRange) {
|
||||
break;
|
||||
}
|
||||
} else if (started && (stop.equals(task.name) || stop.equals(task.shortName))) {
|
||||
task.execute(defaultArgs);
|
||||
break;
|
||||
} else if (started) {
|
||||
task.execute(defaultArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startFrom(Options options, Args defaultArgs) {
|
||||
boolean started = false;
|
||||
for (Task task : this.allTasks) {
|
||||
if (options.startFrom.equals(task.name) || options.startFrom.equals(task.shortName)) {
|
||||
started = true;
|
||||
task.execute(defaultArgs);
|
||||
} else if (started) {
|
||||
task.execute(defaultArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 < this.allTasks.size(); i++) {
|
||||
msg.append(i).append(") ").append(this.allTasks.get(i).description).append("\n");
|
||||
}
|
||||
msg.append("\n").append("You can pick a range of options by using the hyphen - e.g. '2-4' will execute jobs [2,3,4]\n");
|
||||
msg.append("You can execute all tasks starting from a job by using a hyphen and providing only one number - e.g. '8-' will execute jobs [8,9,10]\n");
|
||||
msg.append("You can execute given tasks by providing a colon separated list of tasks - e.g. '3,7,8' will execute jobs [3,7,8]\n");
|
||||
msg.append("\n").append("You can press 'q' to quit\n\n");
|
||||
return msg;
|
||||
}
|
||||
|
||||
void executeTaskFromOption(Args defaultArgs) {
|
||||
String input = chosenOption();
|
||||
switch (input.toLowerCase()) {
|
||||
case "q":
|
||||
System.exit(0);
|
||||
default:
|
||||
if (input.contains("-")) {
|
||||
rangeInteractive(defaultArgs, input);
|
||||
} else if (input.contains(",")) {
|
||||
tasksInteractive(defaultArgs, input);
|
||||
} else {
|
||||
singleTask(defaultArgs, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void singleTask(Args defaultArgs, String input) {
|
||||
int chosenOption = Integer.parseInt(input);
|
||||
Task task = this.allTasks.get(chosenOption);
|
||||
boolean interactive = false;
|
||||
if (task == Tasks.RELEASE_VERBOSE) {
|
||||
interactive = true;
|
||||
}
|
||||
log.info("\n\n\nYou chose [{}]: [{}]\n\n\n", chosenOption, task.description);
|
||||
task.execute(args(defaultArgs, interactive));
|
||||
}
|
||||
|
||||
private void tasksInteractive(Args defaultArgs, String input) {
|
||||
List<String> tasks = Arrays.asList(input.split(","));
|
||||
List<String> taskNames = new ArrayList<>();
|
||||
for (String task : tasks) {
|
||||
Integer taskIndex = Integer.valueOf(task);
|
||||
taskNames.add(this.allTasks.get(taskIndex).name);
|
||||
}
|
||||
tasks(taskNames, defaultArgs);
|
||||
}
|
||||
|
||||
private void rangeInteractive(Args defaultArgs, String input) {
|
||||
String[] range = input.split("-");
|
||||
Integer start = Integer.valueOf(range[0]);
|
||||
Integer stop = null;
|
||||
if (range.length == 2) {
|
||||
stop = Integer.valueOf(range[1]);
|
||||
}
|
||||
String firstName = this.allTasks.get(start).name;
|
||||
String second = stop != null ? this.allTasks.get(stop).name : "";
|
||||
range(firstName + "-" + second, defaultArgs);
|
||||
}
|
||||
|
||||
private Args args(Args defaultArgs, boolean interactive) {
|
||||
return new Args(this.releaser, defaultArgs.project, defaultArgs.projects,
|
||||
defaultArgs.originalVersion, defaultArgs.versionFromScRelease, this.properties, interactive);
|
||||
}
|
||||
|
||||
String chosenOption() {
|
||||
return System.console().readLine();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.cloud.release.internal.Releaser;
|
||||
import org.springframework.cloud.release.internal.ReleaserProperties;
|
||||
import org.springframework.cloud.release.internal.gradle.GradleUpdater;
|
||||
import org.springframework.cloud.release.internal.options.Parser;
|
||||
import org.springframework.cloud.release.internal.template.TemplateGenerator;
|
||||
import org.springframework.cloud.release.internal.project.ProjectBuilder;
|
||||
import org.springframework.cloud.release.internal.git.ProjectGitHandler;
|
||||
@@ -37,4 +38,8 @@ class ReleaserConfiguration {
|
||||
handler, new TemplateGenerator(properties, handler),
|
||||
new GradleUpdater(properties)), properties);
|
||||
}
|
||||
|
||||
@Bean Parser optionsParser() {
|
||||
return new OptionsParser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,17 @@ 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;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.release.internal.Releaser;
|
||||
import org.springframework.cloud.release.internal.ReleaserProperties;
|
||||
import org.springframework.cloud.release.internal.options.Options;
|
||||
import org.springframework.cloud.release.internal.options.OptionsBuilder;
|
||||
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
|
||||
*
|
||||
@@ -26,77 +24,45 @@ public class SpringReleaser {
|
||||
|
||||
private final Releaser releaser;
|
||||
private final ReleaserProperties properties;
|
||||
private final OptionsProcessor optionsProcessor;
|
||||
|
||||
@Autowired
|
||||
public SpringReleaser(Releaser releaser, ReleaserProperties properties) {
|
||||
this.releaser = releaser;
|
||||
this.properties = properties;
|
||||
this.optionsProcessor = new OptionsProcessor(releaser, 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, args.versionFromScRelease)),
|
||||
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",
|
||||
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.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 / blog / tweet etc. templates",
|
||||
args -> {
|
||||
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());
|
||||
|
||||
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());
|
||||
SpringReleaser(Releaser releaser, ReleaserProperties properties,
|
||||
OptionsProcessor optionsProcessor) {
|
||||
this.releaser = releaser;
|
||||
this.properties = properties;
|
||||
this.optionsProcessor = optionsProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behaviour - interactive mode
|
||||
*/
|
||||
public void release() {
|
||||
printVersionRetreival();
|
||||
release(new OptionsBuilder().options());
|
||||
}
|
||||
|
||||
public void release(Options options) {
|
||||
printVersionRetrieval();
|
||||
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);
|
||||
assertNoSnapshotsForANonSnapshotProject(projects, versionFromScRelease);
|
||||
log.info(buildOptionsText().toString());
|
||||
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);
|
||||
Projects projectsFromScRelease = this.releaser.retrieveVersionsFromSCRelease();
|
||||
ProjectVersion versionFromScRelease = projectsFromScRelease.forFile(project);
|
||||
assertNoSnapshotsForANonSnapshotProject(projectsFromScRelease, versionFromScRelease);
|
||||
final Args defaultArgs = new Args(this.releaser, project, projectsFromScRelease,
|
||||
originalVersion, versionFromScRelease, this.properties, options.interactive);
|
||||
this.optionsProcessor.processOptions(options, defaultArgs);
|
||||
}
|
||||
|
||||
private void printVersionRetrieval() {
|
||||
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 void assertNoSnapshotsForANonSnapshotProject(Projects projects,
|
||||
@@ -108,104 +74,6 @@ public class SpringReleaser {
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 "s":
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class Task {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private static final String MSG = "\nPress 'q' to quit, 's' to skip, any key to continue\n\n";
|
||||
|
||||
final String name;
|
||||
final String shortName;
|
||||
final String header;
|
||||
final String description;
|
||||
private final Consumer<Args> consumer;
|
||||
|
||||
Task(String name, String shortName, String header, String description, Consumer<Args> consumer) {
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
this.header = header;
|
||||
this.description = description;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
void execute(Args args) {
|
||||
boolean interactive = args.interactive;
|
||||
printLog(interactive);
|
||||
if (interactive) {
|
||||
boolean skipStep = skipStep();
|
||||
if (!skipStep) {
|
||||
consumer.accept(args);
|
||||
}
|
||||
} else {
|
||||
consumer.accept(args);
|
||||
}
|
||||
}
|
||||
|
||||
private void printLog(boolean interactive) {
|
||||
log.info("\n\n\n=== {} ===\n\n{} {}\n\n", header, description, interactive ? MSG : "");
|
||||
}
|
||||
|
||||
boolean skipStep() {
|
||||
String input = chosenOption();
|
||||
switch (input.toLowerCase()) {
|
||||
case "s":
|
||||
return true;
|
||||
case "q":
|
||||
System.exit(0);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String chosenOption() {
|
||||
return System.console().readLine();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* All tasks that can be executed by the releaser
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class Tasks {
|
||||
static Task UPDATING_POMS = task("updatePoms", "u",
|
||||
"UPDATING POMS",
|
||||
"Update poms with versions from Spring Cloud Release",
|
||||
args -> args.releaser.updateProjectFromScRelease(args.project, args.projects, args.versionFromScRelease));
|
||||
static Task BUILD_PROJECT = task("build", "b",
|
||||
"BUILD PROJECT",
|
||||
"Build the project",
|
||||
args -> args.releaser.buildProject());
|
||||
static Task COMMIT = task("commit", "c",
|
||||
"COMMITTING (ALL) AND PUSHING TAGS (NON-SNAPSHOTS)",
|
||||
"Commit, tag and push the tag",
|
||||
args -> args.releaser.commitAndPushTags(args.project, args.versionFromScRelease));
|
||||
static Task DEPLOY = task("deploy", "d",
|
||||
"ARTIFACT DEPLOYMENT",
|
||||
"Deploy the artifacts",
|
||||
args -> args.releaser.deploy());
|
||||
static Task PUBLISH_DOCS = task("docs", "o",
|
||||
"PUBLISHING DOCS",
|
||||
"Publish the docs",
|
||||
args -> args.releaser.publishDocs(args.versionFromScRelease));
|
||||
static Task SNAPSHOTS = task("snapshots", "s",
|
||||
"REVERTING CHANGES & BUMPING VERSION (RELEASE ONLY)",
|
||||
"Go back to snapshots and bump originalVersion by patch",
|
||||
args -> args.releaser.rollbackReleaseVersion(args.project, args.versionFromScRelease));
|
||||
static Task PUSH = task("push", "p",
|
||||
"PUSHING CHANGES",
|
||||
"Push the commits",
|
||||
args -> args.releaser.pushCurrentBranch(args.project));
|
||||
static Task CLOSE_MILESTONE = task("closeMilestone", "m",
|
||||
"CLOSING MILESTONE",
|
||||
"Close the milestone at Github",
|
||||
args -> args.releaser.closeMilestone(args.versionFromScRelease));
|
||||
static Task CREATE_TEMPLATES = task("createTemplates", "t",
|
||||
"CREATING TEMPLATES",
|
||||
"Create email / blog / tweet etc. templates",
|
||||
args -> {
|
||||
args.releaser.createEmail(args.versionFromScRelease);
|
||||
args.releaser.createBlog(args.versionFromScRelease, args.projects);
|
||||
args.releaser.createTweet(args.versionFromScRelease);
|
||||
args.releaser.createReleaseNotes(args.versionFromScRelease, args.projects);
|
||||
});
|
||||
|
||||
static final List<Task> DEFAULT_TASKS = Stream.of(
|
||||
Tasks.UPDATING_POMS,
|
||||
Tasks.BUILD_PROJECT,
|
||||
Tasks.COMMIT,
|
||||
Tasks.DEPLOY,
|
||||
Tasks.PUBLISH_DOCS,
|
||||
Tasks.SNAPSHOTS,
|
||||
Tasks.PUSH,
|
||||
Tasks.CLOSE_MILESTONE,
|
||||
Tasks.CREATE_TEMPLATES
|
||||
).collect(Collectors.toList());
|
||||
|
||||
static Task RELEASE = Tasks.task("release", "r",
|
||||
"FULL RELEASE",
|
||||
"Perform a full release of this project without interruptions",
|
||||
args -> DEFAULT_TASKS.forEach(task -> task.execute(args)));
|
||||
static Task RELEASE_VERBOSE = Tasks.task("release-verbose", "r",
|
||||
"FULL VERBOSE RELEASE",
|
||||
"Perform a full release of this project in interactive mode (you'll be asked about skipping steps)",
|
||||
args -> DEFAULT_TASKS.forEach(task -> task.execute(args)));
|
||||
|
||||
static final List<Task> COMPOSITE_TASKS = Stream.of(
|
||||
RELEASE,
|
||||
RELEASE_VERBOSE
|
||||
).collect(Collectors.toList());
|
||||
|
||||
static final List<Task> ALL_TASKS = Stream.of(
|
||||
COMPOSITE_TASKS, DEFAULT_TASKS
|
||||
).flatMap(List::stream).collect(Collectors.toList());
|
||||
|
||||
static Task task(String name, String shortName, String header, String description, Consumer<Args> function) {
|
||||
return new Task(name, shortName, header, description, function);
|
||||
}
|
||||
|
||||
static List<Task> forNames(List<Task> tasks, List<String> names) {
|
||||
return tasks.stream()
|
||||
.filter(task -> names.contains(task.name) || names.contains(task.shortName))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
static String tasksInOrder() {
|
||||
return DEFAULT_TASKS.stream().map(task -> task.name).collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
@@ -236,11 +236,11 @@ public class AcceptanceTests {
|
||||
private SpringReleaser releaserWithFullDeployment(String expectedVersion,
|
||||
ReleaserProperties properties) throws Exception {
|
||||
Releaser releaser = defaultReleaser(expectedVersion, properties);
|
||||
return new SpringReleaser(releaser, properties) {
|
||||
@Override int chosenOption() {
|
||||
return 0;
|
||||
return new SpringReleaser(releaser, properties, new OptionsProcessor(releaser, properties) {
|
||||
@Override String chosenOption() {
|
||||
return "0";
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private SpringReleaser releaserWithSnapshotScRelease(File projectFile, String branch, String expectedVersion) throws Exception {
|
||||
@@ -251,11 +251,11 @@ public class AcceptanceTests {
|
||||
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;
|
||||
return new SpringReleaser(releaser, properties, new OptionsProcessor(releaser, properties) {
|
||||
@Override String chosenOption() {
|
||||
return "10";
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private Releaser defaultReleaser(String expectedVersion, ReleaserProperties properties) throws Exception {
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Arrays;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.release.internal.Releaser;
|
||||
import org.springframework.cloud.release.internal.ReleaserProperties;
|
||||
import org.springframework.cloud.release.internal.options.Options;
|
||||
import org.springframework.cloud.release.internal.options.OptionsBuilder;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@SuppressWarnings("unchecked")
|
||||
public class OptionsProcessorTests {
|
||||
|
||||
@Mock Releaser releaser;
|
||||
FirstConsumer first = new FirstConsumer();
|
||||
SecondConsumer second = new SecondConsumer();
|
||||
ThirdConsumer third = new ThirdConsumer();
|
||||
Task firstTask = task("first", "1", "", "", first);
|
||||
List<Task> tasks = Arrays.asList(new Task[] {
|
||||
firstTask,
|
||||
task("second", "2", "", "", second),
|
||||
task("third", "3", "", "", third)
|
||||
});
|
||||
|
||||
OptionsProcessor optionsProcessor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_when_an_invalid_option_was_picked() throws Exception {
|
||||
Options options = nonInteractiveOpts().options();
|
||||
|
||||
thenThrownBy(() -> this.optionsProcessor.processOptions(options, args()))
|
||||
.hasMessageContaining("You haven't picked any recognizable option");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_full_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().startFrom("second").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_short_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().startFrom("2").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_full_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().range("second-third").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_short_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().range("2-3").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_full_name_with_same_range() throws Exception {
|
||||
Options options = nonInteractiveOpts().range("second-second").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_short_name_with_same_range() throws Exception {
|
||||
Options options = nonInteractiveOpts().range("2-2").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_multi_using_full_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().taskNames(list("first", "third")).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_multi_using_short_name() throws Exception {
|
||||
Options options = nonInteractiveOpts().taskNames(list("1", "3")).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_only_single_task() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0";
|
||||
}
|
||||
};
|
||||
Options options = interactiveOpts().options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_range_of_tasks() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0-1";
|
||||
}
|
||||
};
|
||||
Options options = interactiveOpts().options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_start_from() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "1-";
|
||||
}
|
||||
};
|
||||
Options options = interactiveOpts().options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isFalse();
|
||||
then(this.second.executed).isTrue();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_multi() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0,2";
|
||||
}
|
||||
};
|
||||
Options options = interactiveOpts().options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_full_release() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override Task releaseTask() {
|
||||
return firstTask;
|
||||
}
|
||||
};
|
||||
Options options = nonInteractiveOpts().fullRelease(true).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_full_verbose_release() throws Exception {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override Task releaseVerboseTask() {
|
||||
return firstTask;
|
||||
}
|
||||
};
|
||||
Options options = interactiveOpts().fullRelease(true).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
|
||||
then(this.first.executed).isTrue();
|
||||
then(this.second.executed).isFalse();
|
||||
then(this.third.executed).isFalse();
|
||||
}
|
||||
|
||||
private OptionsBuilder interactiveOpts() {
|
||||
return new OptionsBuilder();
|
||||
}
|
||||
|
||||
private OptionsBuilder nonInteractiveOpts() {
|
||||
return new OptionsBuilder().interactive(false);
|
||||
}
|
||||
|
||||
private Args args() {
|
||||
return new Args(null, null, null, null, null, null, false);
|
||||
}
|
||||
|
||||
private List<String> list(String... list) {
|
||||
return Arrays.asList(list);
|
||||
}
|
||||
|
||||
static Task task(String name, String shortName, String header, String description, Consumer<Args> function) {
|
||||
return new Task(name, shortName, header, description, function) {
|
||||
@Override String chosenOption() {
|
||||
return "whatever";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FirstConsumer implements Consumer<Args> {
|
||||
|
||||
boolean executed;
|
||||
|
||||
@Override public void accept(Args o) {
|
||||
this.executed = true;
|
||||
}
|
||||
}
|
||||
|
||||
class SecondConsumer implements Consumer<Args> {
|
||||
|
||||
boolean executed;
|
||||
|
||||
@Override public void accept(Args o) {
|
||||
this.executed = true;
|
||||
}
|
||||
}
|
||||
|
||||
class ThirdConsumer implements Consumer<Args> {
|
||||
|
||||
boolean executed;
|
||||
|
||||
@Override public void accept(Args o) {
|
||||
this.executed = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user