Module: Cinnabar::Command
- Defined in:
- lib/cinnabar/cmd_runner.rb,
lib/cinnabar/cmd_runner.rb,
lib/cinnabar/cmd_runner.rb
Overview
typed: false frozen_string_literal: true
Defined Under Namespace
Modules: ArrExt, ArrMixin, ArrRefin, TaskArrExt, TaskArrMixin, TaskArrRefin
Class Method Summary collapse
-
.async_run(cmd_arr, env_hash = nil, opts: {}) ⇒ Array(IO, Process::Waiter)
Launch a command asynchronously (non-blocking) and return its stdout stream and process waiter.
-
.normalize_env(hash) ⇒ Hash{String => String}?
A hash where both keys and values are strings.
-
.run(cmd_arr, env_hash = nil, opts: {}) ⇒ String?
Executes the command synchronously (blocking) and returns its standard output.
-
.run_cmd(cmd_arr, env_hash = nil, opts: {}) ⇒ Boolean
Executes a system command using Ruby's
Kernel.system. -
.wait_with_output(io_fd, waiter) ⇒ Array(String, Process::Status)
Waits for a process to finish and reads all remaining output from its stdout.
Class Method Details
.async_run(cmd_arr, env_hash = nil, opts: {}) ⇒ Array(IO, Process::Waiter)
Launch a command asynchronously (non-blocking) and return its stdout stream and process waiter.
This is a sugar over Open3.popen2, intended to start a subprocess and immediately hand back:
- an
IOfor reading the command's stdout, and - a
Process::Waiter(a thread-like object) that can be awaited later.
- If
:stdin_datais provided, the data will be written to the child's stdin and the stdin will be closed. - When
:stdin_datais absent, stdin is simply closed and the method returns immediately without blocking on output.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/cinnabar/cmd_runner.rb', line 206 def async_run(cmd_arr, env_hash = nil, opts: {}) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity "Asynchronously executing system command: #{cmd_arr}".log_dbg "opts: #{opts}".log_dbg stdin_data = opts.delete(:stdin_data) binmode = opts.delete(:binmode) stdin_binmode = opts.delete(:stdin_binmode) stdout_binmode = opts.delete(:stdout_binmode) 'async_run() does not support the :allow_failure option.'.log_warn if opts.delete(:allow_failure) final_env = normalize_env(env_hash) stdin, stdout, waiter = if final_env.nil? Open3.popen2(*cmd_arr, opts) else Open3.popen2(final_env, *cmd_arr, opts) end if binmode stdin.binmode stdout.binmode else stdin.binmode if stdin_binmode stdout.binmode if stdout_binmode end # Non-blocking: no stdin to write; return immediately unless stdin_data stdin.close return [stdout, waiter] end begin if stdin_data.respond_to? :readpartial IO.copy_stream(stdin_data, stdin) else stdin.write stdin_data end rescue Errno::EPIPE => e e.log_err rescue StandardError => e "Failed to write stdin data: #{e}".log_err Kernel.raise e ensure stdin.close end [stdout, waiter] end |
.normalize_env(hash) ⇒ Hash{String => String}?
Returns a hash where both keys and values are strings.
129 130 131 132 133 134 135 |
# File 'lib/cinnabar/cmd_runner.rb', line 129 def normalize_env(hash) return nil if hash.nil? return nil if hash.respond_to?(:empty?) && hash.empty? hash.to_h { |k, v| [k.to_s, v.to_s] } .tap { "normalized_env:#{_1}".log_dbg } end |
.run(cmd_arr, env_hash = nil, opts: {}) ⇒ String?
Executes the command synchronously (blocking) and returns its standard output.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/cinnabar/cmd_runner.rb', line 42 def run(cmd_arr, env_hash = nil, opts: {}) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity 'Running and capturing the output of a system command.'.log_dbg cmd_arr.log_info "opts: #{opts}".log_dbg allow_failure = opts.delete(:allow_failure) || false final_env = normalize_env(env_hash) begin stdout, status = if final_env.nil? Open3.capture2(*cmd_arr, opts) else Open3.capture2(final_env, *cmd_arr, opts) end rescue StandardError => e Kernel.raise e unless allow_failure e.log_err return stdout end return stdout if status.success? err_msg = "Command failed: #{cmd_arr.join(' ')}" Kernel.raise err_msg unless allow_failure err_msg.log_err stdout end |
.run_cmd(cmd_arr, env_hash = nil, opts: {}) ⇒ Boolean
Executes a system command using Ruby's Kernel.system.
It runs the command synchronously, blocks until completion, and does not capture stdout or stderr.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/cinnabar/cmd_runner.rb', line 109 def run_cmd(cmd_arr, env_hash = nil, opts: {}) 'Running system command'.log_dbg cmd_arr.log_info "opts: #{opts}".log_dbg allow_failure = opts.delete(:allow_failure) || false exception = !allow_failure "exception: #{exception}".log_dbg = opts.merge({ exception: }) final_env = normalize_env(env_hash) if final_env.nil? Kernel.system(*cmd_arr, ) else Kernel.system(final_env, *cmd_arr, ) end end |
.wait_with_output(io_fd, waiter) ⇒ Array(String, Process::Status)
This method blocks until the process exits and all output is read.
Waits for a process to finish and reads all remaining output from its stdout.
287 288 289 290 291 292 |
# File 'lib/cinnabar/cmd_runner.rb', line 287 def wait_with_output(io_fd, waiter) status = waiter.value output = io_fd.read io_fd.close [output, status] end |