require "open3" module PeerCommander # A single command to be executed, with access to results statuses, output, and timings class Command attr_reader :command, :exception, :opts, :env, :stdin_data # The env specified will be passed directly to Open3.capture2e as the env to run with # The opts provided will also be passed directly to Open3.capture2e as opts # stdin_data will also be passed directly to Open3.capture2e def initialize(command, stdin_data: nil, env: {}, opts: {}) @command = command @stdin_data = stdin_data @env = env @opts = opts end # Execute the command, will capture any exceptions that inherit from StandardError raised by the execution of # the given command and store it in the exception attribute, NOTE: This means it _will not_ propogate the # exception upwards # # Returns self def execute raise Errors::CommandAlreadyExecutedError if executed? start_time = Time.now begin @command_output, @status = Open3.capture2e(env, command, execution_options) rescue StandardError => e @exception = e ensure @timing = Time.now - start_time end self end # Return true if the command was successful def success? raise Errors::CommandNotExecutedError unless executed? exception.nil? && status.success? end # Return true if the command failed def failed? !success? end # Return the output (stdout and stderr interleaved) of running the command def output raise Errors::CommandNotExecutedError unless executed? command_output end # How long the command took to run in seconds def duration raise Errors::CommandNotExecutedError unless executed? timing end # Returns a truthy object if the command has been executed, or nil # if it has not. # # If the command has been executed it will either return the status code or # the exception that was raised def executed? exception || status end def exit_code raise Errors::CommandNotExecutedError unless executed? return nil if exception status.exitstatus end private attr_reader :status, :command_output, :timing def execution_options opts.tap do |options| options[:stdin_data] = stdin_data unless stdin_data.nil? end end end end