Module: Corgibytes::Freshli::Commons::Execute

Defined in:
lib/corgibytes/freshli/commons/execute.rb

Overview

Contains utility methods for executing commands and working with their output.

Constant Summary collapse

BUFFER_LEN =
8
WAIT_TIMEOUT =
1

Instance Method Summary collapse

Instance Method Details

#enable_dotnet_command_colorsObject



24
25
26
# File 'lib/corgibytes/freshli/commons/execute.rb', line 24

def enable_dotnet_command_colors
  ENV['DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION'] = 'true'
end

#execute(command) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/corgibytes/freshli/commons/execute.rb', line 91

def execute(command)
  exit_status = nil
  Open3.popen3(command) do |_in, stdout, stderr, wait_thread|
    for_reading = [stdout, stderr]
    until for_reading.empty?
      # IO.select blocks until one of the streams is has something to read
      # or the wait timeout is reached
      readable, _writable, _errors = IO.select(for_reading, [], [], WAIT_TIMEOUT)
      for_reading = skip_or_read_streams(for_reading, readable, stdout, stderr)
    end

    exit_status = wait_thread.value
  end
  exit_status
end

#fill_buffer_from_stream(stream, buffer) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/corgibytes/freshli/commons/execute.rb', line 50

def fill_buffer_from_stream(stream, buffer)
  # loop through reading data until there is an EOF (value is nil)
  # or there is no more data to read (value is empty)
  result = nil
  loop do
    local_buffer = ''.dup
    result = stream.read_nonblock(BUFFER_LEN, local_buffer, exception: false)
    buffer << local_buffer

    break unless safe_to_read?(result) && buffer.length < BUFFER_LEN
  end
  result
end

#msbuild_dll_pathObject



11
12
13
14
15
16
17
18
# File 'lib/corgibytes/freshli/commons/execute.rb', line 11

def msbuild_dll_path
  dotnet_exe_path = File.realpath(find_executable('dotnet'))
  dotnet_dir = File.dirname(dotnet_exe_path)
  sdk_dir = File.join(dotnet_dir, 'sdk', Dir.children(File.join(dotnet_dir, 'sdk')).max)
  result = File.join(sdk_dir, 'MSBuild.dll')
  result = result.gsub('/', '\\') if Gem.win_platform?
  result
end

#null_output_targetObject



20
21
22
# File 'lib/corgibytes/freshli/commons/execute.rb', line 20

def null_output_target
  Gem.win_platform? ? '\\\\.\\nul' : '/dev/null'
end

#read_streams(for_reading, readable, stdout, stderr) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/corgibytes/freshli/commons/execute.rb', line 64

def read_streams(for_reading, readable, stdout, stderr)
  # In the case that both streams are readable (and thus have content)
  # read from each of them. In this case, we cannot guarantee any order
  # because we recieve the items at essentially the same time.
  # We can still ensure that we don't mix data incorrectly.
  readable.each do |stream|
    buffer = ''.dup
    result = fill_buffer_from_stream(stream, buffer)

    for_reading -= [stream] if stream_eof?(result)

    write_buffered_output_to_correct_stream(buffer, stream, stdout, stderr)
  end
  for_reading
end

#safe_to_read?(status) ⇒ Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/corgibytes/freshli/commons/execute.rb', line 36

def safe_to_read?(status)
  !stream_eof?(status) && !stream_open_but_empty?(status)
end

#skip_or_read_streams(for_reading, readable, stdout, stderr) ⇒ Object



80
81
82
83
84
85
86
87
88
# File 'lib/corgibytes/freshli/commons/execute.rb', line 80

def skip_or_read_streams(for_reading, readable, stdout, stderr)
  # readable is nil in the case of a timeout - loop back again
  if readable.nil?
    Thread.pass
  else
    for_reading = read_streams(for_reading, readable, stdout, stderr)
  end
  for_reading
end

#stream_eof?(status) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/corgibytes/freshli/commons/execute.rb', line 28

def stream_eof?(status)
  status.nil?
end

#stream_open_but_empty?(status) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/corgibytes/freshli/commons/execute.rb', line 32

def stream_open_but_empty?(status)
  status.empty? || status == :wait_readable
end

#write_buffered_output_to_correct_stream(buffer, stream, stdout, stderr) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/corgibytes/freshli/commons/execute.rb', line 40

def write_buffered_output_to_correct_stream(buffer, stream, stdout, stderr)
  if stream == stdout
    $stdout.print(buffer)
  elsif stream == stderr
    $stderr.print(buffer)
  end
end