Class: Maestro::Util::Shell

Inherits:
Object
  • Object
show all
Defined in:
lib/util/shell.rb,
lib/util/version.rb

Defined Under Namespace

Classes: ExitCode

Constant Summary collapse

IS_WINDOWS =

Utility variables

ChildProcess.windows?
SEPARATOR =
IS_WINDOWS ? "\\" : "/"
MOVE_COMMAND =
IS_WINDOWS ? 'move' : 'mv'
ENV_EXPORT_COMMAND =
IS_WINDOWS ? 'set' : 'export'
COMMAND_SEPARATOR =

IS_WINDOWS ? ‘&&’ : ‘&&’

'&&'
SCRIPT_EXTENSION =
IS_WINDOWS ? '.bat' : '.shell'
BASH_EXECUTABLE =
'bash'
VERSION =
'1.0.0'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#exit_codeObject (readonly)

Returns the value of attribute exit_code.



14
15
16
# File 'lib/util/shell.rb', line 14

def exit_code
  @exit_code
end

#output_fileObject (readonly)

Returns the value of attribute output_file.



12
13
14
# File 'lib/util/shell.rb', line 12

def output_file
  @output_file
end

#script_fileObject (readonly)

Returns the value of attribute script_file.



11
12
13
# File 'lib/util/shell.rb', line 11

def script_file
  @script_file
end

#shellObject (readonly)

Returns the value of attribute shell.



13
14
15
# File 'lib/util/shell.rb', line 13

def shell
  @shell
end

Class Method Details

.run_command(command) ⇒ Object



41
42
43
44
45
46
# File 'lib/util/shell.rb', line 41

def Shell.run_command(command)
  shell = Shell.new
  shell.create_script(command)
  shell.run_script
  return shell.exit_code, shell.to_s
end

.unset_env_variable(var) ⇒ Object



37
38
39
# File 'lib/util/shell.rb', line 37

def Shell.unset_env_variable(var)
  IS_WINDOWS ? "set #{var}=" : "unset #{var}"
end

Instance Method Details

#create_script(contents) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/util/shell.rb', line 48

def create_script(contents)
  raise "Script Cannot Be Empty" if contents.nil? or contents.empty?

  @script_file = Tempfile.new(["script", SCRIPT_EXTENSION])
  @output_file = Tempfile.new(['output','log'])

  # Add @echo off in windows if the script doesn't already use @echo
  pre = (IS_WINDOWS and !contents.include?("@echo ")) ? "@echo off\n" : ""

  # Run any commands in the default system Ruby environment, rather
  # than the one the agent is currently using (which within the wrapper,
  # sets clean values for these to avoid RVM or System gems that might
  # conflict). If the caller needs a specific Ruby environment, it should
  # establish that itself (as the rake task does through rvm if chosen)
  # Add clear env variable commands to head of script, since we don't necessarily have access to env here (depending on
  # version of ruby/bugs)
  contents = "#{pre}#{Shell.unset_env_variable('GEM_HOME')}\n#{Shell.unset_env_variable('GEM_PATH')}\n#{contents}"
  @script_file.write(contents)
  @script_file.close
  Maestro.log.debug "Writing Script File To #{@script_file.path}"
  return get_command(@script_file.path)
end

#run_scriptObject



71
72
73
# File 'lib/util/shell.rb', line 71

def run_script
  run_script_with_delegate(nil, nil)
end

#run_script_with_delegate(delegate, on_output) ⇒ Object

if delegate provided, the method named/symbolized by on_output value will be called for each line of output to either stdout or stderr. two parameters are passed:

+text+  String  Output text.  This may be any amount of data from 1 character to many lines.
                do not assume it always represents a single line.
+err+   Boolean True if line is from stderr


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/util/shell.rb', line 81

def run_script_with_delegate(delegate, on_output)
  File.open(@output_file.path, 'a') do |out_file|
    r, w = IO.pipe
    ChildProcess.posix_spawn = true
    if IS_WINDOWS
      if ChildProcess.jruby?
        # Due to https://github.com/jarib/childprocess/issues/26, we
        # must use a different implementation of ChildProcess on
        # Windows + JRuby
        process = ChildProcess::JRuby::Process.new([@command_line])
      else
        process = ChildProcess.build(@command_line)
      end
    else
      process = ChildProcess.build(BASH_EXECUTABLE, @command_line)
    end
    process.io.stdout = process.io.stderr = w
    process.start
    # On Windows, can't close the pipe until process has exited or you
    # get an EOF, see
    # https://github.com/jarib/childprocess/pull/22#issuecomment-3395687
    w.close unless IS_WINDOWS

    potential_eof = false
    begin
      loop {
        text = r.read_nonblock(1024)
        potential_eof = false
        out_file.write(text)

        if delegate && on_output
          delegate.send(on_output, text)
        end
      }
    rescue IO::WaitReadable => e
      if !process.exited?
        # process still running, block for input here to avoid a busy
        # loop, but with a timeout in case the process exited with no
        # further output
        IO.select([r], nil, nil, 1)
        retry
      elsif !potential_eof
        # process is done, but keep looping while there is input to
        # read
        potential_eof = true
        retry
      end
    rescue EOFError
      # expected when we reach end of output from the process
    end

    r.close
    process.wait unless process.exited?
    w.close if IS_WINDOWS
    @exit_code = ExitCode.new(process.exit_code)
  end

  return @exit_code
end

#to_sObject Also known as: output



141
142
143
# File 'lib/util/shell.rb', line 141

def to_s
  @output_file.read if @output_file
end