rabbitt-githooks 1.3.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/githooks/hook.rb CHANGED
@@ -22,7 +22,7 @@ require_relative 'system_utils'
22
22
 
23
23
  module GitHooks
24
24
  class Hook
25
- VALID_PHASES = %w{ pre-commit commit-msg }.freeze
25
+ VALID_PHASES = %w{ pre-commit commit-msg }.freeze unless defined? VALID_PHASES
26
26
 
27
27
  @__phases__ = {}
28
28
  @__mutex__ = Mutex.new
@@ -61,19 +61,19 @@ module GitHooks
61
61
  end
62
62
  end
63
63
 
64
- attr_reader :sections, :phase, :repository, :repository_path
64
+ attr_reader :sections, :phase, :repository, :repository_path, :limiters
65
65
  attr_accessor :args, :staged, :untracked, :tracked
66
66
 
67
67
  def initialize(phase)
68
- @phase = phase.to_s
69
- @sections = {}
70
- @commands = []
71
- @args = []
72
- @staged = true
73
- @tracked = false
74
- @untracked = false
75
-
76
- repository_path = Dir.getwd # rubocop:disable UselessAssignment
68
+ @phase = phase.to_s
69
+ @sections = {}
70
+ @limiters = {}
71
+ @commands = []
72
+ @args = []
73
+ @staged = true
74
+ @tracked = false
75
+ @untracked = false
76
+ @repository = Repository.new(Dir.getwd)
77
77
  end
78
78
 
79
79
  def [](name)
@@ -117,6 +117,29 @@ module GitHooks
117
117
 
118
118
  # DSL methods
119
119
 
120
+ def config_path
121
+ GitHooks.hooks_root.join('configs')
122
+ end
123
+
124
+ def config_file(*path_components)
125
+ config_path.join(*path_components)
126
+ end
127
+
128
+ def lib_path
129
+ GitHooks.hooks_root.join('lib')
130
+ end
131
+
132
+ def lib_file(*path_components)
133
+ lib_path.join(*path_components)
134
+ end
135
+
136
+ def limit(type)
137
+ unless @limiters.include? type
138
+ @limiters[type] ||= Repository::Limiter.new(type)
139
+ end
140
+ @limiters[type]
141
+ end
142
+
120
143
  def command(name, options = {})
121
144
  setup_command name, options
122
145
  end
@@ -160,8 +183,8 @@ module GitHooks
160
183
 
161
184
  def filter(limiters)
162
185
  manifest.dup.tap do |files|
163
- limiters.each do |limiter|
164
- puts "Limiter [#{limiter.type}] -> (#{limiter.only.inspect}) match against: " if GitHooks.debug?
186
+ limiters.each do |type, limiter|
187
+ puts "Limiter [#{type}] -> (#{limiter.only.inspect}) match against: " if GitHooks.debug?
165
188
  limiter.limit(files)
166
189
  end
167
190
  end
@@ -133,7 +133,7 @@ module GitHooks
133
133
  private
134
134
 
135
135
  def repo_path
136
- @repository.root_path
136
+ @repository.path
137
137
  end
138
138
 
139
139
  def git(*args)
@@ -18,8 +18,8 @@ module GitHooks
18
18
 
19
19
  def self.from_file_path(path, tracked = false)
20
20
  path = Pathname.new(path)
21
- entry_line = sprintf(":%06o %06o %040x %040x %s\t%s",
22
- 0, path.stat.mode, 0, 0, (tracked ? '^' : '?'), path.to_s)
21
+ entry_line = format(":%06o %06o %040x %040x %s\t%s",
22
+ 0, path.stat.mode, 0, 0, (tracked ? '^' : '?'), path.to_s)
23
23
  new(entry_line)
24
24
  end
25
25
 
@@ -59,15 +59,17 @@ module GitHooks
59
59
  instance.public_send(method, *args, &block)
60
60
  end
61
61
 
62
- attr_reader :root_path
62
+ attr_reader :path, :hooks
63
+ alias_method :path, :path
63
64
 
64
65
  def initialize(path = Dir.getwd)
