rabbitt-githooks 1.3.0 → 1.3.2
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.
- checksums.yaml +5 -13
- data/Gemfile.lock +29 -19
- data/Rakefile +20 -0
- data/bin/githooks +2 -1
- data/bin/githooks-runner +10 -2
- data/lib/githooks.rb +11 -7
- data/lib/githooks/action.rb +67 -53
- data/lib/githooks/cli.rb +9 -6
- data/lib/githooks/commands/config.rb +26 -19
- data/lib/githooks/core_ext.rb +2 -1
- data/lib/githooks/core_ext/array/extract_options.rb +5 -1
- data/lib/githooks/core_ext/array/min_max.rb +4 -4
- data/lib/githooks/core_ext/array/select_with_index.rb +1 -1
- data/lib/githooks/core_ext/colorize.rb +30 -0
- data/lib/githooks/core_ext/object.rb +8 -0
- data/lib/githooks/core_ext/pathname.rb +7 -6
- data/lib/githooks/core_ext/string.rb +1 -1
- data/lib/githooks/core_ext/string/sanitize.rb +51 -0
- data/lib/githooks/error.rb +1 -0
- data/lib/githooks/hook.rb +21 -26
- data/lib/githooks/repository.rb +31 -35
- data/lib/githooks/repository/config.rb +16 -24
- data/lib/githooks/repository/diff_index_entry.rb +3 -2
- data/lib/githooks/repository/file.rb +14 -7
- data/lib/githooks/runner.rb +38 -36
- data/lib/githooks/section.rb +13 -19
- data/lib/githooks/system_utils.rb +132 -47
- data/lib/githooks/version.rb +1 -1
- data/lib/tasks/dev.task +14 -0
- data/rabbitt-githooks.gemspec +18 -15
- metadata +58 -33
- data/.gitignore +0 -34
- data/.hooks/commit-messages.rb +0 -29
- data/.hooks/formatting.rb +0 -44
- data/.rubocop.yml +0 -87
- data/lib/githooks/core_ext/process.rb +0 -9
- data/lib/githooks/core_ext/string/strip_empty_lines.rb +0 -9
- data/lib/githooks/terminal_colors.rb +0 -62
- data/thoughts.txt +0 -56
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'ostruct'
|
|
2
2
|
require 'pathname'
|
|
3
|
-
require 'githooks/repository/file'
|
|
4
3
|
|
|
5
4
|
module GitHooks
|
|
6
5
|
class Repository
|
|
@@ -31,7 +30,8 @@ module GitHooks
|
|
|
31
30
|
super parse_data(entry)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
# rubocop:disable MultilineOperationIndentation
|
|
34
|
+
def parse_data(entry) # rubocop:disable MethodLength, AbcSize
|
|
35
35
|
data = Hash[
|
|
36
36
|
DIFF_STRUCTURE_REGEXP.names.collect(&:to_sym).zip(
|
|
37
37
|
entry.match(DIFF_STRUCTURE_REGEXP).captures
|
|
@@ -53,6 +53,7 @@ module GitHooks
|
|
|
53
53
|
score: data[:score].to_i
|
|
54
54
|
}
|
|
55
55
|
end
|
|
56
|
+
# rubocop:enable MultilineOperationIndentation
|
|
56
57
|
|
|
57
58
|
def to_repo_file
|
|
58
59
|
Repository::File.new(self)
|
|
@@ -17,16 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
17
17
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18
18
|
=end
|
|
19
19
|
|
|
20
|
-
require 'ostruct'
|
|
21
20
|
require 'delegate'
|
|
22
21
|
|
|
23
|
-
# allow for reloading of class
|
|
24
|
-
unless defined? DiffIndexEntryDelegateClass
|
|
25
|
-
DiffIndexEntryDelegateClass = DelegateClass(GitHooks::Repository::DiffIndexEntry)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
22
|
module GitHooks
|
|
29
23
|
class Repository
|
|
24
|
+
# allow for reloading of class
|
|
25
|
+
unless defined? DiffIndexEntryDelegateClass
|
|
26
|
+
DiffIndexEntryDelegateClass = DelegateClass(DiffIndexEntry)
|
|
27
|
+
end
|
|
28
|
+
|
|
30
29
|
class File < DiffIndexEntryDelegateClass
|
|
31
30
|
def initialize(entry)
|
|
32
31
|
unless entry.is_a? Repository::DiffIndexEntry
|
|
@@ -68,7 +67,7 @@ module GitHooks
|
|
|
68
67
|
end
|
|
69
68
|
end
|
|
70
69
|
|
|
71
|
-
def match(type, selector)
|
|
70
|
+
def match(type, selector) # rubocop:disable AbcSize
|
|
72
71
|
value = attribute_value(type)
|
|
73
72
|
return selector.call(value) if selector.respond_to? :call
|
|
74
73
|
|
|
@@ -122,6 +121,14 @@ module GitHooks
|
|
|
122
121
|
return [] unless fd
|
|
123
122
|
strip_newlines ? fd.readlines.collect(&:chomp!) : fd.readlines
|
|
124
123
|
end
|
|
124
|
+
|
|
125
|
+
def <=>(other)
|
|
126
|
+
path.to_s <=> other.path.to_s
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def ==(other)
|
|
130
|
+
path.to_s == other.path.to_s
|
|
131
|
+
end
|
|
125
132
|
end
|
|
126
133
|
end
|
|
127
134
|
end
|
data/lib/githooks/runner.rb
CHANGED
|
@@ -17,23 +17,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
17
17
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18
18
|
=end
|
|
19
19
|
|
|
20
|
+
require 'thor'
|
|
20
21
|
require 'fileutils'
|
|
21
|
-
require 'githooks/terminal_colors'
|
|
22
22
|
require 'shellwords'
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
require_relative 'error'
|
|
25
|
+
require_relative 'hook'
|
|
26
|
+
require_relative 'repository'
|
|
27
|
+
require_relative 'system_utils'
|
|
24
28
|
|
|
25
29
|
module GitHooks
|
|
26
30
|
module Runner
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
MARK_SUCCESS = '✓'
|
|
30
|
-
MARK_FAILURE = 'X'
|
|
31
|
-
MARK_UNKNOWN = '?'
|
|
32
|
-
|
|
33
|
-
def run(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
|
|
34
|
-
# unfreeze options
|
|
31
|
+
# rubocop:disable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
|
|
32
|
+
def run(options = {})
|
|
35
33
|
options = Thor::CoreExt::HashWithIndifferentAccess.new(options)
|
|
36
34
|
|
|
35
|
+
options['staged'] = options['staged'].nil? ? true : options['staged']
|
|
36
|
+
|
|
37
37
|
repo = options['repo'] ||= Repository.root_path
|
|
38
38
|
script = options['script'] ||= Repository.instance(repo).config.script
|
|
39
39
|
libpath = options['path'] ||= Repository.instance(repo).config.path
|
|
@@ -56,7 +56,7 @@ module GitHooks
|
|
|
56
56
|
load_tests(libpath, options['skip-bundler'])
|
|
57
57
|
start(options)
|
|
58
58
|
else
|
|
59
|
-
puts
|
|
59
|
+
puts %q"I can't figure out what to run - specify either path or script to give me a hint..."
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
if options['skip-post']
|
|
@@ -64,17 +64,17 @@ module GitHooks
|
|
|
64
64
|
else
|
|
65
65
|
run_externals('post-run-execute', repo, args)
|
|
66
66
|
end
|
|
67
|
-
rescue GitHooks::Error::NotAGitRepo
|
|
67
|
+
rescue GitHooks::Error::NotAGitRepo => e
|
|
68
68
|
puts "Unable to find a valid git repo in #{repo}."
|
|
69
69
|
puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
|
|
70
|
+
raise e
|
|
70
71
|
end
|
|
71
72
|
module_function :run
|
|
72
73
|
|
|
73
|
-
def attach(options = {})
|
|
74
|
+
def attach(options = {})
|
|
74
75
|
repo_path = options[:repo] || Repository.root_path
|
|
75
76
|
repo_path = Pathname.new(repo_path) unless repo_path.nil?
|
|
76
|
-
repo_hooks = repo_path
|
|
77
|
-
|
|
77
|
+
repo_hooks = repo_path.join('.git', 'hooks')
|
|
78
78
|
entry_path = options[:script] || options[:path]
|
|
79
79
|
|
|
80
80
|
hook_phases = options[:hooks]
|
|
@@ -114,7 +114,7 @@ module GitHooks
|
|
|
114
114
|
|
|
115
115
|
def detach(repo_path, hook_phases)
|
|
116
116
|
repo_path ||= Repository.root_path
|
|
117
|
-
repo_hooks = Pathname.new(repo_path)
|
|
117
|
+
repo_hooks = Pathname.new(repo_path).join('.git', 'hooks')
|
|
118
118
|
hook_phases ||= Hook::VALID_PHASES
|
|
119
119
|
|
|
120
120
|
repo = Repository.instance(repo_path)
|
|
@@ -136,7 +136,7 @@ module GitHooks
|
|
|
136
136
|
end
|
|
137
137
|
module_function :detach
|
|
138
138
|
|
|
139
|
-
def list(repo_path)
|
|
139
|
+
def list(repo_path)
|
|
140
140
|
repo_path ||= Pathname.new(Repository.root_path)
|
|
141
141
|
|
|
142
142
|
repo = Repository.instance(repo_path)
|
|
@@ -149,9 +149,7 @@ module GitHooks
|
|
|
149
149
|
|
|
150
150
|
if (executables = repo.config.pre_run_execute).size > 0
|
|
151
151
|
puts 'PreRun Executables (in execution order):'
|
|
152
|
-
executables.
|
|
153
|
-
puts " #{exe}"
|
|
154
|
-
end
|
|
152
|
+
puts executables.collect { |exe| " #{exe}" }.join("\n")
|
|
155
153
|
puts
|
|
156
154
|
end
|
|
157
155
|
|
|
@@ -196,9 +194,10 @@ module GitHooks
|
|
|
196
194
|
end
|
|
197
195
|
puts
|
|
198
196
|
end
|
|
199
|
-
rescue
|
|
197
|
+
rescue Error::NotAGitRepo
|
|
200
198
|
puts "Unable to find a valid git repo in #{repo}."
|
|
201
199
|
puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
|
|
200
|
+
raise
|
|
202
201
|
end
|
|
203
202
|
module_function :list
|
|
204
203
|
|
|
@@ -206,7 +205,7 @@ module GitHooks
|
|
|
206
205
|
|
|
207
206
|
def run_externals(which, repo_path, args)
|
|
208
207
|
Repository.instance(repo_path).config[which].all? do |executable|
|
|
209
|
-
command = SystemUtils::Command.new(File.basename(executable),
|
|
208
|
+
command = SystemUtils::Command.new(File.basename(executable), bin_path: executable)
|
|
210
209
|
|
|
211
210
|
puts "#{which.camelize}: #{command.build_command(args)}" if GitHooks.verbose
|
|
212
211
|
unless (r = command.execute(*args)).status.success?
|
|
@@ -222,7 +221,7 @@ module GitHooks
|
|
|
222
221
|
end
|
|
223
222
|
module_function :run_externals
|
|
224
223
|
|
|
225
|
-
def start(options = {}) # rubocop:disable
|
|
224
|
+
def start(options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
|
|
226
225
|
phase = options[:hook] || GitHooks.hook_name || 'pre-commit'
|
|
227
226
|
puts "PHASE: #{phase}" if GitHooks.debug
|
|
228
227
|
|
|
@@ -237,21 +236,22 @@ module GitHooks
|
|
|
237
236
|
end
|
|
238
237
|
|
|
239
238
|
success = active_hook.run
|
|
240
|
-
section_length = active_hook.sections.
|
|
239
|
+
section_length = active_hook.sections.maximum { |s| s.title.length }
|
|
241
240
|
sections = active_hook.sections.select { |section| !section.actions.empty? }
|
|
242
241
|
|
|
242
|
+
# TODO: refactor to show this in realtime instead of after the hooks have run
|
|
243
243
|
sections.each do |section|
|
|
244
244
|
hash_tail_length = (section_length - section.title.length)
|
|
245
245
|
printf "===== %s %s===== (%ds)\n", section.colored_name(phase), ('=' * hash_tail_length), section.benchmark
|
|
246
|
-
|
|
247
246
|
section.actions.each_with_index do |action, index|
|
|
248
|
-
printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.
|
|
247
|
+
printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.status_symbol, action.colored_title, action.benchmark
|
|
249
248
|
|
|
250
249
|
action.errors.each do |error|
|
|
251
|
-
printf " %s %s\n",
|
|
250
|
+
printf " %s %s\n", GitHooks::FAILURE_SYMBOL, error
|
|
252
251
|
end
|
|
253
252
|
|
|
254
|
-
state_string =
|
|
253
|
+
state_string = action.success? ? GitHooks::SUCCESS_SYMBOL : GitHooks::UNKNOWN_SYMBOL
|
|
254
|
+
|
|
255
255
|
action.warnings.each do |warning|
|
|
256
256
|
printf " %s %s\n", state_string, warning
|
|
257
257
|
end
|
|
@@ -270,11 +270,11 @@ module GitHooks
|
|
|
270
270
|
end
|
|
271
271
|
module_function :start
|
|
272
272
|
|
|
273
|
-
def load_tests(path, skip_bundler = false)
|
|
273
|
+
def load_tests(path, skip_bundler = false)
|
|
274
274
|
hooks_root = Pathname.new(path).realpath
|
|
275
|
-
hooks_path = (p = (hooks_root
|
|
276
|
-
hooks_libs = hooks_root
|
|
277
|
-
gemfile = hooks_root
|
|
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')
|
|
278
278
|
|
|
279
279
|
if gemfile.exist? && !skip_bundler
|
|
280
280
|
puts "loading Gemfile from: #{gemfile}" if GitHooks.verbose
|
|
@@ -286,16 +286,16 @@ module GitHooks
|
|
|
286
286
|
# executable-hooks gem preloading bundler. hence the following ...
|
|
287
287
|
if defined? Bundler
|
|
288
288
|
[:@settings, :@bundle_path, :@configured, :@definition, :@load].each do |var|
|
|
289
|
-
Bundler.instance_variable_set(var, nil)
|
|
289
|
+
::Bundler.instance_variable_set(var, nil)
|
|
290
290
|
end
|
|
291
291
|
else
|
|
292
292
|
require 'bundler'
|
|
293
293
|
end
|
|
294
|
-
Bundler.require(:default)
|
|
294
|
+
::Bundler.require(:default)
|
|
295
295
|
rescue LoadError
|
|
296
|
-
puts %
|
|
296
|
+
puts %q"Unable to load bundler - please make sure it's installed."
|
|
297
297
|
raise # rubocop:disable SignalException
|
|
298
|
-
rescue Bundler::GemNotFound => e
|
|
298
|
+
rescue ::Bundler::GemNotFound => e
|
|
299
299
|
puts "Error: #{e.message}"
|
|
300
300
|
puts 'Did you bundle install your Gemfile?'
|
|
301
301
|
raise # rubocop:disable SignalException
|
|
@@ -311,5 +311,7 @@ module GitHooks
|
|
|
311
311
|
end
|
|
312
312
|
end
|
|
313
313
|
module_function :load_tests
|
|
314
|
+
|
|
315
|
+
# rubocop:enable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
|
|
314
316
|
end
|
|
315
317
|
end
|
data/lib/githooks/section.rb
CHANGED
|
@@ -21,8 +21,6 @@ require 'delegate'
|
|
|
21
21
|
|
|
22
22
|
module GitHooks
|
|
23
23
|
class Section < DelegateClass(Array)
|
|
24
|
-
include TerminalColors
|
|
25
|
-
|
|
26
24
|
attr_reader :name, :hook, :success, :actions, :benchmark
|
|
27
25
|
alias_method :title, :name
|
|
28
26
|
alias_method :success?, :success
|
|
@@ -49,7 +47,7 @@ module GitHooks
|
|
|
49
47
|
# overrides previous action method to only return
|
|
50
48
|
# actions that have a non-empty manifest
|
|
51
49
|
def actions
|
|
52
|
-
@actions.
|
|
50
|
+
@actions.reject { |action| action.manifest.empty? }
|
|
53
51
|
end
|
|
54
52
|
alias_method :__getobj__, :actions
|
|
55
53
|
|
|
@@ -63,40 +61,36 @@ module GitHooks
|
|
|
63
61
|
end
|
|
64
62
|
|
|
65
63
|
def completed?
|
|
66
|
-
@actions.all?
|
|
64
|
+
@actions.all?(&:finished?)
|
|
67
65
|
end
|
|
68
66
|
|
|
69
67
|
def wait_count
|
|
70
|
-
@actions.select
|
|
68
|
+
@actions.select(&:waiting?).size
|
|
71
69
|
end
|
|
72
70
|
|
|
73
71
|
def name(phase = GitHooks::HOOK_NAME)
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
"#{(phase || GitHooks::HOOK_NAME).to_s.camelize} :: #{@name}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def colored_name(phase = GitHooks::HOOK_NAME)
|
|
76
|
+
status_colorize name(phase)
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
def key_name
|
|
79
80
|
self.class.key_from_name(@name)
|
|
80
81
|
end
|
|
81
82
|
|
|
82
|
-
def
|
|
83
|
-
|
|
83
|
+
def status_colorize(text)
|
|
84
|
+
return text.unknown! unless finished? && completed?
|
|
85
|
+
success? ? text.success! : text.failure!
|
|
84
86
|
end
|
|
85
87
|
|
|
86
|
-
def action(title,
|
|
87
|
-
fail ArgumentError, '
|
|
88
|
+
def action(title, &block)
|
|
89
|
+
fail ArgumentError, 'expected block, received none' unless block_given?
|
|
88
90
|
@actions << Action.new(title, self, &block)
|
|
89
91
|
self
|
|
90
92
|
end
|
|
91
93
|
|
|
92
|
-
def status_colorize(text)
|
|
93
|
-
if finished? && completed?
|
|
94
|
-
success? ? color_bright_green(text) : color_bright_red(text)
|
|
95
|
-
else
|
|
96
|
-
color_dark_cyan(text)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
94
|
def run
|
|
101
95
|
running!
|
|
102
96
|
begin
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sublime: x_syntax Packages/Ruby/Ruby.tmLanguage
|
|
2
|
+
# sublime: translate_tabs_to_spaces true; tab_size 2
|
|
3
|
+
|
|
2
4
|
require 'pathname'
|
|
3
|
-
require '
|
|
4
|
-
require 'ostruct'
|
|
5
|
+
require 'tempfile'
|
|
5
6
|
require 'shellwords'
|
|
6
7
|
|
|
7
8
|
module GitHooks
|
|
@@ -22,7 +23,7 @@ module GitHooks
|
|
|
22
23
|
end
|
|
23
24
|
module_function :find_bin
|
|
24
25
|
|
|
25
|
-
def with_path(path, &
|
|
26
|
+
def with_path(path, &_block)
|
|
26
27
|
fail ArgumentError, 'Missing required block' unless block_given?
|
|
27
28
|
begin
|
|
28
29
|
cwd = Dir.getwd
|
|
@@ -34,7 +35,7 @@ module GitHooks
|
|
|
34
35
|
end
|
|
35
36
|
module_function :with_path
|
|
36
37
|
|
|
37
|
-
def quiet(&
|
|
38
|
+
def quiet(&_block)
|
|
38
39
|
od, ov = GitHooks.debug, GitHooks.verbose
|
|
39
40
|
GitHooks.debug, GitHooks.verbose = false, false
|
|
40
41
|
yield
|
|
@@ -43,67 +44,151 @@ module GitHooks
|
|
|
43
44
|
end
|
|
44
45
|
module_function :quiet
|
|
45
46
|
|
|
47
|
+
def command(name)
|
|
48
|
+
(@commands ||= {})[name] ||= begin
|
|
49
|
+
Command.new(name).tap { |cmd|
|
|
50
|
+
define_method("command_#{cmd.name}") { |*args| cmd.execute(*args) }
|
|
51
|
+
alias_method cmd.method, "command_#{cmd.name}"
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
module_function :command
|
|
56
|
+
|
|
57
|
+
def commands(*names)
|
|
58
|
+
names.each { |name| command(name) }
|
|
59
|
+
end
|
|
60
|
+
module_function :commands
|
|
61
|
+
|
|
46
62
|
class Command
|
|
47
63
|
include Shellwords
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
ENV_WHITELIST = %w(
|
|
66
|
+
PATH HOME LDFLAGS CPPFLAGS DISPLAY EDITOR
|
|
67
|
+
LANG LC_ALL SHELL SHLVL TERM TMPDIR USER
|
|
68
|
+
SSH_USER SSH_AUTH_SOCK
|
|
69
|
+
GEM_HOME GEM_PATH MY_RUBY_HOME
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
class Result
|
|
73
|
+
attr_reader :output, :error, :status
|
|
74
|
+
def initialize(output, error, status)
|
|
75
|
+
@output = output.strip
|
|
76
|
+
@error = error.strip
|
|
77
|
+
@status = status
|
|
78
|
+
end
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
80
|
+
def output_lines(prefix = nil)
|
|
81
|
+
@output.split(/\n/).collect { |line|
|
|
82
|
+
prefix ? "#{prefix}: #{line}" : line
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def error_lines(prefix = nil)
|
|
87
|
+
@error.split(/\n/).collect { |line|
|
|
88
|
+
prefix ? "#{prefix}: #{line}" : line
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def sanitize!(*args)
|
|
93
|
+
@output.sanitize!(*args)
|
|
94
|
+
@error.sanitize!(*args)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def success?
|
|
98
|
+
status? ? @status.success? : false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def failure?
|
|
102
|
+
!success?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def status?
|
|
106
|
+
!!@status
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def exitstatus
|
|
110
|
+
status? ? @status.exitstatus : -1
|
|
111
|
+
end
|
|
112
|
+
alias_method :code, :exitstatus
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
attr_reader :run_path, :bin_path, :name
|
|
116
|
+
|
|
117
|
+
def initialize(name, options = {})
|
|
118
|
+
@bin_path = options.delete(:bin_path) || SystemUtils.which(name) || name
|
|
119
|
+
@run_path = options.delete(:chdir)
|
|
120
|
+
@name = name.to_s.gsub(/([\W-]+)/, '_')
|
|
58
121
|
end
|
|
59
122
|
|
|
60
|
-
def
|
|
61
|
-
|
|
123
|
+
def method
|
|
124
|
+
@name.to_sym
|
|
62
125
|
end
|
|
63
126
|
|
|
64
|
-
def build_command(args, options
|
|
65
|
-
|
|
127
|
+
def build_command(args, options)
|
|
128
|
+
Array(args).unshift(command_path(options))
|
|
129
|
+
end
|
|
66
130
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
131
|
+
def command_path(options = {})
|
|
132
|
+
options.delete(:use_name) ? name : bin_path.to_s
|
|
133
|
+
end
|
|
70
134
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
135
|
+
def prep_env(env = {})
|
|
136
|
+
Hash[env].each_with_object([]) do |(k, v), array|
|
|
137
|
+
array << %Q|#{k}="#{v}"| if ENV_WHITELIST.include? k
|
|
138
|
+
end.join(' ')
|
|
74
139
|
end
|
|
75
140
|
|
|
76
|
-
def execute(*args, &
|
|
77
|
-
options = args.extract_options
|
|
78
|
-
strip_empty_lines = !!options.delete(:strip_empty_lines)
|
|
141
|
+
def execute(*args, &_block) # rubocop:disable MethodLength, CyclomaticComplexity, AbcSize, PerceivedComplexity
|
|
142
|
+
options = args.extract_options!
|
|
79
143
|
|
|
80
144
|
command = build_command(args, options)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
145
|
+
command.unshift("cd #{run_path} ;") if run_path
|
|
146
|
+
command.unshift('sudo') if options.delete(:use_sudo)
|
|
147
|
+
command = Array(command.flatten.join(' '))
|
|
148
|
+
|
|
149
|
+
command.unshift options.delete(:pre_pipe) if options[:pre_pipe]
|
|
150
|
+
command.push options.delete(:post_pipe) if options[:post_pipe]
|
|
151
|
+
command = Array(command.flatten.join('|'))
|
|
152
|
+
|
|
153
|
+
command.unshift options.delete(:pre_run) if options[:pre_run]
|
|
154
|
+
command.push options.delete(:post_run) if options[:post_run]
|
|
155
|
+
command = shellwords(command.flatten.join(';'))
|
|
156
|
+
|
|
157
|
+
environment = prep_env(options.delete(:env) || ENV)
|
|
158
|
+
|
|
159
|
+
error_file = Tempfile.new('ghstderr')
|
|
160
|
+
begin
|
|
161
|
+
real_command = %Q{
|
|
162
|
+
/usr/bin/env -i #{environment} bash -c '
|
|
163
|
+
( #{command.join(' ').gsub("'", %q|'"'"'|)}) 2>#{error_file.path}
|
|
164
|
+
'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
$stderr.puts real_command if GitHooks.debug?
|
|
168
|
+
|
|
169
|
+
output = %x{ #{real_command} }
|
|
170
|
+
result = Result.new(output, error_file.read, $?)
|
|
171
|
+
|
|
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"
|
|
94
178
|
end
|
|
95
|
-
end
|
|
96
179
|
|
|
97
|
-
|
|
180
|
+
sanitize = [ :strip, :non_printable ]
|
|
181
|
+
sanitize << :colors unless options.delete(:color)
|
|
182
|
+
sanitize << :empty_lines if options.delete(:strip_empty_lines)
|
|
183
|
+
result.sanitize!(*sanitize)
|
|
98
184
|
|
|
99
|
-
|
|
185
|
+
block_given? ? yield(result) : result
|
|
186
|
+
ensure
|
|
187
|
+
error_file.close
|
|
188
|
+
error_file.unlink
|
|
189
|
+
end
|
|
100
190
|
end
|
|
101
191
|
alias_method :call, :execute
|
|
102
|
-
|
|
103
|
-
def normalize(name)
|
|
104
|
-
name.to_s.gsub(/[^a-z_]+/, '_')
|
|
105
|
-
end
|
|
106
|
-
private :normalize
|
|
107
192
|
end
|
|
108
193
|
end
|
|
109
194
|
end
|