diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 9009cf9d..bdf1e633 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -17,6 +17,7 @@ Improvement:: * BREAKING: Fix Macro APIs to take StructuralNodes and return Phrase- or StructuralNodes. (#1084) * BREAKING: Allow Preprocessor extensions to create new Readers and replace the original Reader. (#1081) +* Add command line option --failure-level to force non-zero exit code from AsciidoctorJ CLI if specified logging level is reached. (#1114) * Upgrade to asciidoctorj-pdf 2.3.0 (#1109) * Upgrade to JRuby 9.3.7.0 (#1109) * Upgrade to tilt 2.0.11 (#1109) diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorCliOptions.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorCliOptions.java index 8b694278..724a6539 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorCliOptions.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorCliOptions.java @@ -2,6 +2,7 @@ import com.beust.jcommander.Parameter; import org.asciidoctor.*; +import org.asciidoctor.log.Severity; import java.io.File; import java.util.*; @@ -95,6 +96,9 @@ public class AsciidoctorCliOptions { @Parameter(names = {QUIET, "--quiet"}, description = "suppress warnings (default: false)") private boolean quiet = false; + @Parameter(names = {"--failure-level"}, converter = SeverityConverter.class, description = "set minimum log level that yields a non-zero exit code: [info, warning, error, fatal] (default: fatal) ") + private Severity failureLevel = Severity.FATAL; + @Parameter(names = {REQUIRE, "--require"}, description = "require the specified library before executing the processor (using require)") private List require; @@ -111,6 +115,8 @@ public boolean isQuiet() { return quiet; } + public Severity getFailureLevel() { return failureLevel; } + public boolean isRequire() { return this.require != null && this.require.size() > 0; } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorInvoker.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorInvoker.java index 86adfd09..5e834a9b 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorInvoker.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/AsciidoctorInvoker.java @@ -26,7 +26,7 @@ public class AsciidoctorInvoker { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); - public void invoke(String... parameters) { + public int invoke(String... parameters) { AsciidoctorCliOptions asciidoctorCliOptions = new AsciidoctorCliOptions(); JCommander jCommander = new JCommander(asciidoctorCliOptions); @@ -43,7 +43,7 @@ public void invoke(String... parameters) { System.out.println("AsciidoctorJ " + getAsciidoctorJVersion() + " (Asciidoctor " + asciidoctor.asciidoctorVersion() + ") [https://asciidoctor.org]"); Object rubyVersionString = JRubyRuntimeContext.get(asciidoctor).evalScriptlet("\"#{JRUBY_VERSION} (#{RUBY_VERSION})\""); System.out.println("Runtime Environment: jruby " + rubyVersionString); - return; + return 0; } List inputFiles = getInputFiles(asciidoctorCliOptions); @@ -58,6 +58,9 @@ public void invoke(String... parameters) { + "' missing or cannot be read"); } + MaxSeverityLogHandler maxSeverityLogHandler = new MaxSeverityLogHandler(); + asciidoctor.registerLogHandler(maxSeverityLogHandler); + Options options = asciidoctorCliOptions.parse(); if (asciidoctorCliOptions.isRequire()) { @@ -72,12 +75,17 @@ public void invoke(String... parameters) { convertInput(asciidoctor, options, inputFiles); + if (asciidoctorCliOptions.getFailureLevel().compareTo(maxSeverityLogHandler.getMaxSeverity()) <= 0) { + return 1; + } + if (asciidoctorCliOptions.isTimings()) { Map optionsMap = options.map(); IRubyObject timings = (IRubyObject) optionsMap.get(TIMINGS_OPTION_NAME); timings.callMethod(JRubyRuntimeContext.get(asciidoctor).getCurrentContext(), "print_report"); } } + return 0; } private String getAsciidoctorJVersion() { @@ -196,7 +204,10 @@ public static void main(String args[]) { // Process .jrubyrc file Main.processDotfile(); - new AsciidoctorInvoker().invoke(args); + int status = new AsciidoctorInvoker().invoke(args); + if (status != 0) { + System.exit(status); + } } } diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/MaxSeverityLogHandler.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/MaxSeverityLogHandler.java new file mode 100644 index 00000000..5cfdd504 --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/MaxSeverityLogHandler.java @@ -0,0 +1,20 @@ +package org.asciidoctor.jruby.cli; + +import org.asciidoctor.log.LogHandler; +import org.asciidoctor.log.LogRecord; +import org.asciidoctor.log.Severity; + +public class MaxSeverityLogHandler implements LogHandler { + private Severity maxSeverity = Severity.DEBUG; + + @Override + public void log(LogRecord logRecord) { + if (this.maxSeverity.compareTo(logRecord.getSeverity()) < 0) { + this.maxSeverity = logRecord.getSeverity(); + } + } + + public Severity getMaxSeverity() { + return this.maxSeverity; + } +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java new file mode 100644 index 00000000..ef53e0a1 --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java @@ -0,0 +1,17 @@ +package org.asciidoctor.jruby.cli; + +import com.beust.jcommander.converters.EnumConverter; +import org.asciidoctor.log.Severity; + +public class SeverityConverter extends EnumConverter { + + public SeverityConverter() { + super("--failure-level", Severity.class); + } + + @Override + public Severity convert(String argument) { + return super.convert(argument.toUpperCase()); + } + +} diff --git a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/cli/WhenAsciidoctorIsCalledUsingCli.java b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/cli/WhenAsciidoctorIsCalledUsingCli.java index f3f9d3d0..464aab34 100644 --- a/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/cli/WhenAsciidoctorIsCalledUsingCli.java +++ b/asciidoctorj-core/src/test/java/org/asciidoctor/jruby/cli/WhenAsciidoctorIsCalledUsingCli.java @@ -249,6 +249,22 @@ public void quiet_option_should_not_write_to_console() { } + @Test + public void should_exit_with_zero_status_even_if_errors_were_logged() { + File inputFile = classpath.getResource("brokeninclude.asciidoc"); + String inputPath = inputFile.getPath().substring(pwd.length() + 1); + int status = new AsciidoctorInvoker().invoke(inputPath); + assertThat(status, is(0)); + } + + @Test + public void should_exit_with_nonzero_status_if_logged_severity_was_at_least_failure_level() { + File inputFile = classpath.getResource("brokeninclude.asciidoc"); + String inputPath = inputFile.getPath().substring(pwd.length() + 1); + int status = new AsciidoctorInvoker().invoke("--failure-level", "warn", inputPath); + assertThat(status, is(1)); + } + @Test public void with_absolute_path_file_should_be_rendered() {