65
- @root_path = get_root_path(path)
66
+ @path = get_root_path(path)
67
+ @hooks = Pathname.new(@path).join('.git', 'hooks')
66
68
  end
67
69
  protected :initialize
68
70
 
69
71
  def config
70
- @config ||= Repository::Config.new(root_path)
72
+ @config ||= Repository::Config.new(path)
71
73
  end
72
74
 
73
75
  def get_root_path(path)
@@ -140,10 +142,13 @@ module GitHooks
140
142
  cmd << (options.delete(:ref) || 'HEAD')
141
143
  end
142
144
 
143
- git(*cmd.compact.compact).output_lines.collect do |diff_data|
145
+ git(*cmd.flatten.compact).output_lines.collect do |diff_data|
144
146
  DiffIndexEntry.new(diff_data).to_repo_file
145
147
  end
146
- rescue
148
+ rescue StandardError => e
149
+ puts 'Error Encountered while acquiring manifest'
150
+ puts "Command: git #{cmd.flatten.compact.join(' ')}"
151
+ puts "Error: #{e.class.name}: #{e.message}: #{e.backtrace[0..5].join("\n\t")}"
147
152
  exit! 1
148
153
  end
149
154
 
@@ -27,75 +27,68 @@ require_relative 'repository'
27
27
  require_relative 'system_utils'
28
28
 
29
29
  module GitHooks
30
- module Runner
31
- # rubocop:disable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
32
- def run(options = {})
33
- options = Thor::CoreExt::HashWithIndifferentAccess.new(options)
34
-
35
- options['staged'] = options['staged'].nil? ? true : options['staged']
30
+ class Runner # rubocop:disable Metrics/ClassLength
31
+ attr_reader :repository, :script, :hook_path, :repo_path, :options
32
+ private :repository, :script, :hook_path, :repo_path, :options
36
33
 
37
- repo = options['repo'] ||= Repository.root_path
38
- script = options['script'] ||= Repository.instance(repo).config.script
39
- libpath = options['path'] ||= Repository.instance(repo).config.path
40
- args = options['args'] ||= []
34
+ def initialize(options = {}) # rubocop:disable Metrics/AbcSize
35
+ @repo_path = Pathname.new(options.delete('repo') || Repository.path)
36
+ @repository = Repository.instance(@repo_path)
37
+ @hook_path = acquire_hooks_path(options.delete('path') || @repository.config.path || @repository.path)
38
+ @script = options.delete('script') || @repository.config.script
39
+ @options = IndifferentAccessOpenStruct.new(options)
41
40
 
42
41
  GitHooks.verbose = !!ENV['GITHOOKS_VERBOSE']
43
42
  GitHooks.debug = !!ENV['GITHOOKS_DEBUG']
43
+ end
44
44
 
45
- if options['skip-pre']
45
+ # rubocop:disable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
46
+ def run
47
+ options.staged = options.staged.nil? ? true : options.staged
48
+
49
+ if options.skip_pre
46
50
  puts 'Skipping PreRun Executables'
47
51
  else
48
- run_externals('pre-run-execute', repo, args)
52
+ run_externals('pre-run-execute')
49
53
  end
50
54
 
51
- if script && !(options['ignore-script'] || GitHooks.ignore_script)
55
+ if script && !(options.ignore_script || GitHooks.ignore_script)
52
56
  command = "#{script} #{Pathname.new($0)} #{Shellwords.join(ARGV)};"
53
57
  puts "Kernel#exec(#{command.inspect})" if GitHooks.verbose
54
58
  exec(command)
55
- elsif libpath
56
- load_tests(libpath, options['skip-bundler'])
57
- start(options)
59
+ elsif hook_path
60
+ load_tests(hook_path, options.skip_bundler)
61
+ start
58
62
  else
59
- puts %q"I can't figure out what to run - specify either path or script to give me a hint..."
63
+ puts %q"I can't figure out what to run! Specify either path or script to give me a hint..."
60
64
  end
61
65
 
62
- if options['skip-post']
66
+ if options.skip_post
63
67
  puts 'Skipping PostRun Executables'
