From d876c3377074a49fd85d0d6a784c65abc3102e6d Mon Sep 17 00:00:00 2001 From: Pasi Eronen Date: Fri, 2 Sep 2022 21:10:25 +0300 Subject: [PATCH 1/3] Add --failure-level command line option to force non-zero exit code from CLI if specified logging level is reached --- CHANGELOG.adoc | 1 + .../jruby/cli/AsciidoctorCliOptions.java | 6 ++++++ .../jruby/cli/AsciidoctorInvoker.java | 14 +++++++++++--- .../asciidoctor/jruby/cli/SeverityConverter.java | 12 ++++++++++++ .../jruby/internal/JRubyAsciidoctor.java | 10 ++++++++++ .../cli/WhenAsciidoctorIsCalledUsingCli.java | 16 ++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java 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..ec5fc462 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); @@ -72,12 +72,17 @@ public void invoke(String... parameters) { convertInput(asciidoctor, options, inputFiles); + if (asciidoctorCliOptions.getFailureLevel().compareTo(asciidoctor.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 +201,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/SeverityConverter.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java new file mode 100644 index 00000000..b0fc987f --- /dev/null +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java @@ -0,0 +1,12 @@ +package org.asciidoctor.jruby.cli; + +import com.beust.jcommander.IStringConverter; +import org.asciidoctor.SafeMode; +import org.asciidoctor.log.Severity; + +public class SeverityConverter implements IStringConverter { + + @Override + public Severity convert(String argument) { return Severity.valueOf(argument.toUpperCase()); } + +} diff --git a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java index ff85e1e2..4d561d17 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java @@ -22,6 +22,7 @@ import org.asciidoctor.jruby.syntaxhighlighter.internal.SyntaxHighlighterRegistryExecutor; import org.asciidoctor.log.LogHandler; import org.asciidoctor.log.LogRecord; +import org.asciidoctor.log.Severity; import org.asciidoctor.syntaxhighlighter.SyntaxHighlighterRegistry; import org.jruby.*; import org.jruby.exceptions.RaiseException; @@ -46,6 +47,8 @@ public class JRubyAsciidoctor implements AsciidoctorJRuby, LogHandler { private List logHandlers = new ArrayList<>(); + private Severity maxSeverity = Severity.DEBUG; + public JRubyAsciidoctor() { this(createRubyRuntime(Collections.singletonMap(GEM_PATH, null), new ArrayList<>(), null)); processRegistrations(this); @@ -507,8 +510,15 @@ private RubyClass getExtensionGroupClass() { @Override public void log(LogRecord logRecord) { + if (this.maxSeverity.compareTo(logRecord.getSeverity()) < 0) { + this.maxSeverity = logRecord.getSeverity(); + } for (LogHandler logHandler : logHandlers) { logHandler.log(logRecord); } } + + public Severity getMaxSeverity() { + return this.maxSeverity; + } } 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() { From dac99a5060d98a5b35103167563d99fc13cdf531 Mon Sep 17 00:00:00 2001 From: Pasi Eronen Date: Fri, 9 Sep 2022 09:34:11 +0300 Subject: [PATCH 2/3] Improve error messages when parsing --failure-level parameter --- .../asciidoctor/jruby/cli/SeverityConverter.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 index b0fc987f..ef53e0a1 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/SeverityConverter.java @@ -1,12 +1,17 @@ package org.asciidoctor.jruby.cli; -import com.beust.jcommander.IStringConverter; -import org.asciidoctor.SafeMode; +import com.beust.jcommander.converters.EnumConverter; import org.asciidoctor.log.Severity; -public class SeverityConverter implements IStringConverter { +public class SeverityConverter extends EnumConverter { + + public SeverityConverter() { + super("--failure-level", Severity.class); + } @Override - public Severity convert(String argument) { return Severity.valueOf(argument.toUpperCase()); } + public Severity convert(String argument) { + return super.convert(argument.toUpperCase()); + } } From beb9f6b4d21c6a868b50e56fd22eec3b370d8554 Mon Sep 17 00:00:00 2001 From: Pasi Eronen Date: Fri, 9 Sep 2022 09:44:44 +0300 Subject: [PATCH 3/3] Implement maxSeverity tracking on log handler --- .../jruby/cli/AsciidoctorInvoker.java | 5 ++++- .../jruby/cli/MaxSeverityLogHandler.java | 20 +++++++++++++++++++ .../jruby/internal/JRubyAsciidoctor.java | 10 ---------- 3 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 asciidoctorj-core/src/main/java/org/asciidoctor/jruby/cli/MaxSeverityLogHandler.java 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 ec5fc462..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 @@ -58,6 +58,9 @@ public int invoke(String... parameters) { + "' missing or cannot be read"); } + MaxSeverityLogHandler maxSeverityLogHandler = new MaxSeverityLogHandler(); + asciidoctor.registerLogHandler(maxSeverityLogHandler); + Options options = asciidoctorCliOptions.parse(); if (asciidoctorCliOptions.isRequire()) { @@ -72,7 +75,7 @@ public int invoke(String... parameters) { convertInput(asciidoctor, options, inputFiles); - if (asciidoctorCliOptions.getFailureLevel().compareTo(asciidoctor.getMaxSeverity()) <= 0) { + if (asciidoctorCliOptions.getFailureLevel().compareTo(maxSeverityLogHandler.getMaxSeverity()) <= 0) { return 1; } 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/internal/JRubyAsciidoctor.java b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java index 4d561d17..ff85e1e2 100644 --- a/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java +++ b/asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/JRubyAsciidoctor.java @@ -22,7 +22,6 @@ import org.asciidoctor.jruby.syntaxhighlighter.internal.SyntaxHighlighterRegistryExecutor; import org.asciidoctor.log.LogHandler; import org.asciidoctor.log.LogRecord; -import org.asciidoctor.log.Severity; import org.asciidoctor.syntaxhighlighter.SyntaxHighlighterRegistry; import org.jruby.*; import org.jruby.exceptions.RaiseException; @@ -47,8 +46,6 @@ public class JRubyAsciidoctor implements AsciidoctorJRuby, LogHandler { private List logHandlers = new ArrayList<>(); - private Severity maxSeverity = Severity.DEBUG; - public JRubyAsciidoctor() { this(createRubyRuntime(Collections.singletonMap(GEM_PATH, null), new ArrayList<>(), null)); processRegistrations(this); @@ -510,15 +507,8 @@ private RubyClass getExtensionGroupClass() { @Override public void log(LogRecord logRecord) { - if (this.maxSeverity.compareTo(logRecord.getSeverity()) < 0) { - this.maxSeverity = logRecord.getSeverity(); - } for (LogHandler logHandler : logHandlers) { logHandler.log(logRecord); } } - - public Severity getMaxSeverity() { - return this.maxSeverity; - } }