class OctocatalogDiff::Util::ScriptRunner
This is a utility class to execute a built-in script.
Attributes
Public Class Methods
Create the object - the object is a configured script, which can be executed multiple times with different environment varibles.
@param opts [Hash] Options hash
opts[:default_script] (Required) Path to script, relative to `scripts` directory opts[:logger] (Optional) Logger object opts[:override_script_path] (Optional) Directory where a similarly-named script MAY exist
# File lib/octocatalog-diff/util/scriptrunner.rb, line 27 def initialize(opts = {}) @logger = opts[:logger] @script_src = find_script(opts.fetch(:default_script), opts[:override_script_path]) @script = temp_script(@script_src) @stdout = nil @stderr = nil @exitcode = nil end
Public Instance Methods
All output from the latest execution of the command. @return [String] Combined output of STDOUT and STDERR
# File lib/octocatalog-diff/util/scriptrunner.rb, line 70 def output return if @exitcode.nil? [ 'STDOUT:', @stdout.split(/\n/).map { |line| " #{line}" }, 'STDERR:', @stderr.split(/\n/).map { |line| " #{line}" } ].flatten.compact.join("\n") end
Execute the script from a given working directory, with additional environment variables specified in the options hash.
@param opts [Hash] Options hash
opts[:working_dir] (Required) Directory where script is to be executed opts[:argv] (Optional Array) Command line arguments opts[:pass_env_vars] (Optional Array) Environment variables to pass (default: HOME, PATH) opts[<STRING>] (Optional) Environment variable
# File lib/octocatalog-diff/util/scriptrunner.rb, line 44 def run(opts = {}) working_dir = opts.fetch(:working_dir) assert_directory_exists(working_dir) argv = opts.fetch(:argv, []) logger = opts[:logger] || @logger pass_env_vars = [opts[:pass_env_vars], 'HOME', 'PATH'].flatten.compact env = opts.select { |k, _v| k.is_a?(String) } pass_env_vars.each { |var| env[var] ||= ENV[var] } env['PWD'] = working_dir cmdline = [script, argv].flatten.compact.map { |x| Shellwords.escape(x) }.join(' ') log(:debug, "Execute: #{cmdline}", opts[:logger]) @stdout, @stderr, status = Open3.capture3(env, cmdline, unsetenv_others: true, chdir: working_dir) @exitcode = status.exitstatus @stderr.split(/\n/).select { |line| line =~ /\S/ }.each { |line| log(:debug, "STDERR: #{line}", logger) } log(:debug, "Exit status: #{@exitcode}", logger) return @stdout if @exitcode.zero? raise ScriptException, output end
Private Instance Methods
PRIVATE: Assert that a directory exists (and is a directory). Raise error if not.
@param dir [String] Directory to test
# File lib/octocatalog-diff/util/scriptrunner.rb, line 136 def assert_directory_exists(dir) return if File.directory?(dir) raise Errno::ENOENT, "Invalid directory '#{dir}'" end
PRIVATE: Determine the path to the script to execute, taking into account the default script location and the optional override script path.
@param default_script [String] Path to script, relative to `scripts` directory @param override_script_path [String] Optional directory with override script @return [String] Full path to script
# File lib/octocatalog-diff/util/scriptrunner.rb, line 109 def find_script(default_script, override_script_path = nil) script = find_script_from_override_path(default_script, override_script_path) || File.expand_path("../../../scripts/#{default_script}", File.dirname(__FILE__)) raise Errno::ENOENT, "Unable to locate script '#{script}'" unless File.file?(script) script end
PRIVATE: Find script from override path.
@param default_script [String] Path to script, relative to `scripts` directory @param override_script_path [String] Optional directory @return [String] Override script if found, else nil
# File lib/octocatalog-diff/util/scriptrunner.rb, line 121 def find_script_from_override_path(default_script, override_script_path = nil) return unless override_script_path script_test = File.join(override_script_path, File.basename(default_script)) if File.file?(script_test) log(:debug, "Selecting #{script_test} from override script path") script_test else log(:debug, "Did not find #{script_test} in override script path") nil end end
PRIVATE: Log a message, if logger is defined. Since this might be called under `parallel` it's possible that the logger isn't defined, and if so the logged message is skipped.
# File lib/octocatalog-diff/util/scriptrunner.rb, line 84 def log(priority, message, logger = @logger) return unless logger logger.send(priority, [message]) end
PRIVATE: Create a temporary file with the contents of the script and mark the script executable. This is to avoid changing ownership or permissions on any user-supplied file.
@param script [String] Path to script @return [String] Path to tempfile containing script
# File lib/octocatalog-diff/util/scriptrunner.rb, line 94 def temp_script(script) raise Errno::ENOENT, "Script '#{script}' not found" unless File.file?(script) temp_dir = OctocatalogDiff::Util::Util.temp_dir('ocd-scriptrunner') temp_file = File.join(temp_dir, File.basename(script)) File.open(temp_file, 'w') { |f| f.write(File.read(script)) } FileUtils.chmod 0o755, temp_file temp_file end