Table done; fixes gh-111
This commit is contained in:
@@ -45,7 +45,7 @@ public class ReleaserApplication implements CommandLineRunner {
|
||||
@Autowired SpringReleaser releaser;
|
||||
@Autowired Parser parser;
|
||||
|
||||
@Override public void run(String... strings) throws Exception {
|
||||
@Override public void run(String... strings) {
|
||||
Options options = this.parser.parse(strings);
|
||||
try {
|
||||
this.releaser.release(options);
|
||||
|
||||
@@ -37,11 +37,11 @@ class Task {
|
||||
this.taskType = taskType;
|
||||
}
|
||||
|
||||
void execute(Args args) {
|
||||
TaskAndException execute(Args args) {
|
||||
if (args.taskType != this.taskType) {
|
||||
log.info("Skipping [{}] since task type is [{}] and should be [{}]]",
|
||||
this.name, this.taskType, args.taskType);
|
||||
return;
|
||||
return TaskAndException.skipped(this);
|
||||
}
|
||||
try {
|
||||
boolean interactive = args.interactive;
|
||||
@@ -49,18 +49,28 @@ class Task {
|
||||
if (interactive) {
|
||||
boolean skipStep = stepSkipper.skipStep();
|
||||
if (!skipStep) {
|
||||
this.consumer.accept(args);
|
||||
return runTask(args);
|
||||
}
|
||||
return TaskAndException.skipped(this);
|
||||
} else {
|
||||
this.consumer.accept(args);
|
||||
return runTask(args);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("\n\n\nBUILD FAILED!!!\n\nException occurred for task <" +
|
||||
log.error("\n\n\nBUILD FAILED!!!\n\nException occurred for project <" +
|
||||
(args.project != null ? args.project.getName() : "") + "> task <" +
|
||||
this.name + "> \n\nwith description <" + this.description + ">\n\n", e);
|
||||
throw e;
|
||||
if (this.taskType == TaskType.RELEASE) {
|
||||
throw e;
|
||||
}
|
||||
return TaskAndException.failure(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private TaskAndException runTask(Args args) {
|
||||
this.consumer.accept(args);
|
||||
return TaskAndException.success(this);
|
||||
}
|
||||
|
||||
private void printLog(boolean interactive) {
|
||||
log.info("\n\n\n=== {} ===\n\n{} {}\n\n", this.header, this.description, interactive ? MSG : "");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class TaskAndException {
|
||||
|
||||
final Task task;
|
||||
final TaskState taskState;
|
||||
final Exception exception;
|
||||
|
||||
private TaskAndException(Task task, TaskState taskState) {
|
||||
this.task = task;
|
||||
this.taskState = taskState;
|
||||
this.exception = null;
|
||||
}
|
||||
|
||||
private TaskAndException(Task task, TaskState taskState, Exception exception) {
|
||||
this.task = task;
|
||||
this.taskState = taskState;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
static TaskAndException skipped(Task task) {
|
||||
return new TaskAndException(task, TaskState.SKIPPED);
|
||||
}
|
||||
|
||||
static TaskAndException success(Task task) {
|
||||
return new TaskAndException(task, TaskState.SUCCESS);
|
||||
}
|
||||
|
||||
static TaskAndException failure(Task task, Exception exception) {
|
||||
return new TaskAndException(task, TaskState.FAILURE, exception);
|
||||
}
|
||||
|
||||
enum TaskState {
|
||||
SKIPPED, SUCCESS, FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jakewharton.fliptables.FlipTableConverters;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* All tasks that can be executed by the releaser
|
||||
*
|
||||
@@ -135,23 +142,22 @@ class Tasks {
|
||||
static Task RELEASE = Tasks.task("release", "fr",
|
||||
"FULL RELEASE",
|
||||
"Perform a full release of this project without interruptions",
|
||||
args -> DEFAULT_TASKS_PER_PROJECT.forEach(task -> task.execute(args)));
|
||||
args -> new CompositeConsumer(DEFAULT_TASKS_PER_PROJECT).accept(args));
|
||||
static Task POST_RELEASE = Tasks.task("postRelease", "pr",
|
||||
"POST RELEASE TASKS",
|
||||
"Perform post release tasks for this release without interruptions",
|
||||
args -> DEFAULT_TASKS_PER_RELEASE.forEach(task -> task.execute(args)),
|
||||
args -> new CompositeConsumer(DEFAULT_TASKS_PER_RELEASE).accept(args),
|
||||
TaskType.POST_RELEASE);
|
||||
static Task RELEASE_VERBOSE = Tasks.task("releaseVerbose", "r",
|
||||
"FULL VERBOSE RELEASE",
|
||||
"Perform a full release of this project in interactive mode (you'll be asked about skipping steps)",
|
||||
args -> DEFAULT_TASKS_PER_PROJECT.forEach(task -> task.execute(args)));
|
||||
args -> new CompositeConsumer(DEFAULT_TASKS_PER_PROJECT).accept(args));
|
||||
static Task META_RELEASE = Tasks.task("metaRelease", "x",
|
||||
"META RELEASE",
|
||||
"Perform a meta release of projects",
|
||||
args -> DEFAULT_TASKS_PER_PROJECT.forEach(task -> {
|
||||
args.properties.getMetaRelease().setEnabled(true);
|
||||
task.execute(args);
|
||||
}));
|
||||
args -> new CompositeConsumer(DEFAULT_TASKS_PER_PROJECT,
|
||||
(args1 -> args.properties.getMetaRelease().setEnabled(true)))
|
||||
.accept(args));
|
||||
|
||||
static final List<Task> COMPOSITE_TASKS = Stream.of(
|
||||
RELEASE,
|
||||
@@ -187,4 +193,83 @@ class Tasks {
|
||||
|
||||
enum TaskType {
|
||||
RELEASE, POST_RELEASE
|
||||
}
|
||||
|
||||
class CompositeConsumer implements Consumer<Args> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CompositeConsumer.class);
|
||||
|
||||
private final List<Task> tasks;
|
||||
private final Consumer<Args> setup;
|
||||
|
||||
CompositeConsumer(List<Task> tasks) {
|
||||
this.tasks = tasks;
|
||||
this.setup = args -> {};
|
||||
}
|
||||
|
||||
CompositeConsumer(List<Task> tasks, Consumer<Args> setup) {
|
||||
this.tasks = tasks;
|
||||
this.setup = setup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Args args) {
|
||||
this.setup.accept(args);
|
||||
List<Table> table = this.tasks.stream()
|
||||
.map(task -> new Table(task.execute(args)))
|
||||
.collect(Collectors.toList());
|
||||
String string = "\n\n***** BUILD REPORT *****\n\n"
|
||||
+ FlipTableConverters.fromIterable(table, Table.class)
|
||||
+ "\n\n***** BUILD REPORT *****\n\n";
|
||||
List<Table> brokenTasks = table.stream()
|
||||
.filter(table1 -> StringUtils.hasText(table1.thrownException))
|
||||
.collect(Collectors.toList());
|
||||
if (!brokenTasks.isEmpty()) {
|
||||
String brokenBuilds = "\n\n[BUILD UNSTABLE] One of the tasks is failing!\n\n" +
|
||||
FlipTableConverters.fromIterable(brokenTasks, Table.class) + "\n\n";
|
||||
log.info(string + brokenBuilds);
|
||||
throw new IllegalStateException("[BUILD UNSTABLE] One of the tasks is failing! + \n\n\n" + brokenBuilds);
|
||||
} else {
|
||||
log.info(string);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Table {
|
||||
final String taskCaption;
|
||||
final String taskDescription;
|
||||
final String taskState;
|
||||
final String thrownException;
|
||||
|
||||
Table(TaskAndException tae) {
|
||||
this.taskCaption = tae.task.name;
|
||||
this.taskDescription = tae.task.description;
|
||||
this.taskState = tae.taskState.name().toLowerCase();
|
||||
this.thrownException = tae.exception == null ? "" : Arrays
|
||||
.stream(tae.exception.getStackTrace())
|
||||
.map(s -> {
|
||||
String[] strings = s.toString().split("\\.");
|
||||
return strings[strings.length - 3] + "." + strings[strings.length - 2] + "." + strings[strings.length - 1];
|
||||
})
|
||||
.limit(15)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public String getTaskCaption() {
|
||||
return this.taskCaption;
|
||||
}
|
||||
|
||||
public String getTaskDescription() {
|
||||
return this.taskDescription;
|
||||
}
|
||||
|
||||
public String getTaskState() {
|
||||
return this.taskState;
|
||||
}
|
||||
|
||||
public String getThrownException() {
|
||||
return this.thrownException;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class TestDocumentationUpdater extends DocumentationUpdater {
|
||||
}
|
||||
|
||||
@Override
|
||||
String readIndexHtmlContents(File indexHtml) throws IOException {
|
||||
String readIndexHtmlContents(File indexHtml) {
|
||||
return response();
|
||||
}
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ public class AcceptanceTests {
|
||||
return this.testPomReader.readPom(new File(dir, "pom.xml"));
|
||||
}
|
||||
|
||||
private File emailTemplate() throws URISyntaxException {
|
||||
private File emailTemplate() {
|
||||
return new File("target/email.txt");
|
||||
}
|
||||
|
||||
@@ -534,15 +534,15 @@ public class AcceptanceTests {
|
||||
return new String(Files.readAllBytes(emailTemplate().toPath()));
|
||||
}
|
||||
|
||||
private File blogTemplate() throws URISyntaxException {
|
||||
private File blogTemplate() {
|
||||
return new File("target/blog.md");
|
||||
}
|
||||
|
||||
private File tweetTemplate() throws URISyntaxException {
|
||||
private File tweetTemplate() {
|
||||
return new File("target/tweet.txt");
|
||||
}
|
||||
|
||||
private File releaseNotesTemplate() throws URISyntaxException {
|
||||
private File releaseNotesTemplate() {
|
||||
return new File("target/notes.md");
|
||||
}
|
||||
|
||||
@@ -620,7 +620,7 @@ public class AcceptanceTests {
|
||||
}
|
||||
|
||||
private Releaser defaultReleaser(String expectedVersion, String projectName,
|
||||
ReleaserProperties properties) throws Exception {
|
||||
ReleaserProperties properties) {
|
||||
ProjectPomUpdater pomUpdater = new ProjectPomUpdater(properties);
|
||||
ProjectBuilder projectBuilder = new ProjectBuilder(properties);
|
||||
TestProjectGitHandler handler = new TestProjectGitHandler(properties,
|
||||
@@ -644,7 +644,7 @@ public class AcceptanceTests {
|
||||
return releaser;
|
||||
}
|
||||
|
||||
private Releaser defaultMetaReleaser(ReleaserProperties properties) throws Exception {
|
||||
private Releaser defaultMetaReleaser(ReleaserProperties properties) {
|
||||
ProjectPomUpdater pomUpdater = new ProjectPomUpdater(properties);
|
||||
ProjectBuilder projectBuilder = new ProjectBuilder(properties);
|
||||
NonAssertingTestProjectGitHandler handler = new NonAssertingTestProjectGitHandler(properties);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.release.internal.spring;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class CompositeConsumerTests {
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_for_a_release_task() {
|
||||
CompositeConsumer compositeConsumer = new CompositeConsumer(Arrays.asList(
|
||||
new Task("foo", "foo", "foo", "foo",
|
||||
(args -> {})),
|
||||
new Task("bar", "bar", "bar", "bar",
|
||||
(args -> { throw new MyException(); }))
|
||||
));
|
||||
|
||||
BDDAssertions.thenThrownBy(() ->
|
||||
compositeConsumer.accept(new Args(TaskType.RELEASE)))
|
||||
.isInstanceOf(MyException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_for_a_post_release_task_after_creating_a_report() {
|
||||
CompositeConsumer compositeConsumer = new CompositeConsumer(Arrays.asList(
|
||||
new Task("foo", "foo", "foo", "foo",
|
||||
(args -> {}), TaskType.POST_RELEASE),
|
||||
new Task("bar", "bar", "bar", "bar",
|
||||
(args -> { throw new MyException(); }), TaskType.POST_RELEASE)
|
||||
));
|
||||
|
||||
BDDAssertions.thenThrownBy(() ->
|
||||
compositeConsumer.accept(new Args(TaskType.POST_RELEASE)))
|
||||
.isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
|
||||
class MyException extends RuntimeException {}
|
||||
@@ -50,7 +50,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_when_an_invalid_option_was_picked() throws Exception {
|
||||
public void should_throw_exception_when_an_invalid_option_was_picked() {
|
||||
Options options = nonInteractiveOpts().options();
|
||||
|
||||
thenThrownBy(() -> this.optionsProcessor.processOptions(options, args()))
|
||||
@@ -58,7 +58,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_full_name() throws Exception {
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_full_name() {
|
||||
Options options = nonInteractiveOpts().startFrom("second").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -70,7 +70,7 @@ public class OptionsProcessorTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_short_name() throws Exception {
|
||||
public void should_execute_only_tasks_after_the_provided_one_using_short_name() {
|
||||
Options options = nonInteractiveOpts().startFrom("2").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -81,7 +81,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_full_name() throws Exception {
|
||||
public void should_execute_only_tasks_from_range_using_full_name() {
|
||||
Options options = nonInteractiveOpts().range("second-third").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -92,7 +92,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_short_name() throws Exception {
|
||||
public void should_execute_only_tasks_from_range_using_short_name() {
|
||||
Options options = nonInteractiveOpts().range("2-3").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -103,7 +103,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_full_name_with_same_range() throws Exception {
|
||||
public void should_execute_only_tasks_from_range_using_full_name_with_same_range() {
|
||||
Options options = nonInteractiveOpts().range("second-second").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -114,7 +114,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_range_using_short_name_with_same_range() throws Exception {
|
||||
public void should_execute_only_tasks_from_range_using_short_name_with_same_range() {
|
||||
Options options = nonInteractiveOpts().range("2-2").options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -125,7 +125,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_multi_using_full_name() throws Exception {
|
||||
public void should_execute_only_tasks_from_multi_using_full_name() {
|
||||
Options options = nonInteractiveOpts().taskNames(list("first", "third")).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -136,7 +136,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_only_tasks_from_multi_using_short_name() throws Exception {
|
||||
public void should_execute_only_tasks_from_multi_using_short_name() {
|
||||
Options options = nonInteractiveOpts().taskNames(list("1", "3")).options();
|
||||
|
||||
this.optionsProcessor.processOptions(options, args());
|
||||
@@ -147,7 +147,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_only_single_task() throws Exception {
|
||||
public void should_execute_interactively_only_single_task() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0";
|
||||
@@ -163,7 +163,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_range_of_tasks() throws Exception {
|
||||
public void should_execute_interactively_range_of_tasks() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0-1";
|
||||
@@ -179,7 +179,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_start_from() throws Exception {
|
||||
public void should_execute_interactively_start_from() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "1-";
|
||||
@@ -195,7 +195,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_interactively_multi() throws Exception {
|
||||
public void should_execute_interactively_multi() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override String chosenOption() {
|
||||
return "0,2";
|
||||
@@ -211,7 +211,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_full_release() throws Exception {
|
||||
public void should_execute_full_release() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override Task releaseTask() {
|
||||
return OptionsProcessorTests.this.firstTask;
|
||||
@@ -231,7 +231,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_execute_full_verbose_release() throws Exception {
|
||||
public void should_execute_full_verbose_release() {
|
||||
this.optionsProcessor = new OptionsProcessor(this.releaser, new ReleaserProperties(), this.tasks) {
|
||||
@Override Task releaseVerboseTask() {
|
||||
return OptionsProcessorTests.this.firstTask;
|
||||
@@ -251,7 +251,7 @@ public class OptionsProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_remove_single_quotes() throws Exception {
|
||||
public void should_remove_single_quotes() {
|
||||
Options options = interactiveOpts().fullRelease(true)
|
||||
.range("'1-2'")
|
||||
.startFrom("'c'")
|
||||
|
||||
@@ -43,7 +43,7 @@ public class TaskTests {
|
||||
then(someBool.get()).isTrue();
|
||||
then(this.capture.toString())
|
||||
.contains("BUILD FAILED!!!")
|
||||
.contains("Exception occurred for task <foo>")
|
||||
.contains("Exception occurred for project <> task <foo>")
|
||||
.contains("with description <descr>");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user