Class: GitHooks::SystemUtils::Command

Inherits:
Object
  • Object
show all
Includes:
Shellwords
Defined in:
lib/githooks/system_utils.rb

Defined Under Namespace

Classes: Result

Constant Summary collapse

ENV_WHITELIST =
%w(
  PATH HOME LDFLAGS CPPFLAGS DISPLAY EDITOR
  LANG LC_ALL SHELL SHLVL TERM TMPDIR USER HOME
  SSH_USER SSH_AUTH_SOCK
  GEM_HOME GEM_PATH MY_RUBY_HOME
  GIT_DIR GIT_AUTHOR_DATE GIT_INDEX_FILE GIT_AUTHOR_NAME GIT_PREFIX GIT_AUTHOR_EMAIL
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ Command

Returns a new instance of Command.



119
120
121
122
123
# File 'lib/githooks/system_utils.rb', line 119

def initialize(name, options = {})
  @bin_path = options.delete(:bin_path) || SystemUtils.which(name) || name
  @run_path = options.delete(:chdir)
  @name     = name.to_s.gsub(/([\W-]+)/, '_')
end

Instance Attribute Details

#bin_pathObject (readonly)

Returns the value of attribute bin_path.



117
118
119
# File 'lib/githooks/system_utils.rb', line 117

def bin_path
  @bin_path
end

#nameObject (readonly)

Returns the value of attribute name.



117
118
119
# File 'lib/githooks/system_utils.rb', line 117

def name
  @name
end

#run_pathObject (readonly)

Returns the value of attribute run_path.



117
118
119
# File 'lib/githooks/system_utils.rb', line 117

def run_path
  @run_path
end

Instance Method Details

#build_command(args, options) ⇒ Object



129
130
131
# File 'lib/githooks/system_utils.rb', line 129

def build_command(args, options)
  Array(args).unshift(command_path(options))
end

#command_path(options = {}) ⇒ Object



133
134
135
# File 'lib/githooks/system_utils.rb', line 133

def command_path(options = {})
  options.delete(:use_name) ? name : bin_path.to_s
end

#execute(*args, &_block) ⇒ Object Also known as: call

rubocop:disable MethodLength, CyclomaticComplexity, AbcSize, PerceivedComplexity



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/githooks/system_utils.rb', line 156

def execute(*args, &_block) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize, PerceivedComplexity
  options = args.extract_options!

  command = build_command(args, options)
  command.unshift("cd #{run_path} ;") if run_path
  command.unshift('sudo') if options.delete(:use_sudo)
  command = Array(command.flatten.join(' '))

  command.unshift options.delete(:pre_pipe)  if options[:pre_pipe]
  command.push options.delete(:post_pipe) if options[:post_pipe]
  command = Array(command.flatten.join('|'))

  command.unshift options.delete(:pre_run)  if options[:pre_run]
  command.push options.delete(:post_run) if options[:post_run]
  command = shellwords(command.flatten.join(';'))

  command_env    = options.delete(:env) || {}
  whitelist_keys = ENV_WHITELIST | command_env.keys
  environment    = prep_env(ENV.to_h.merge(command_env), include: whitelist_keys).join(' ')

  error_file = Tempfile.new('ghstderr')

  script_file = Tempfile.new('ghscript')
  script_file.puts "exec 2>#{error_file.path}"
  script_file.puts command.join(' ')

  script_file.rewind

  begin
    real_command = "/usr/bin/env -i #{environment} bash #{script_file.path}"

    if GitHooks.verbose?
      $stderr.puts "Command Line  :\n----\n#{real_command}\n----\n"
      $stderr.puts "Command Script:\n----\n#{script_file.read}\n----\n"
    end

    output = %x{ #{real_command} }
    result = Result.new(output, error_file.read, $?)

    if GitHooks.verbose?
      if result.failure?
        STDERR.puts "Command failed with exit code [#{result.status.exitstatus}]",
                    "ENVIRONMENT:\n\t#{environment}\n\n",
                    "COMMAND:\n\t#{command.join(' ')}\n\n",
                    "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
                    "ERROR:\n-----\n#{result.error}\n-----\n\n"
      else
        STDERR.puts "Command succeeded with exit code [#{result.status.exitstatus}]",
                    "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
                    "ERROR:\n-----\n#{result.error}\n-----\n\n"
      end
    end

    sanitize = [ :strip, :non_printable ]
    sanitize << :colors unless options.delete(:color)
    sanitize << :empty_lines if options.delete(:strip_empty_lines)
    result.sanitize!(*sanitize)

    result.tap { yield(result) if block_given? }
  ensure
    script_file.close
    script_file.unlink

    error_file.close
    error_file.unlink
  end
end

#methodObject



125
126
127
# File 'lib/githooks/system_utils.rb', line 125

def method
  @name.to_sym
end

#prep_env(env = ENV, options = {}) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/githooks/system_utils.rb', line 137

def prep_env(env = ENV, options = {})
  include_keys = options.delete(:include) || ENV_WHITELIST
  exclude_keys = options.delete(:exclude) || []

  if exclude_keys.size > 0 && include_keys.size > 0
    raise ArgumentError, "include and exclude are mutually exclusive"
  end

  Hash[env].each_with_object([]) do |(key, value), array|
    if exclude_keys.size > 0
      next if exclude_keys.include?(key)
    elsif include_keys.size > 0
      next unless include_keys.include?(key)
    end

    array << %Q'#{key}=#{value.inspect}'
  end
end