rabbitt-githooks 1.4.1 → 1.5.0
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 +4 -4
- data/Gemfile.lock +1 -1
- data/TODO +19 -0
- data/bin/githooks-runner +1 -0
- data/lib/githooks/action.rb +14 -18
- data/lib/githooks/cli/config.rb +28 -41
- data/lib/githooks/cli.rb +4 -3
- data/lib/githooks/core_ext/array/min_max.rb +2 -0
- data/lib/githooks/core_ext/rainbow.rb +4 -4
- data/lib/githooks/hook.rb +1 -1
- data/lib/githooks/repository/config.rb +56 -61
- data/lib/githooks/repository/diff_index_entry.rb +8 -6
- data/lib/githooks/repository/file.rb +22 -9
- data/lib/githooks/repository.rb +29 -35
- data/lib/githooks/runner.rb +40 -40
- data/lib/githooks/section.rb +5 -8
- data/lib/githooks/version.rb +1 -1
- data/lib/githooks.rb +4 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2910eaac5a0a17a57550adbda1fb32c5663d511
|
4
|
+
data.tar.gz: beacd673a92ee04af1e4a876fc05ff83282a3115
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdb2a859e74d8ce90bf8644f20a0409b056a35599bacb1d87b7dbab3cfd0fbe3ca793dc17023f61c6059a0d0897dfb29223ec96c57221868e84c027e72d3f973
|
7
|
+
data.tar.gz: 8e756225570a161cab9eabe5a22fbf3ed001d0d008c3e5a7058d850b905bf22c19f932f59b6b39fd36c7a183128b10cd8c7170a2e13d05ac65801ff1f26c5e44
|
data/Gemfile.lock
CHANGED
data/TODO
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
* General
|
2
|
+
- allow attaching scripts (not hooks-path) to specific hook phases
|
3
|
+
- add `githooks fix-links` command to reset hook symlinks to those defined in config
|
4
|
+
|
5
|
+
* Config
|
6
|
+
- only store githooks configuration in local repository config
|
7
|
+
(means no need to store the repo path - it's implicit in the repo)
|
8
|
+
|
9
|
+
- store sub-config for each hook phase, for example:
|
10
|
+
[githooks pre-commit]
|
11
|
+
hooks-path = ...
|
12
|
+
script = ...
|
13
|
+
etc...
|
14
|
+
[githooks pre-push]
|
15
|
+
hooks-path = ...
|
16
|
+
...
|
17
|
+
|
18
|
+
* DSL
|
19
|
+
- encapsulate it into it's own class to isolate what's exposed
|
data/bin/githooks-runner
CHANGED
data/lib/githooks/action.rb
CHANGED
@@ -34,7 +34,7 @@ module GitHooks
|
|
34
34
|
@title = title
|
35
35
|
@section = section
|
36
36
|
@on = nil
|
37
|
-
@limiters =
|
37
|
+
@limiters = {}
|
38
38
|
@success = true
|
39
39
|
@errors = []
|
40
40
|
@warnings = []
|
@@ -46,11 +46,12 @@ module GitHooks
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def manifest
|
49
|
-
@manifest ||= section.hook.manifest.filter(@limiters)
|
49
|
+
@manifest ||= section.hook.manifest.filter(section.limiters.merge(@limiters))
|
50
50
|
end
|
51
51
|
|
52
52
|
def colored_title
|
53
|
-
|
53
|
+
return title.color_unknown! unless finished?
|
54
|
+
success? ? title.color_success! : title.color_failure!
|
54
55
|
end
|
55
56
|
|
56
57
|
def status_symbol
|
@@ -63,11 +64,6 @@ module GitHooks
|
|
63
64
|
define_method(:"#{method}!") { @status = method.to_sym }
|
64
65
|
end
|
65
66
|
|
66
|
-
def status_colorize(text)
|
67
|
-
return text.unknown! unless finished?
|
68
|
-
success? ? text.success! : text.failure!
|
69
|
-
end
|
70
|
-
|
71
67
|
def run # rubocop:disable MethodLength, AbcSize
|
72
68
|
running!
|
73
69
|
with_benchmark do
|
@@ -151,20 +147,20 @@ module GitHooks
|
|
151
147
|
@limiters[type]
|
152
148
|
end
|
153
149
|
|
154
|
-
def on_each_file
|
155
|
-
@on = -> { manifest.collect { |file|
|
150
|
+
def on_each_file
|
151
|
+
@on = -> { manifest.collect { |file| yield file }.all? }
|
156
152
|
end
|
157
153
|
|
158
|
-
def on_all_files
|
159
|
-
@on = -> {
|
154
|
+
def on_all_files
|
155
|
+
@on = -> { yield manifest }
|
160
156
|
end
|
161
157
|
|
162
|
-
def on_argv
|
163
|
-
@on = -> {
|
158
|
+
def on_argv
|
159
|
+
@on = -> { yield section.hook.args }
|
164
160
|
end
|
165
161
|
|
166
|
-
def on(*args
|
167
|
-
@on = -> {
|
162
|
+
def on(*args)
|
163
|
+
@on = -> { yield(*args) }
|
168
164
|
end
|
169
165
|
|
170
166
|
private
|
@@ -176,8 +172,8 @@ module GitHooks
|
|
176
172
|
}
|
177
173
|
|
178
174
|
result = command.execute(*args, &block)
|
179
|
-
result.output_lines(prefix).each { |line| puts line }
|
180
|
-
result.error_lines(prefix).each { |line| puts line }
|
175
|
+
result.output_lines(prefix).each { |line| $stdout.puts line }
|
176
|
+
result.error_lines(prefix).each { |line| $stderr.puts line }
|
181
177
|
result.status.success?
|
182
178
|
end
|
183
179
|
end
|
data/lib/githooks/cli/config.rb
CHANGED
@@ -4,7 +4,7 @@ require 'githooks/repository'
|
|
4
4
|
module GitHooks
|
5
5
|
module CLI
|
6
6
|
class Config < Thor
|
7
|
-
VALID_CONFIG_OPTIONS =
|
7
|
+
VALID_CONFIG_OPTIONS = Repository::Config::OPTIONS.keys.freeze
|
8
8
|
|
9
9
|
# class_option :verbose, type: :boolean, desc: 'verbose output', default: false
|
10
10
|
# class_option :debug, type: :boolean, desc: 'debug output', default: false
|
@@ -22,30 +22,22 @@ module GitHooks
|
|
22
22
|
}
|
23
23
|
|
24
24
|
desc :get, 'display the value for a configuration option'
|
25
|
-
def get(
|
26
|
-
unless VALID_CONFIG_OPTIONS.include?
|
27
|
-
puts "Invalid option '#{
|
25
|
+
def get(option) # rubocop:disable MethodLength, AbcSize
|
26
|
+
unless VALID_CONFIG_OPTIONS.include? option
|
27
|
+
puts "Invalid option '#{option}': expected one of #{VALID_CONFIG_OPTIONS.join(', ')}"
|
28
28
|
return 1
|
29
29
|
end
|
30
30
|
|
31
31
|
GitHooks.verbose = !!options['verbose']
|
32
32
|
GitHooks.debug = !!options['debug']
|
33
|
-
options['repo'] ||= GitHooks::Repository.path
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
global: options['global']
|
39
|
-
)
|
40
|
-
|
41
|
-
if repo_data.nil?
|
42
|
-
puts "Repository [#{options['repo']}] option '#{option_name}' is currently not set."
|
43
|
-
return
|
44
|
-
end
|
34
|
+
repository = Repository.new(options['repo'])
|
35
|
+
config_data = repository.config.get(option, global: options['global'])
|
36
|
+
config_data ||= 'not set'
|
45
37
|
|
46
|
-
|
47
|
-
|
48
|
-
puts "#{
|
38
|
+
puts "Repository [#{repository.path.basename}]"
|
39
|
+
Array(config_data).flatten.each do |value|
|
40
|
+
puts " #{option} = #{value}"
|
49
41
|
end
|
50
42
|
end
|
51
43
|
|
@@ -56,16 +48,13 @@ module GitHooks
|
|
56
48
|
desc: 'overwrite all existing values.',
|
57
49
|
default: false
|
58
50
|
}
|
59
|
-
def set(
|
51
|
+
def set(option, value) # rubocop:disable AbcSize
|
60
52
|
GitHooks.verbose = !!options['verbose']
|
61
53
|
GitHooks.debug = !!options['debug']
|
62
|
-
options['repo'] ||= GitHooks::Repository.path
|
63
54
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
repo_path: options['repo'],
|
68
|
-
global: options['global'],
|
55
|
+
Repository.new(options['repo']).config.set(
|
56
|
+
option, value,
|
57
|
+
global: options['global'],
|
69
58
|
overwrite: options['overwrite-all']
|
70
59
|
).status.success?
|
71
60
|
rescue ArgumentError => e
|
@@ -73,16 +62,12 @@ module GitHooks
|
|
73
62
|
end
|
74
63
|
|
75
64
|
desc :unset, 'Unsets a configuration value'
|
76
|
-
def unset(
|
65
|
+
def unset(option, value = nil) # rubocop:disable AbcSize
|
77
66
|
GitHooks.verbose = !!options['verbose']
|
78
67
|
GitHooks.debug = !!options['debug']
|
79
|
-
options['repo'] ||= GitHooks::Repository.path
|
80
68
|
|
81
|
-
|
82
|
-
|
83
|
-
option_value,
|
84
|
-
repo_path: options['repo'],
|
85
|
-
global: options['global']
|
69
|
+
Repository.new(options['repo']).config.unset(
|
70
|
+
option, value, global: options['global']
|
86
71
|
)
|
87
72
|
rescue ArgumentError => e
|
88
73
|
puts e.message
|
@@ -93,20 +78,22 @@ module GitHooks
|
|
93
78
|
GitHooks.verbose = !!options['verbose']
|
94
79
|
GitHooks.debug = !!options['debug']
|
95
80
|
|
96
|
-
options['repo']
|
97
|
-
|
98
|
-
|
99
|
-
githooks = config.list(global: options['global'], repo_path: options['repo'])['githooks']
|
81
|
+
repository = Repository.new(options['repo'])
|
82
|
+
githooks = repository.config.list(global: options['global'])['githooks']
|
100
83
|
return unless githooks
|
101
84
|
|
102
85
|
githooks.each do |path, data|
|
103
|
-
puts "Repository #{path}:"
|
104
86
|
key_size, value_size = data.keys.collect(&:size).maximum, data.values.collect(&:size).maximum
|
105
|
-
|
106
|
-
|
107
|
-
|
87
|
+
display_format = " %-#{key_size}s = %-#{value_size}s\n"
|
88
|
+
|
89
|
+
puts "Repository [#{File.basename(path)}]"
|
90
|
+
printf display_format, 'Repo Path', path
|
91
|
+
|
92
|
+
data.each { |key, value|
|
93
|
+
Array(value).flatten.each do |v|
|
94
|
+
printf display_format, key.tr('-', ' ').titleize, v
|
108
95
|
end
|
109
|
-
|
96
|
+
}
|
110
97
|
end
|
111
98
|
end
|
112
99
|
end
|
data/lib/githooks/cli.rb
CHANGED
@@ -25,7 +25,7 @@ module GitHooks
|
|
25
25
|
desc :attach, 'attach githooks to repository hooks'
|
26
26
|
method_option :bootstrap, type: :string, desc: 'Path to bootstrap script', default: nil
|
27
27
|
method_option :script, aliases: '-s', type: :string, desc: 'Path to script to run', default: nil
|
28
|
-
method_option :path, aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
|
28
|
+
method_option :'hooks-path', aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
|
29
29
|
method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
|
30
30
|
method_option :hooks, { # rubocop:disable BracesAroundHashParameters
|
31
31
|
type: :array,
|
@@ -37,7 +37,7 @@ module GitHooks
|
|
37
37
|
GitHooks.verbose = !!options['verbose']
|
38
38
|
GitHooks.debug = !!options['debug']
|
39
39
|
|
40
|
-
unless options['script'] || options['path']
|
40
|
+
unless options['script'] || options['hooks-path']
|
41
41
|
fail ArgumentError, %q"Neither 'path' nor 'script' were specified - please provide at least one."
|
42
42
|
end
|
43
43
|
|
@@ -79,11 +79,12 @@ module GitHooks
|
|
79
79
|
method_option :tracked, aliases: '-A', type: :boolean, desc: 'test all tracked files', default: false
|
80
80
|
method_option :untracked, aliases: '-T', type: :boolean, desc: 'test untracked files', default: false
|
81
81
|
method_option :script, aliases: '-s', type: :string, desc: 'Path to script to run', default: nil
|
82
|
-
method_option :path, aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
|
82
|
+
method_option :'hooks-path', aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
|
83
83
|
method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
|
84
84
|
method_option :'skip-pre', type: :boolean, desc: 'Skip PreRun Scripts', default: false
|
85
85
|
method_option :'skip-post', type: :boolean, desc: 'Skip PostRun Scripts', default: false
|
86
86
|
method_option :'skip-bundler', type: :boolean, desc: %q"Don't load bundler gemfile", default: false
|
87
|
+
method_option :'hook', type: :string, enum: Hook::VALID_PHASES, desc: 'Hook to run', default: 'pre-commit'
|
87
88
|
method_option :args, type: :array, desc: 'Args to pass to pre/post scripts and main testing script', default: []
|
88
89
|
def execute
|
89
90
|
GitHooks.verbose = options['verbose']
|
@@ -22,18 +22,18 @@ module Rainbow
|
|
22
22
|
module Ext
|
23
23
|
module String
|
24
24
|
module InstanceMethods
|
25
|
-
def
|
25
|
+
def color_success!
|
26
26
|
color(:green).bright
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def color_failure!
|
30
30
|
color(:red).bright
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
33
|
+
def color_unknown!
|
34
34
|
color(:yellow).bright
|
35
35
|
end
|
36
|
-
alias_method :
|
36
|
+
alias_method :color_warning!, :color_unknown!
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
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 unless defined? VALID_PHASES
|
25
|
+
VALID_PHASES = %w{ pre-commit commit-msg pre-push }.freeze unless defined? VALID_PHASES
|
26
26
|
|
27
27
|
@__phases__ = {}
|
28
28
|
@__mutex__ = Mutex.new
|
@@ -21,15 +21,11 @@ module GitHooks
|
|
21
21
|
class Repository
|
22
22
|
class Config # rubocop:disable ClassLength
|
23
23
|
OPTIONS = {
|
24
|
-
'path'
|
24
|
+
'hooks-path' => { type: :path, multiple: false },
|
25
25
|
'script' => { type: :path, multiple: false },
|
26
26
|
'pre-run-execute' => { type: :path, multiple: true },
|
27
27
|
'post-run-execute' => { type: :path, multiple: true },
|
28
|
-
}
|
29
|
-
|
30
|
-
def initialize(path = Dir.getwd)
|
31
|
-
@repository = Repository.instance(path)
|
32
|
-
end
|
28
|
+
}.freeze unless defined? OPTIONS
|
33
29
|
|
34
30
|
OPTIONS.keys.each do |name|
|
35
31
|
method_name = name.gsub(/-/, '_')
|
@@ -41,33 +37,29 @@ module GitHooks
|
|
41
37
|
EOS
|
42
38
|
end
|
43
39
|
|
40
|
+
def initialize(repository)
|
41
|
+
@repository = repository
|
42
|
+
end
|
43
|
+
|
44
44
|
def [](option)
|
45
|
-
send(option.gsub('-', '_'))
|
45
|
+
send(option.to_s.gsub('-', '_'))
|
46
46
|
end
|
47
47
|
|
48
48
|
def set(option, value, options = {}) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
repo = options.delete(:repo_path) || repo_path
|
54
|
-
global = (opt = options.delete(:global)).nil? ? false : opt
|
49
|
+
option = normalize_option(option)
|
50
|
+
repo = options.delete(:repo_path) || @repository.path
|
55
51
|
var_type = "--#{OPTIONS[option][:type]}"
|
56
52
|
add_type = OPTIONS[option][:multiple] ? '--add' : '--replace-all'
|
57
53
|
overwrite = !!options.delete(:overwrite)
|
58
54
|
|
59
|
-
|
60
|
-
|
61
|
-
errors = []
|
62
|
-
errors << 'path must be a real location' unless new_path.exist?
|
63
|
-
errors << 'path must be a directory' unless new_path.directory?
|
64
|
-
unless (new_path + 'hooks').exist? || (new_path + '.hooks').exist?
|
65
|
-
errors << 'path must have a hooks or .hooks directory in it'
|
66
|
-
end
|
55
|
+
global = (opt = options.delete(:global)).nil? ? false : opt
|
56
|
+
global = global ? '--global' : '--local'
|
67
57
|
|
68
|
-
|
69
|
-
|
70
|
-
|
58
|
+
if OPTIONS[option][:type] == :path
|
59
|
+
new_path = Pathname.new(value)
|
60
|
+
unless new_path.exist?
|
61
|
+
puts "Unable to set option option #{option} for [#{repo}]:"
|
62
|
+
puts " Path does not exist: #{new_path}"
|
71
63
|
fail ArgumentError
|
72
64
|
end
|
73
65
|
else
|
@@ -78,81 +70,84 @@ module GitHooks
|
|
78
70
|
|
79
71
|
if overwrite && !self[option].nil? && !self[option].empty?
|
80
72
|
puts "Overwrite requested for option '#{option}'" if GitHooks.verbose
|
81
|
-
unset(option,
|
73
|
+
unset(option, chdir: repo, global: global)
|
82
74
|
end
|
83
75
|
|
84
76
|
option = "githooks.#{repo}.#{option}"
|
85
|
-
git(global
|
77
|
+
git(global, var_type, add_type, option, value, chdir: repo).tap do |result|
|
86
78
|
puts "Added option #{option} with value #{value}" if result.status.success?
|
87
79
|
end
|
88
80
|
end
|
89
81
|
|
90
82
|
def remove_section(options = {})
|
91
|
-
repo = options.delete(:repo_path) ||
|
83
|
+
repo = options.delete(:repo_path) || @repository.path
|
92
84
|
global = (opt = options.delete(:global)).nil? ? false : opt
|
93
|
-
|
94
|
-
git(global
|
85
|
+
global = global ? '--global' : '--local'
|
86
|
+
git(global, '--remove-section', "githooks.#{repo}", chdir: repo)
|
95
87
|
end
|
96
88
|
|
97
|
-
def unset(option, *args) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity
|
98
|
-
|
99
|
-
fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
|
100
|
-
end
|
101
|
-
|
102
|
-
options = args.extract_options
|
103
|
-
repo = options.delete(:repo_path) || repo_path
|
89
|
+
def unset(option, *args) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity
|
90
|
+
options = args.extract_options!
|
104
91
|
global = (opt = options.delete(:global)).nil? ? false : opt
|
105
|
-
|
92
|
+
global = global ? '--global' : '--local'
|
93
|
+
option = "githooks.#{repo}.#{normalize_option(option)}"
|
106
94
|
|
107
95
|
value_regex = args.first
|
108
96
|
|
109
97
|
if options.delete(:all) || value_regex.nil?
|
110
|
-
git(global
|
98
|
+
git(global, '--unset-all', option, options)
|
111
99
|
else
|
112
|
-
git(global
|
113
|
-
end.tap do |result|
|
114
|
-
puts "Unset option #{option.git_option_path_split.last}" if result.status.success?
|
100
|
+
git(global, '--unset', option, value_regex, options)
|
115
101
|
end
|
102
|
+
|
103
|
+
result.status.success?
|
116
104
|
end
|
117
105
|
|
118
106
|
def get(option, options = {})
|
119
|
-
|
120
|
-
|
107
|
+
option = normalize_option(option)
|
108
|
+
|
109
|
+
begin
|
110
|
+
repo = options[:repo_path] || @repository.path
|
111
|
+
return unless (value = list(options)['githooks'][repo.to_s][option])
|
112
|
+
OPTIONS[option][:type] == :path ? Pathname.new(value) : value
|
113
|
+
rescue NoMethodError
|
114
|
+
nil
|
121
115
|
end
|
122
|
-
|
123
|
-
repo = options[:repo_path] || repo_path
|
124
|
-
githooks = list(options)['githooks']
|
125
|
-
|
126
|
-
githooks[repo][option] if githooks && githooks[repo] && githooks[repo][option]
|
127
116
|
end
|
128
117
|
|
129
118
|
def list(options = {})
|
130
|
-
config(options.delete(:repo_path) ||
|
119
|
+
config(chdir: options.delete(:repo_path) || options.delete(:chdir))
|
120
|
+
end
|
121
|
+
|
122
|
+
def inspect
|
123
|
+
opts = OPTIONS.keys.collect { |k| ":'#{k}'=>#{get(k).inspect}" }.join(' ')
|
124
|
+
format '<%s:0x%0x014 %s>', self.class.name, (__id__ * 2), opts
|
131
125
|
end
|
132
126
|
|
133
127
|
private
|
134
128
|
|
135
|
-
def
|
136
|
-
|
129
|
+
def normalize_option(option)
|
130
|
+
unless OPTIONS.keys.include? option
|
131
|
+
fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
|
132
|
+
end
|
133
|
+
|
134
|
+
option.to_s
|
137
135
|
end
|
138
136
|
|
139
137
|
def git(*args)
|
140
|
-
|
141
|
-
@repository.
|
138
|
+
options = args.extract_options!
|
139
|
+
args.push(options.merge(chdir: options[:repo_path] || options[:chdir] || @repository.path))
|
140
|
+
@repository.git(:config, *args)
|
142
141
|
end
|
143
142
|
|
144
|
-
def config(
|
145
|
-
|
146
|
-
|
147
|
-
raw_config = git('--list', chdir: path).output.split("\n")
|
148
|
-
raw_config.sort.uniq.each_with_object({}) do |line, hash|
|
143
|
+
def config(*args) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
|
144
|
+
raw_config = git('--list', *args).output.split("\n").sort.uniq
|
145
|
+
raw_config.each_with_object({}) do |line, hash|
|
149
146
|
key, value = line.split(/\s*=\s*/)
|
150
147
|
key_parts = key.git_option_path_split
|
151
148
|
|
152
149
|
ptr = hash[key_parts.shift] ||= {} # rubocop:disable IndentationWidth
|
153
|
-
|
154
|
-
ptr = ptr[part] ||= {} # rubocop:disable IndentationWidth
|
155
|
-
end
|
150
|
+
ptr = ptr[key_parts.shift] ||= {} until key_parts.size == 1
|
156
151
|
|
157
152
|
key = key_parts.shift
|
158
153
|
case ptr[key]
|
@@ -16,14 +16,16 @@ module GitHooks
|
|
16
16
|
(?<rename_path>\S+)?
|
17
17
|
}xi unless defined? DIFF_STRUCTURE_REGEXP
|
18
18
|
|
19
|
-
def self.from_file_path(path, tracked = false)
|
20
|
-
|
19
|
+
def self.from_file_path(repo, path, tracked = false)
|
20
|
+
relative_path = Pathname.new(path)
|
21
|
+
full_path = repo.path + relative_path
|
21
22
|
entry_line = format(":%06o %06o %040x %040x %s\t%s",
|
22
|
-
0,
|
23
|
-
new(entry_line)
|
23
|
+
0, full_path.stat.mode, 0, 0, (tracked ? '^' : '?'), relative_path.to_s)
|
24
|
+
new(repo, entry_line)
|
24
25
|
end
|
25
26
|
|
26
|
-
def initialize(entry)
|
27
|
+
def initialize(repo, entry)
|
28
|
+
@repo = repo
|
27
29
|
unless entry =~ DIFF_STRUCTURE_REGEXP
|
28
30
|
fail ArgumentError, "Unable to parse incoming diff entry data: #{entry}"
|
29
31
|
end
|
@@ -56,7 +58,7 @@ module GitHooks
|
|
56
58
|
# rubocop:enable MultilineOperationIndentation
|
57
59
|
|
58
60
|
def to_repo_file
|
59
|
-
Repository::File.new(self)
|
61
|
+
Repository::File.new(@repo, self)
|
60
62
|
end
|
61
63
|
|
62
64
|
class FileState
|
@@ -27,15 +27,16 @@ module GitHooks
|
|
27
27
|
end
|
28
28
|
|
29
29
|
class File < DiffIndexEntryDelegateClass
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@file = entry
|
35
|
-
end
|
30
|
+
attr_reader :repo, :file
|
31
|
+
|
32
|
+
private :repo
|
33
|
+
protected :file
|
36
34
|
|
37
|
-
|
38
|
-
|
35
|
+
alias_method :__getobj__, :file
|
36
|
+
|
37
|
+
def initialize(repo, entry)
|
38
|
+
@repo = repo
|
39
|
+
@file = entry
|
39
40
|
end
|
40
41
|
|
41
42
|
def inspect
|
@@ -49,6 +50,10 @@ module GitHooks
|
|
49
50
|
to.path || from.path
|
50
51
|
end
|
51
52
|
|
53
|
+
def full_path
|
54
|
+
repo.path.join(path)
|
55
|
+
end
|
56
|
+
|
52
57
|
def name
|
53
58
|
path.basename.to_s
|
54
59
|
end
|
@@ -85,7 +90,7 @@ module GitHooks
|
|
85
90
|
def fd
|
86
91
|
case type
|
87
92
|
when :deleted, :deletion then nil
|
88
|
-
else
|
93
|
+
else full_path.open
|
89
94
|
end
|
90
95
|
end
|
91
96
|
|
@@ -122,6 +127,14 @@ module GitHooks
|
|
122
127
|
strip_newlines ? fd.readlines.collect(&:chomp!) : fd.readlines
|
123
128
|
end
|
124
129
|
|
130
|
+
def eql?(other)
|
131
|
+
path.to_s == other.path.to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def hash
|
135
|
+
path.to_s.hash
|
136
|
+
end
|
137
|
+
|
125
138
|
def <=>(other)
|
126
139
|
path.to_s <=> other.path.to_s
|
127
140
|
end
|
data/lib/githooks/repository.rb
CHANGED
@@ -17,6 +17,9 @@ 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 'set'
|
21
|
+
require 'singleton'
|
22
|
+
|
20
23
|
module GitHooks
|
21
24
|
class Repository # rubocop:disable ClassLength
|
22
25
|
extend SystemUtils
|
@@ -40,36 +43,20 @@ module GitHooks
|
|
40
43
|
CHANGE_TYPES = CHANGE_TYPE_SYMBOLS.invert.freeze unless defined? CHANGE_TYPES
|
41
44
|
DEFAULT_DIFF_INDEX_OPTIONS = { staged: true } unless defined? DEFAULT_DIFF_INDEX_OPTIONS
|
42
45
|
|
43
|
-
|
44
|
-
@__mutex__ = Mutex.new
|
45
|
-
|
46
|
-
def self.instance(path = Dir.getwd)
|
47
|
-
path = Pathname.new(path).realpath
|
48
|
-
strpath = path.to_s
|
49
|
-
return @__instance__[strpath] if @__instance__[strpath]
|
46
|
+
attr_reader :path, :hooks, :config
|
50
47
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
48
|
+
def initialize(path = nil)
|
49
|
+
@path = Pathname.new(get_root_path(path || Dir.getwd))
|
50
|
+
@hooks = Pathname.new(@path).join('.git', 'hooks')
|
51
|
+
@config = Repository::Config.new(self)
|
55
52
|
end
|
56
53
|
|
57
|
-
def
|
58
|
-
|
59
|
-
instance.public_send(method, *args, &block)
|
54
|
+
def hooks_script
|
55
|
+
config['script']
|
60
56
|
end
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def initialize(path = Dir.getwd)
|
66
|
-
@path = get_root_path(path)
|
67
|
-
@hooks = Pathname.new(@path).join('.git', 'hooks')
|
68
|
-
end
|
69
|
-
protected :initialize
|
70
|
-
|
71
|
-
def config
|
72
|
-
@config ||= Repository::Config.new(path)
|
58
|
+
def hooks_path
|
59
|
+
config['hooks-path']
|
73
60
|
end
|
74
61
|
|
75
62
|
def get_root_path(path)
|
@@ -81,7 +68,7 @@ module GitHooks
|
|
81
68
|
end
|
82
69
|
|
83
70
|
def stash
|
84
|
-
git(*%w(
|
71
|
+
git(*%w(stash -q --keep-index -a)).status.success?
|
85
72
|
end
|
86
73
|
|
87
74
|
def unstash
|
@@ -96,18 +83,17 @@ module GitHooks
|
|
96
83
|
|
97
84
|
if options.delete(:tracked)
|
98
85
|
tracked_manifest(ref: ref).each_with_object(manifest_list) do |file, list|
|
99
|
-
list << file
|
86
|
+
list << file
|
100
87
|
end
|
101
88
|
end
|
102
89
|
|
103
90
|
if options.delete(:untracked)
|
104
91
|
untracked_manifest(ref: ref).each_with_object(manifest_list) do |file, list|
|
105
|
-
list << file
|
92
|
+
list << file
|
106
93
|
end
|
107
94
|
end
|
108
95
|
|
109
|
-
manifest_list.sort
|
110
|
-
manifest_list.uniq { |f| f.path.to_s }
|
96
|
+
manifest_list.sort
|
111
97
|
end
|
112
98
|
|
113
99
|
def staged_manifest(options = {})
|
@@ -121,12 +107,18 @@ module GitHooks
|
|
121
107
|
|
122
108
|
def tracked_manifest(*)
|
123
109
|
files = git('ls-files', '--exclude-standard').output.strip.split(/\s*\n\s*/)
|
124
|
-
files.collect { |path|
|
110
|
+
files.collect { |path|
|
111
|
+
next unless self.path.join(path).file?
|
112
|
+
DiffIndexEntry.from_file_path(self, path, true).to_repo_file
|
113
|
+
}.compact
|
125
114
|
end
|
126
115
|
|
127
116
|
def untracked_manifest(*)
|
128
117
|
files = git('ls-files', '--others', '--exclude-standard').output.strip.split(/\s*\n\s*/)
|
129
|
-
files.collect { |path|
|
118
|
+
files.collect { |path|
|
119
|
+
next unless self.path.join(path).file?
|
120
|
+
DiffIndexEntry.from_file_path(self, path).to_repo_file
|
121
|
+
}.compact
|
130
122
|
end
|
131
123
|
|
132
124
|
private
|
@@ -142,9 +134,11 @@ module GitHooks
|
|
142
134
|
cmd << (options.delete(:ref) || 'HEAD')
|
143
135
|
end
|
144
136
|
|
145
|
-
|
146
|
-
|
147
|
-
|
137
|
+
Set.new(
|
138
|
+
git(*cmd.flatten.compact).output_lines.collect { |diff_data|
|
139
|
+
DiffIndexEntry.new(self, diff_data).to_repo_file
|
140
|
+
}.compact
|
141
|
+
)
|
148
142
|
rescue StandardError => e
|
149
143
|
puts 'Error Encountered while acquiring manifest'
|
150
144
|
puts "Command: git #{cmd.flatten.compact.join(' ')}"
|
data/lib/githooks/runner.rb
CHANGED
@@ -32,10 +32,10 @@ module GitHooks
|
|
32
32
|
private :repository, :script, :hook_path, :repo_path, :options
|
33
33
|
|
34
34
|
def initialize(options = {}) # rubocop:disable Metrics/AbcSize
|
35
|
-
@repo_path = Pathname.new(options.delete('repo') ||
|
36
|
-
@repository = Repository.
|
37
|
-
@hook_path = acquire_hooks_path(options.delete('path') || @repository.config.
|
38
|
-
@script = options.delete('script') || @repository.
|
35
|
+
@repo_path = Pathname.new(options.delete('repo') || Dir.getwd)
|
36
|
+
@repository = Repository.new(@repo_path)
|
37
|
+
@hook_path = acquire_hooks_path(options.delete('hooks-path') || @repository.config.hooks_path || @repository.path)
|
38
|
+
@script = options.delete('script') || @repository.hooks_script
|
39
39
|
@options = IndifferentAccessOpenStruct.new(options)
|
40
40
|
|
41
41
|
GitHooks.verbose = !!ENV['GITHOOKS_VERBOSE']
|
@@ -57,8 +57,7 @@ module GitHooks
|
|
57
57
|
puts "Kernel#exec(#{command.inspect})" if GitHooks.verbose
|
58
58
|
exec(command)
|
59
59
|
elsif hook_path
|
60
|
-
load_tests
|
61
|
-
start
|
60
|
+
load_tests && start
|
62
61
|
else
|
63
62
|
puts %q"I can't figure out what to run! Specify either path or script to give me a hint..."
|
64
63
|
end
|
@@ -68,25 +67,27 @@ module GitHooks
|
|
68
67
|
else
|
69
68
|
run_externals('post-run-execute')
|
70
69
|
end
|
70
|
+
rescue SystemStackError => e
|
71
|
+
puts "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
71
72
|
rescue GitHooks::Error::NotAGitRepo => e
|
72
|
-
puts "Unable to find a valid git repo in #{
|
73
|
-
puts 'Please specify path to
|
73
|
+
puts "Unable to find a valid git repo in #{repository}."
|
74
|
+
puts 'Please specify path to repository via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
|
74
75
|
raise e
|
75
76
|
end
|
76
77
|
|
77
78
|
def attach
|
78
|
-
entry_path = Pathname.new(
|
79
|
+
entry_path = Pathname.new(script || hook_path).realdirpath
|
79
80
|
hook_phases = options.hooks || Hook::VALID_PHASES
|
80
81
|
bootstrapper = Pathname.new(options.bootstrap).realpath if options.bootstrap
|
81
82
|
|
82
83
|
if entry_path.directory?
|
83
|
-
if
|
84
|
-
fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to path #{
|
84
|
+
if repository.hooks_path # rubocop:disable AssignmentInCondition
|
85
|
+
fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to hook path #{repository.hooks_path} - Detach to continue."
|
85
86
|
end
|
86
|
-
repository.config.set('path', entry_path)
|
87
|
+
repository.config.set('hooks-path', entry_path)
|
87
88
|
elsif entry_path.executable?
|
88
|
-
if
|
89
|
-
fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to script #{
|
89
|
+
if repository.hooks_script # rubocop:disable AssignmentInCondition
|
90
|
+
fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to script #{repository.hooks_script}. Detach to continue."
|
90
91
|
end
|
91
92
|
repository.config.set('script', entry_path)
|
92
93
|
else
|
@@ -115,7 +116,7 @@ module GitHooks
|
|
115
116
|
|
116
117
|
if active_hooks.empty?
|
117
118
|
puts 'All hooks detached. Removing configuration section.'
|
118
|
-
|
119
|
+
repository.config.remove_section(repo_path: repository.path)
|
119
120
|
else
|
120
121
|
puts "Keeping configuration for active hooks: #{active_hooks.join(', ')}"
|
121
122
|
end
|
@@ -144,7 +145,7 @@ module GitHooks
|
|
144
145
|
puts " #{hook_path}"
|
145
146
|
puts
|
146
147
|
|
147
|
-
SystemUtils.quiet { load_tests(
|
148
|
+
SystemUtils.quiet { load_tests(true) }
|
148
149
|
|
149
150
|
%w{ pre-commit commit-msg }.each do |phase|
|
150
151
|
next unless Hook.phases[phase]
|
@@ -183,12 +184,7 @@ module GitHooks
|
|
183
184
|
|
184
185
|
def acquire_hooks_path(path)
|
185
186
|
path = Pathname.new(path) unless path.is_a? Pathname
|
186
|
-
path
|
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
|
187
|
+
path
|
192
188
|
end
|
193
189
|
|
194
190
|
def run_externals(which)
|
@@ -220,7 +216,8 @@ module GitHooks
|
|
220
216
|
active_hook.tracked = options.tracked
|
221
217
|
active_hook.repository_path = repository.path
|
222
218
|
else
|
223
|
-
|
219
|
+
$stderr.puts "Hook '#{phase}' not defined - skipping..." if GitHooks.verbose? || GitHooks.debug?
|
220
|
+
exit!(0) # exit quickly - no need to hold things up
|
224
221
|
end
|
225
222
|
|
226
223
|
success = active_hook.run
|
@@ -235,10 +232,14 @@ module GitHooks
|
|
235
232
|
printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.status_symbol, action.colored_title, action.benchmark
|
236
233
|
|
237
234
|
action.errors.each do |error|
|
238
|
-
|
235
|
+
if action.success?
|
236
|
+
printf " %s %s\n", GitHooks::WARNING_SYMBOL, error.color_warning!
|
237
|
+
else
|
238
|
+
printf " %s %s\n", GitHooks::FAILURE_SYMBOL, error
|
239
|
+
end
|
239
240
|
end
|
240
241
|
|
241
|
-
state_string = action.success? ? GitHooks::SUCCESS_SYMBOL : GitHooks::
|
242
|
+
state_string = action.success? ? GitHooks::SUCCESS_SYMBOL : GitHooks::WARNING_SYMBOL
|
242
243
|
|
243
244
|
action.warnings.each do |warning|
|
244
245
|
printf " %s %s\n", state_string, warning
|
@@ -257,22 +258,15 @@ module GitHooks
|
|
257
258
|
exit(success ? 0 : 1)
|
258
259
|
end
|
259
260
|
|
260
|
-
def load_tests(
|
261
|
-
|
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
|
261
|
+
def load_tests(skip_bundler = nil)
|
262
|
+
skip_bundler = skip_bundler.nil? ? options.skip_bundler : skip_bundler
|
270
263
|
|
271
|
-
|
272
|
-
|
273
|
-
|
264
|
+
hooks_path = repository.hooks_path
|
265
|
+
hooks_libs = hooks_path.join('lib')
|
266
|
+
hooks_init = (p = hooks_path.join('hooks_init.rb')).exist? ? p : hooks_path.join('githooks_init.rb')
|
267
|
+
gemfile = hooks_path.join('Gemfile')
|
274
268
|
|
275
|
-
GitHooks.hooks_root =
|
269
|
+
GitHooks.hooks_root = hooks_path
|
276
270
|
|
277
271
|
if gemfile.exist? && !(skip_bundler.nil? ? ENV.include?('GITHOOKS_SKIP_BUNDLER') : skip_bundler)
|
278
272
|
puts "loading Gemfile from: #{gemfile}" if GitHooks.verbose
|
@@ -288,7 +282,9 @@ module GitHooks
|
|
288
282
|
end
|
289
283
|
# bundler tests for @settings using defined? - which means we need
|
290
284
|
# to forcibly remove it.
|
291
|
-
Bundler.
|
285
|
+
if Bundler.instance_variables.include? :@settings
|
286
|
+
Bundler.send(:remove_instance_variable, :@settings)
|
287
|
+
end
|
292
288
|
else
|
293
289
|
require 'bundler'
|
294
290
|
end
|
@@ -305,6 +301,8 @@ module GitHooks
|
|
305
301
|
|
306
302
|
$LOAD_PATH.unshift hooks_libs.to_s
|
307
303
|
|
304
|
+
Dir.chdir repo_path
|
305
|
+
|
308
306
|
if hooks_init.exist?
|
309
307
|
puts "Loading hooks from #{hooks_init} ..." if GitHooks.verbose?
|
310
308
|
require hooks_init.sub_ext('').to_s
|
@@ -316,6 +314,8 @@ module GitHooks
|
|
316
314
|
require lib
|
317
315
|
end
|
318
316
|
end
|
317
|
+
|
318
|
+
true
|
319
319
|
end
|
320
320
|
|
321
321
|
# rubocop:enable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
|
data/lib/githooks/section.rb
CHANGED
@@ -21,14 +21,14 @@ require 'delegate'
|
|
21
21
|
|
22
22
|
module GitHooks
|
23
23
|
class Section < DelegateClass(Array)
|
24
|
-
attr_reader :name, :hook, :success, :
|
24
|
+
attr_reader :name, :hook, :success, :benchmark, :limiters
|
25
25
|
|
26
26
|
alias_method :title, :name
|
27
27
|
alias_method :success?, :success
|
28
28
|
|
29
29
|
class << self
|
30
30
|
def key_from_name(name)
|
31
|
-
name.to_s.
|
31
|
+
name.to_s.underscore.to_sym
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -74,18 +74,15 @@ module GitHooks
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def colored_name(phase = GitHooks::HOOK_NAME)
|
77
|
-
|
77
|
+
title = name(phase)
|
78
|
+
return title.color_unknown! unless finished? && completed?
|
79
|
+
success? ? title.color_success! : title.color_failure!
|
78
80
|
end
|
79
81
|
|
80
82
|
def key_name
|
81
83
|
self.class.key_from_name(@name)
|
82
84
|
end
|
83
85
|
|
84
|
-
def status_colorize(text)
|
85
|
-
return text.unknown! unless finished? && completed?
|
86
|
-
success? ? text.success! : text.failure!
|
87
|
-
end
|
88
|
-
|
89
86
|
def run
|
90
87
|
running!
|
91
88
|
begin
|
data/lib/githooks/version.rb
CHANGED
data/lib/githooks.rb
CHANGED
@@ -73,9 +73,10 @@ module GitHooks
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
SUCCESS_SYMBOL = '✓'.
|
77
|
-
FAILURE_SYMBOL = 'X'.
|
78
|
-
UNKNOWN_SYMBOL = '?'.
|
76
|
+
SUCCESS_SYMBOL = '✓'.color_success! unless defined? SUCCESS_SYMBOL
|
77
|
+
FAILURE_SYMBOL = 'X'.color_failure! unless defined? FAILURE_SYMBOL
|
78
|
+
UNKNOWN_SYMBOL = '?'.color_unknown! unless defined? UNKNOWN_SYMBOL
|
79
|
+
WARNING_SYMBOL = '!'.color_warning! unless defined? WARNING_SYMBOL
|
79
80
|
|
80
81
|
LIB_PATH = Pathname.new(__FILE__).dirname.realpath unless defined? LIB_PATH
|
81
82
|
GEM_PATH = LIB_PATH.parent unless defined? GEM_PATH
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabbitt-githooks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl P. Corliss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rainbow
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- LICENSE.txt
|
155
155
|
- README.md
|
156
156
|
- Rakefile
|
157
|
+
- TODO
|
157
158
|
- bin/githooks
|
158
159
|
- bin/githooks-runner
|
159
160
|
- lib/githooks.rb
|
@@ -208,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
209
|
version: '0'
|
209
210
|
requirements: []
|
210
211
|
rubyforge_project:
|
211
|
-
rubygems_version: 2.
|
212
|
+
rubygems_version: 2.2.2
|
212
213
|
signing_key:
|
213
214
|
specification_version: 4
|
214
215
|
summary: framework for building git hooks tests
|