64
68
  else
65
- run_externals('post-run-execute', repo, args)
69
+ run_externals('post-run-execute')
66
70
  end
67
71
  rescue GitHooks::Error::NotAGitRepo => e
68
72
  puts "Unable to find a valid git repo in #{repo}."
69
73
  puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
70
74
  raise e
71
75
  end
72
- module_function :run
73
-
74
- def attach(options = {})
75
- repo_path = options[:repo] || Repository.root_path
76
- repo_path = Pathname.new(repo_path) unless repo_path.nil?
77
- repo_hooks = repo_path.join('.git', 'hooks')
78
- entry_path = options[:script] || options[:path]
79
76
 
80
- hook_phases = options[:hooks]
81
- hook_phases ||= Hook::VALID_PHASES
82
-
83
- bootstrapper = options[:bootstrap]
84
- bootstrapper = Pathname.new(bootstrapper).realpath unless bootstrapper.nil?
85
- entry_path = Pathname.new(entry_path).realdirpath
86
-
87
- repo = Repository.instance(repo_path)
77
+ def attach
78
+ entry_path = Pathname.new(options.script || options.path).realdirpath
79
+ hook_phases = options.hooks || Hook::VALID_PHASES
80
+ bootstrapper = Pathname.new(options.bootstrap).realpath if options.bootstrap
88
81
 
89
82
  if entry_path.directory?
90
- if path = repo.config['path'] # rubocop:disable AssignmentInCondition
83
+ if path = repository.config['path'] # rubocop:disable AssignmentInCondition
91
84
  fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to path #{path} - Detach to continue."
92
85
  end
93
- repo.config.set('path', entry_path)
86
+ repository.config.set('path', entry_path)
94
87
  elsif entry_path.executable?
95
- if path = repo.config['script'] # rubocop:disable AssignmentInCondition
88
+ if path = repository.config['script'] # rubocop:disable AssignmentInCondition
96
89
  fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to script #{path}. Detach to continue."
97
90
  end
98
- repo.config.set('script', entry_path)
91
+ repository.config.set('script', entry_path)
99
92
  else
100
93
  fail ArgumentError, "Provided path '#{entry_path}' is neither a directory nor an executable file."
101
94
  end
@@ -105,49 +98,35 @@ module GitHooks
105
98
  gitrunner ||= (GitHooks::BIN_PATH + 'githooks-runner').realpath
106
99
 
107
100
  hook_phases.each do |hook|
108
- hook = (repo_hooks + hook).to_s
101
+ hook = (@repository.hooks + hook).to_s
109
102
  puts "Linking #{gitrunner} -> #{hook}" if GitHooks.verbose
110
103
  FileUtils.ln_sf gitrunner.to_s, hook
111
104
  end
112
105
  end
113
- module_function :attach
114
-
115
- def detach(repo_path, hook_phases)
116
- repo_path ||= Repository.root_path
117
- repo_hooks = Pathname.new(repo_path).join('.git', 'hooks')
118
- hook_phases ||= Hook::VALID_PHASES
119
106
 
120
- repo = Repository.instance(repo_path)
121
-
122
- hook_phases.each do |hook|
123
- next unless (repo_hook = (repo_hooks + hook)).symlink?
124
- puts "Removing hook '#{hook}' from repository at: #{repo_path}" if GitHooks.verbose
107
+ def detach(hook_phases = nil)
108
+ (hook_phases || Hook::VALID_PHASES).each do |hook|
109
+ next unless (repo_hook = (@repository.hooks + hook)).symlink?
110
+ puts "Removing hook '#{hook}' from repository at: #{repository.path}" if GitHooks.verbose
125
111
  FileUtils.rm_f repo_hook
126
112
  end
127
113
 
128
- active_hooks = Hook::VALID_PHASES.select { |hook| (repo_hooks + hook).exist? }
114
+ active_hooks = Hook::VALID_PHASES.select { |hook| (@repository.hooks + hook).exist? }
129
115
 
130
116
  if active_hooks.empty?
