require 'jekyll_plugin_support' require 'rack/utils' require_relative 'jekyll_pre/version' module JekyllPreModule def self.compress(response, no_strip) result = response.chomp result = result.strip unless no_strip result = result.gsub('\n\n', '
\n') result = Rack::Utils.escape_html(result) unless @no_escape result end class ExecTag < JekyllSupport::JekyllTag include JekyllPreVersion def self.remove_html_tags(string) string.gsub(/<[^>]*>/, '') end def render_impl parse_args @original_command = @helper.remaining_markup_original command = JekyllPluginHelper.expand_env @original_command if command.strip.empty? msg = "Command is empty on on line #{@line_number} (after front matter) of #{@page['path']}" unless @die_if_error @logger.warn { msg } return '' end raise PreError, msg, [] end response = run_command(command) response = if @child_status.success? JekyllPreModule.compress(response, @no_strip) else handle_error(command) end <<~END_OUTPUT #{Rack::Utils.escape_html(@original_command)} #{response} END_OUTPUT rescue PreError => e raise PreError, e.message, [] rescue StandardError => e msg = self.class.remove_html_tags(e.message) + " from executing '#{@original_command}' on line #{@line_number} (after front matter) of #{@page['path']}" raise PreError, msg.red, [] if @die_if_error end private def die(msg) msg_no_html = self.class.remove_html_tags(msg) @logger.error("#{@page['path']} - #{msg_no_html}") raise PreError, "#{@page['path']} - #{msg_no_html.red}", [] end def handle_error(command) msg0 = "Error: executing '#{command}'" msg0 += " (expanded from #{@original_command})" if command != @original_command msg0 += " in directory '#{@cd}'" if @cd msg = <<~END_MSG #{msg0} on line #{@line_number} (after front matter) of #{@page['path']} returned error code #{@child_status.exitstatus} END_MSG raise PreError, msg.red, [] if @die_if_nonzero @logger.error { msg } "Error code #{@child_status.exitstatus}" end def parse_args @cd = @helper.parameter_specified? 'cd' @cd = JekyllPluginHelper.expand_env(@cd) if @cd @no_escape = @helper.parameter_specified? 'no_escape' @no_strip = @helper.parameter_specified? 'no_strip' @no_stderr = @helper.parameter_specified? 'no_stderr' @die_if_nonzero = @helper.parameter_specified?('die_if_nonzero') # Implies die_if_error @die_if_error = @helper.parameter_specified?('die_if_error') | @die_if_nonzero end # References @cd # Defines @child_status # Ignores stderr output # @return result of running command # @param command [String] Shell command to execute def run_command(command) stdout_str = '' stderr_str = '' if @cd Dir.chdir(@cd) do @logger.debug { "Executing '#{command}' from '#{@cd}'" } stdout_str, stderr_str, @child_status = Open3.capture3 command end else @logger.debug { "Executing '#{command}'" } stdout_str, stderr_str, @child_status = Open3.capture3 command end unless @no_stderr stderr_str.strip! unless stderr_str.empty? @logger.info do "'#{command}' STDERR=#{stderr_str}\nThe exec subcommand's 'no_stderr' option suppresses this message." end end end stdout_str rescue StandardError => e msg = self.class.remove_html_tags(e.message) + " from executing '#{@original_command}' on line #{@line_number} (after front matter) of #{@page['path']}" raise PreError, msg.red, [] if @die_if_error ensure @child_status = $CHILD_STATUS stdout_str end JekyllPluginHelper.register(self, 'exec') end end