lib/liquidoc.rb in liquidoc-0.9.5 vs lib/liquidoc.rb in liquidoc-0.10.0

- old
+ new

@@ -8,10 +8,12 @@ require 'logger' require 'csv' require 'crack/xml' require 'fileutils' require 'jekyll' +require 'open3' +require 'highline' # === # Table of Contents # === # @@ -51,10 +53,11 @@ @verbose = false @quiet = false @explicit = false @search_index = false @search_index_dry = '' +@safemode = true # Instantiate the main Logger object, which is always running @logger = Logger.new(STDOUT) @logger.formatter = proc do |severity, datetime, progname, msg| "#{severity}: #{msg}\n" @@ -93,10 +96,24 @@ @logger.error "Problem loading config file #{config_file}. #{ex} Exiting." end raise "ConfigFileError" end cfg = BuildConfig.new(config) # convert the config file to a new object called 'cfg' + if @safemode + commands = "" + cfg.steps.each do |step| + if step['action'] == "execute" + commands = commands + "> " + step['command'] + "\n" + end + end + unless commands.to_s.strip.empty? + puts "\nWARNING: This routine will execute the following shell commands:\n\n#{commands}" + ui = HighLine.new + answer = ui.ask("\nDo you approve? (YES/no): ") + raise "CommandExecutionsNotAuthorized" unless answer.strip == "YES" + end + end iterate_build(cfg) end def iterate_build cfg stepcount = 0 @@ -142,10 +159,13 @@ render_doc(doc, build) # perform the render operation end when "deploy" @logger.warn "Deploy actions are limited and experimental." jekyll_serve(build) + when "execute" + @logger.info "Executing shell command: #{step.command}" + execute_command(step) else @logger.warn "The action `#{type}` is not valid." end end end @@ -211,10 +231,27 @@ "#{msg}\n" end end end +def generate_file content, target + base_path = File.dirname(target) + begin + FileUtils::mkdir_p(base_path) unless File.exists?(base_path) + File.open(target, 'w') { |file| file.write(content) } # saves file + rescue Exception => ex + @logger.error "Failed to save output.\n#{ex.class} #{ex.message}" + raise "FileNotBuilt" + end + if File.exists?(target) + @logger.info "File built: #{target}" + else + @logger.error "Hrmp! File not built." + raise "FileNotBuilt" + end +end + # === # Core classes # === # For now BuildConfig is mostly to objectify the primary build 'action' steps @@ -278,10 +315,14 @@ def options return @step['options'] end + def command + return @step['command'] + end + def stage return @step['stage'] end def builds @@ -343,10 +384,12 @@ reqs = ["data,builds"] when "migrate" reqs = ["source,target"] when "render" reqs = ["builds"] + when "execute" + reqs = ["command"] end for req in reqs if (defined?(@step[req])).nil? @logger.error "Every #{@step['action']}-type in the configuration file needs a '#{req}' declaration." raise "ConfigStructError" @@ -743,24 +786,11 @@ @logger.error message raise message end unless output.downcase == "stdout" output_file = output - base_path = File.dirname(output) - begin - FileUtils::mkdir_p(base_path) unless File.exists?(base_path) - File.open(output_file, 'w') { |file| file.write(rendered) } # saves file - rescue Exception => ex - @logger.error "Failed to save output.\n#{ex.class} #{ex.message}" - raise "FileNotBuilt" - end - if File.exists?(output_file) - @logger.info "File built: #{output_file}" - else - @logger.error "Hrmp! File not built." - raise "FileNotBuilt" - end + generate_file(rendered, output_file) else # if stdout puts "========\nOUTPUT: Rendered with template #{template_file}:\n\n#{rendered}\n" end end @@ -922,13 +952,10 @@ @logger.debug "Final pre-Asciidoctor attributes: #{attrs.to_yaml}" # Perform the aciidoctor convert if build.backend == "pdf" @logger.info "Generating PDF. This can take some time..." end - - - Asciidoctor.convert_file( doc.index, to_file: to_file, attributes: attrs, require: "pdf", @@ -1017,10 +1044,42 @@ end end end # === +# Execute +# === + +def execute_command cmd + stdout, stderr, status = Open3.capture3(cmd.command) + failed = true if status.to_s.include?("exit 1") + unless cmd.options + puts stdout + puts stderr if failed + else + if failed && cmd.options['error'] + @logger.warn cmd.options['error']['message'] if cmd.options['error']['message'] + if cmd.options['error']['response'] == "exit" + @logger.error "Command failure: #{stderr}" + raise "CommandExecutionException" + end + end + if cmd.options['outfile'] + contents = stdout + if cmd.options['outfile'] + contents = "#{cmd.options['outfile']['prepend']}\n#{stdout}" if cmd.options['outfile']['prepend'] + contents = "#{stdout}/n#{cmd.options['outfile']['append']}" if cmd.options['outfile']['append'] + generate_file(contents, cmd.options['outfile']['path']) + end + if cmd.options['stdout'] + puts stdout + end + end + end +end + +# === # Text manipulation Classes, Modules, procs, etc # === module HashMash @@ -1220,9 +1279,13 @@ @passed_vars.merge!pair end opts.on("--parse-config", "Preprocess the designated configuration file as a Liquid template. Superfluous when passing -v/--var arguments.") do @parseconfig = true + end + + opts.on("--unsafe", "Enable shell command executions without interactive check.") do + @safemode = false end opts.on("-h", "--help", "Returns help.") do puts opts exit