131
117
  puts 'All hooks detached. Removing configuration section.'
132
- repo.config.remove_section(repo_path: repo_path)
118
+ repo.config.remove_section(repo_path: repository.path)
133
119
  else
134
120
  puts "Keeping configuration for active hooks: #{active_hooks.join(', ')}"
135
121
  end
136
122
  end
137
- module_function :detach
138
-
139
- def list(repo_path)
140
- repo_path ||= Pathname.new(Repository.root_path)
141
123
 
142
- repo = Repository.instance(repo_path)
143
- script = repo.config.script
144
- libpath = repo.config.path
145
-
146
- unless script || libpath
124
+ def list
125
+ unless script || hook_path
147
126
  fail Error::NotAttached, 'Repository currently not configured. Usage attach to setup for use with githooks.'
148
127
  end
149
128
 
150
- if (executables = repo.config.pre_run_execute).size > 0
129
+ if (executables = repository.config.pre_run_execute).size > 0
151
130
  puts 'PreRun Executables (in execution order):'
152
131
  puts executables.collect { |exe| " #{exe}" }.join("\n")
153
132
  puts
@@ -159,13 +138,13 @@ module GitHooks
159
138
  puts
160
139
  end
161
140
 
162
- if libpath
141
+ if hook_path
163
142
  puts 'Main Testing Library with Tests (in execution order):'
164
143
  puts ' Tests loaded from:'
165
- puts " #{libpath}"
144
+ puts " #{hook_path}"
166
145
  puts
167
146
 
168
- SystemUtils.quiet { load_tests(libpath, true) }
147
+ SystemUtils.quiet { load_tests(hook_path, true) }
169
148
 
170
149
  %w{ pre-commit commit-msg }.each do |phase|
171
150
  next unless Hook.phases[phase]
@@ -173,7 +152,7 @@ module GitHooks
173
152
  puts " Phase #{phase.camelize}:"
174
153
  Hook.phases[phase].sections.each_with_index do |section, section_index|
175
154
  printf " %3d: %s\n", section_index + 1, section.title
176
- section.all.each_with_index do |action, action_index|
155
+ section.actions.each_with_index do |action, action_index|
177
156
  printf " %3d: %s\n", action_index + 1, action.title
178
157
  action.limiters.each_with_index do |limiter, limiter_index|
179
158
  type, value = limiter.type.inspect, limiter.only
@@ -187,7 +166,7 @@ module GitHooks
187
166
  puts
188
167
  end
189
168
 
190
- if (executables = repo.config.post_run_execute).size > 0
169
+ if (executables = repository.config.post_run_execute).size > 0
191
170
  puts 'PostRun Executables (in execution order):'
192
171
  executables.each do |exe|
193
172
  puts " #{exe}"
@@ -195,16 +174,26 @@ module GitHooks
195
174
  puts
196
175
  end
197
176
  rescue Error::NotAGitRepo
198
- puts "Unable to find a valid git repo in #{repo}."
177
+ puts "Unable to find a valid git repo in #{repository}."
199
178
  puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
200
179
  raise
201
180
  end
202
- module_function :list
203
181
 
204
182
  private
205
183
 
206
- def run_externals(which, repo_path, args)
207
- Repository.instance(repo_path).config[which].all? do |executable|
184
+ def acquire_hooks_path(path)
185
+ path = Pathname.new(path) unless path.is_a? Pathname
186
+ path.tap do # return input path by default
187
+ return path if path.include? 'hooks'
188
+ return path if path.include? '.hooks'
189
+ return p if (p = path.join('hooks')).exist? # rubocop:disable Lint/UselessAssignment
190
+ return p if (p = path.join('.hooks')).exist? # rubocop:disable Lint/UselessAssignment
191
+ end
192
+ end
193
+
194
+ def run_externals(which)
195
+ args = options.args || []
196
+ repository.config[which].all? { |executable|
208
197
  command = SystemUtils::Command.new(File.basename(executable), bin_path: executable)
209
198
 
210
199
  puts "#{which.camelize}: #{command.build_command(args)}" if GitHooks.verbose
@@ -217,20 +206,19 @@ module GitHooks
217
206
  end
218
207
  end
219
208
  r.status.success?
220
- end || fail(TestsFailed, "Failed #{which.camelize} executables - giving up")
209
+ } || fail(TestsFailed, "Failed #{which.camelize} executables - giving up")
221
210
  end
