@@ -162,4 +162,59 @@ def windows_platform?
162162 win_platform_regex = /mingw|mswin/
163163 RUBY_PLATFORM =~ win_platform_regex || RUBY_DESCRIPTION =~ win_platform_regex
164164 end
165+
166+ require 'delegate'
167+
168+ # A wrapper around a ProcessExecuter::Status that also includes command output
169+ # @api public
170+ class CommandResult < SimpleDelegator
171+ # Create a new CommandResult
172+ # @example
173+ # status = ProcessExecuter.spawn(*command, timeout:, out:, err:)
174+ # CommandResult.new(status, out_buffer.string, err_buffer.string)
175+ # @param status [ProcessExecuter::Status] The status of the process
176+ # @param out [String] The standard output of the process
177+ # @param err [String] The standard error of the process
178+ def initialize ( status , out , err )
179+ super ( status )
180+ @out = out
181+ @err = err
182+ end
183+
184+ # @return [String] The stdout output of the process
185+ attr_reader :out
186+
187+ # @return [String] The stderr output of the process
188+ attr_reader :err
189+ end
190+
191+ # Run a command and return the status including stdout and stderr output
192+ #
193+ # @example
194+ # command = %w[git status]
195+ # status = run(command)
196+ # status.success? # => true
197+ # status.exitstatus # => 0
198+ # status.out # => "On branch master\nnothing to commit, working tree clean\n"
199+ # status.err # => ""
200+ #
201+ # @param command [Array<String>] The command to run
202+ # @param timeout [Numeric, nil] Seconds to allow command to run before killing it or nil for no timeout
203+ # @param raise_errors [Boolean] Raise an exception if the command fails
204+ # @param error_message [String] The message to use when raising an exception
205+ #
206+ # @return [CommandResult] The result of running
207+ #
208+ def run_command ( *command , timeout : nil , raise_errors : true , error_message : "#{ command [ 0 ] } failed" )
209+ out_buffer = StringIO . new
210+ out = ProcessExecuter ::MonitoredPipe . new ( out_buffer )
211+ err_buffer = StringIO . new
212+ err = ProcessExecuter ::MonitoredPipe . new ( err_buffer )
213+
214+ status = ProcessExecuter . spawn ( *command , timeout : timeout , out : out , err : err )
215+
216+ raise "#{ error_message } : #{ err_buffer . string } " if raise_errors && !status . success?
217+
218+ CommandResult . new ( status , out_buffer . string , err_buffer . string )
219+ end
165220end
0 commit comments