222
- module_function :run_externals
223
211
 
224
- def start(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
225
- phase = options[:hook] || GitHooks.hook_name || 'pre-commit'
212
+ def start # rubocop:disable CyclomaticComplexity, MethodLength
213
+ phase = options.hook || GitHooks.hook_name || 'pre-commit'
226
214
  puts "PHASE: #{phase}" if GitHooks.debug
227
215
 
228
216
  if (active_hook = Hook.phases[phase])
229
- active_hook.args = options.delete(:args)
230
- active_hook.staged = options.delete(:staged)
231
- active_hook.untracked = options.delete(:untracked)
232
- active_hook.tracked = options.delete(:tracked)
233
- active_hook.repository_path = options.delete(:repo)
217
+ active_hook.args = options.args
218
+ active_hook.staged = options.staged
219
+ active_hook.untracked = options.untracked
220
+ active_hook.tracked = options.tracked
221
+ active_hook.repository_path = repository.path
234
222
  else
235
223
  fail Error::InvalidPhase, "Hook '#{phase}' is not defined - have you registered any tests for this hook yet?"
236
224
  end
@@ -268,26 +256,39 @@ module GitHooks
268
256
 
269
257
  exit(success ? 0 : 1)
270
258
  end
271
- module_function :start
272
259
 
273
- def load_tests(path, skip_bundler = false)
274
- hooks_root = Pathname.new(path).realpath
275
- hooks_path = (p = (hooks_root.join('hooks'))).exist? ? p : (hooks_root.join('.hooks'))
276
- hooks_libs = hooks_root.join('libs')
277
- gemfile = hooks_root.join('Gemfile')
260
+ def load_tests(hooks_path, skip_bundler = false)
261
+ # hooks stored locally in the repo_root should have their libs, init and
262
+ # gemfile stored in the hooks directory itself, whereas hooks stored
263
+ # in a separate repository should have have them all stored relative
264
+ # to the separate hooks directory.
265
+ if hook_path.to_s.start_with? repository.path
266
+ hook_data_path = acquire_hooks_path(repository.path)
267
+ else
268
+ hook_data_path = acquire_hooks_path(hook_path)
269
+ end
270
+
271
+ hooks_libs = hook_data_path.join('lib')
272
+ hooks_init = hook_data_path.join('hooks_init.rb')
273
+ gemfile = hook_data_path.join('Gemfile')
274
+
275
+ GitHooks.hooks_root = hook_data_path
278
276
 
279
- if gemfile.exist? && !skip_bundler
277
+ if gemfile.exist? && !(skip_bundler.nil? ? ENV.include?('GITHOOKS_SKIP_BUNDLER') : skip_bundler)
280
278
  puts "loading Gemfile from: #{gemfile}" if GitHooks.verbose
281
279
 
282
280
  begin
283
- ENV['BUNDLE_GEMFILE'] = (hooks_root + 'Gemfile').to_s
281
+ ENV['BUNDLE_GEMFILE'] = gemfile.to_s
284
282
 
285
283
  # stupid RVM polluting my environment without asking via it's
286
284
  # executable-hooks gem preloading bundler. hence the following ...
287
285
  if defined? Bundler
288
- [:@settings, :@bundle_path, :@configured, :@definition, :@load].each do |var|
286
+ [:@bundle_path, :@configured, :@definition, :@load].each do |var|
289
287
  ::Bundler.instance_variable_set(var, nil)
290
288
  end
289
+ # bundler tests for @settings using defined? - which means we need
290
+ # to forcibly remove it.
291
+ Bundler.send(:remove_instance_variable, :@settings)
291
292
  else
292
293
  require 'bundler'
293
294
  end
@@ -304,13 +305,18 @@ module GitHooks
304
305
 
305
306
  $LOAD_PATH.unshift hooks_libs.to_s
306
307
 
307
- Dir["#{hooks_path}/**/*.rb"].each do |lib|
308
- lib.gsub!('.rb', '')
309
- puts "Loading: #{lib}" if GitHooks.verbose
310
- require lib
308
+ if hooks_init.exist?
309
+ puts "Loading hooks from #{hooks_init} ..." if GitHooks.verbose?
310
+ require hooks_init.sub_ext('').to_s
311
+ else
312
+ puts 'Loading hooks brute-force style ...' if GitHooks.verbose?
313
+ Dir["#{hooks_path}/**/*.rb"].each do |lib|
314
+ lib.gsub!('.rb', '')
315
+ puts " -> #{lib}" if GitHooks.verbose
316
+ require lib
317
+ end
311
318
  end
312
319
  end
313
- module_function :load_tests
314
320
 
315
321
  # rubocop:enable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
316
322
  end
@@ -21,10 +21,10 @@ require 'delegate'
21
21
 
22
22
  module GitHooks
23
23
  class Section < DelegateClass(Array)
24
- attr_reader :name, :hook, :success, :actions, :benchmark
24
+ attr_reader :name, :hook, :success, :actions, :benchmark, :limiters
25
+
25
26
  alias_method :title, :name
26
27
  alias_method :success?, :success
27
- alias_method :all, :actions
28
28
 
29
29
  class << self
30
30
  def key_from_name(name)
@@ -36,6 +36,7 @@ module GitHooks
36
36
  @name = name.to_s.titleize
37
37
  @success = true
38
38
  @actions = []
39
+ @limiters = hook.limiters
39
40
  @hook = hook
40
41
  @benchmark = 0
41
42
 
@@ -85,12 +86,6 @@ module GitHooks
85
86
  success? ? text.success! : text.failure!
86
87
  end
87
88
 
88
- def action(title, &block)
89
- fail ArgumentError, 'expected block, received none' unless block_given?
90
- @actions << Action.new(title, self, &block)
91
- self
92
- end
93
-
94
89
  def run
95
90
  running!
96
91
  begin
@@ -101,5 +96,36 @@ module GitHooks
101
96
  finished!
102
97
  end
103
98
  end
99
+
100
+ ## DSL
101
+
102
+ def config_path
103
+ GitHooks.hooks_root.join('configs')
104
+ end
105
+
106
+ def config_file(*path_components)
107
+ config_path.join(*path_components)
108
+ end
109
+
110
+ def lib_path
111
+ GitHooks.hooks_root.join('lib')
112
+ end
113
+
114
+ def lib_file(*path_components)
115
+ lib_path.join(*path_components)
116
+ end
117
+
118
+ def limit(type)
119
+ unless @limiters.include? type
120
+ @limiters[type] ||= Repository::Limiter.new(type)
121
+ end
122
+ @limiters[type]
123
+ end
124
+
125
+ def action(title, &block)
126
+ fail ArgumentError, 'expected block, received none' unless block_given?
127
+ @actions << Action.new(title, self, &block)
128
+ self
129
+ end
104
130
  end
105
131
  end
@@ -14,8 +14,8 @@ module GitHooks
14
14
 
15
15
  def find_bin(name)
16
16
  # rubocop:disable MultilineBlockChain, Blocks
17
- ENV['PATH'].split(/:/).collect {
18
- |path| Pathname.new(path) + name.to_s
17
+ ENV['PATH'].split(/:/).collect { |path|
18
+ Pathname.new(path) + name.to_s
19
19
  }.select { |path|
20
20
  path.exist? && path.executable?
21
21
  }.collect(&:to_s)
@@ -67,10 +67,12 @@ module GitHooks
67
67
  LANG LC_ALL SHELL SHLVL TERM TMPDIR USER
68
68
  SSH_USER SSH_AUTH_SOCK
69
69
  GEM_HOME GEM_PATH MY_RUBY_HOME
70
- )
70
+ GIT_DIR GIT_AUTHOR_DATE GIT_INDEX_FILE GIT_AUTHOR_NAME GIT_PREFIX GIT_AUTHOR_EMAIL
71
+ ) unless defined? ENV_WHITELIST
71
72
 
72
73
  class Result
73
- attr_reader :output, :error, :status
74
+ attr_accessor :output, :error
75
+ attr_reader :status
74
76
  def initialize(output, error, status)
75
77
  @output = output.strip
76
78
  @error = error.strip
@@ -135,7 +137,7 @@ module GitHooks
135
137
  def prep_env(env = {})
136
138
  Hash[env].each_with_object([]) do |(k, v), array|
137
139
  array << %Q|#{k}="#{v}"| if ENV_WHITELIST.include? k
138
- end.join(' ')
140
+ end
139
141
  end
140
142
 
141
143
  def execute(*args, &_block) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize, PerceivedComplexity
@@ -154,27 +156,39 @@ module GitHooks
154
156
  command.push options.delete(:post_run) if options[:post_run]
155
157
  command = shellwords(command.flatten.join(';'))
156
158
 
157
- environment = prep_env(options.delete(:env) || ENV)
159
+ environment = prep_env(options.delete(:env) || ENV).join(' ')
158
160
 
159
161
  error_file = Tempfile.new('ghstderr')
162
+
163
+ script_file = Tempfile.new('ghscript')
164
+ script_file.puts "exec 2>#{error_file.path}"
165
+ script_file.puts command.join(' ')
166
+
167
+ script_file.rewind
168
+
160
169
  begin
161
- real_command = %Q{
162
- /usr/bin/env -i #{environment} bash -c '
163
- ( #{command.join(' ').gsub("'", %q|'"'"'|)}) 2>#{error_file.path}
164
- '
165
- }
170
+ real_command = "/usr/bin/env -i #{environment} bash #{script_file.path}"
166
171
 
167
- $stderr.puts real_command if GitHooks.debug?
172
+ if GitHooks.verbose?
173
+ $stderr.puts "Command Line :\n----\n#{real_command}\n----\n"
174
+ $stderr.puts "Command Script:\n----\n#{script_file.read}\n----\n"
175
+ end
168
176
 
169
177
  output = %x{ #{real_command} }
170
178
  result = Result.new(output, error_file.read, $?)
171
179
 
172
- if GitHooks.verbose? && result.failure?
173
- STDERR.puts "Command failed with exit code [#{result.status.exitstatus}]",
174
- "ENVIRONMENT:\n\t#{environment}\n\n",
175
- "COMMAND:\n\t#{command.join(' ')}\n\n",
176
- "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
177
- "ERROR:\n-----\n#{result.error}\n-----\n\n"
180
+ if GitHooks.verbose?
181
+ if result.failure?
182
+ STDERR.puts "Command failed with exit code [#{result.status.exitstatus}]",
183
+ "ENVIRONMENT:\n\t#{environment}\n\n",
184
+ "COMMAND:\n\t#{command.join(' ')}\n\n",
185
+ "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
186
+ "ERROR:\n-----\n#{result.error}\n-----\n\n"
187
+ else
188
+ STDERR.puts "Command succeeded with exit code [#{result.status.exitstatus}]",
189
+ "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
190
+ "ERROR:\n-----\n#{result.error}\n-----\n\n"
191
+ end
178
192
  end
179
193
 
180
194
  sanitize = [ :strip, :non_printable ]
@@ -182,8 +196,11 @@ module GitHooks
182
196
  sanitize << :empty_lines if options.delete(:strip_empty_lines)
183
197
  result.sanitize!(*sanitize)
184
198
 
185
- block_given? ? yield(result) : result
199
+ result.tap { yield(result) if block_given? }
186
200
  ensure
201
+ script_file.close
202
+ script_file.unlink
203
+
187
204
  error_file.close
188
205
  error_file.unlink
189
206
  end
@@ -18,5 +18,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
18
18
  =end
19
19
 
20
20
  module GitHooks
21
- VERSION = '1.3.2'
21
+ VERSION = '1.4.1' unless defined? VERSION
22
22
  end