rabbitt-githooks 1.2.7 → 1.3.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.
@@ -1,79 +1,77 @@
1
1
  require 'ostruct'
2
2
  require 'pathname'
3
+ require 'githooks/repository/file'
3
4
 
4
5
  module GitHooks
5
- class Repository::DiffIndexEntry < OpenStruct
6
- DIFF_STRUCTURE_REGEXP = %r{
7
- ^:
8
- (?<original_mode>\d+)\s
9
- (?<new_mode>\d+)\s
10
- (?<original_sha>[a-f\d]+)\s
11
- (?<new_sha>[a-f\d]+)\s
12
- (?<change_type>.)
13
- (?:(?<score>\d+)?)\s
14
- (?<file_path>\S+)\s?
15
- (?<rename_path>\S+)?
16
- }xi unless defined? DIFF_STRUCTURE_REGEXP
6
+ class Repository
7
+ class DiffIndexEntry < OpenStruct
8
+ DIFF_STRUCTURE_REGEXP = %r{
9
+ ^:
10
+ (?<original_mode>\d+)\s
11
+ (?<new_mode>\d+)\s
12
+ (?<original_sha>[a-f\d]+)\.*\s
13
+ (?<new_sha>[a-f\d]+)\.*\s
14
+ (?<change_type>.)
15
+ (?:(?<score>\d+)?)\s
16
+ (?<file_path>\S+)\s?
17
+ (?<rename_path>\S+)?
18
+ }xi unless defined? DIFF_STRUCTURE_REGEXP
17
19
 
18
- def self.from_file_path(file_path)
19
- file_path = Pathname.new(file_path)
20
- new(
21
- [
22
- 0,
23
- file_path.stat.mode.to_s(8),
24
- 0x0,
25
- 0x0,
26
- '?',
27
- file_path.to_s
28
- ].join(' ').prepend(':')
29
- )
30
- end
20
+ def self.from_file_path(path, tracked = false)
21
+ path = Pathname.new(path)
22
+ entry_line = sprintf(":%06o %06o %040x %040x %s\t%s",
23
+ 0, path.stat.mode, 0, 0, (tracked ? '^' : '?'), path.to_s)
24
+ new(entry_line)
25
+ end
31
26
 
32
- def initialize(entry)
33
- unless entry =~ DIFF_STRUCTURE_REGEXP
34
- fail ArgumentError, 'Unable to parse incoming diff entry data: #{entry}'
27
+ def initialize(entry)
28
+ unless entry =~ DIFF_STRUCTURE_REGEXP
29
+ fail ArgumentError, "Unable to parse incoming diff entry data: #{entry}"
30
+ end
31
+ super parse_data(entry)
35
32
  end
36
- super parse_data(entry)
37
- end
38
33
 
39
- def parse_data(entry) # rubocop:disable MethodLength
40
- data = Hash[DIFF_STRUCTURE_REGEXP.names.collect(&:to_sym).zip(
41
- entry.match(DIFF_STRUCTURE_REGEXP).captures
42
- )]
34
+ def parse_data(entry) # rubocop:disable MethodLength
35
+ data = Hash[
36
+ DIFF_STRUCTURE_REGEXP.names.collect(&:to_sym).zip(
37
+ entry.match(DIFF_STRUCTURE_REGEXP).captures
38
+ )
39
+ ]
43
40
 
44
- {
45
- from: FileState.new(
46
- data[:original_mode].to_i(8),
47
- data[:original_sha],
48
- data[:file_path].nil? ? nil : Pathname.new(data[:file_path])
49
- ),
50
- to: FileState.new(
51
- data[:new_mode].to_i(8),
52
- data[:new_sha],
53
- data[:rename_path].nil? ? nil : Pathname.new(data[:rename_path])
54
- ),
55
- type: Repository::CHANGE_TYPES[data[:change_type]],
56
- score: data[:score].to_i
57
- }
58
- end
41
+ {
42
+ from: FileState.new(
43
+ data[:original_mode].to_i(8),
44
+ data[:original_sha],
45
+ data[:file_path].nil? ? nil : Pathname.new(data[:file_path])
46
+ ),
47
+ to: FileState.new(
48
+ data[:new_mode].to_i(8),
49
+ data[:new_sha],
50
+ data[:rename_path].nil? ? nil : Pathname.new(data[:rename_path])
51
+ ),
52
+ type: Repository::CHANGE_TYPES[data[:change_type]],
53
+ score: data[:score].to_i
54
+ }
55
+ end
59
56
 
60
- def to_repo_file
61
- Repository::File.new(self)
62
- end
57
+ def to_repo_file
58
+ Repository::File.new(self)
59
+ end
63
60
 
64
- class FileState
65
- attr_reader :mode, :sha, :path
61
+ class FileState
62
+ attr_reader :mode, :sha, :path
66
63
 
67
- def initialize(mode, sha, path)
68
- @mode, @sha, @path = mode, sha, path
69
- end
64
+ def initialize(mode, sha, path)
65
+ @mode, @sha, @path = mode, sha, path
66
+ end
70
67
 
71
- def inspect
72
- "#<#{self.class.name.split('::').last} mode=#{mode.to_s(8)} path=#{path.to_s.inspect} sha=#{sha.inspect}>"
73
- end
68
+ def inspect
69
+ "#<#{self.class.name.split('::').last} mode=#{mode.to_s(8)} path=#{path.to_s.inspect} sha=#{sha.inspect}>"
70
+ end
74
71
 
75
- def to_path
76
- Pathname.new(@path)
72
+ def to_path
73
+ Pathname.new(@path)
74
+ end
77
75
  end
78
76
  end
79
77
  end
@@ -26,100 +26,102 @@ unless defined? DiffIndexEntryDelegateClass
26
26
  end
27
27
 
28
28
  module GitHooks
29
- class Repository::File < DiffIndexEntryDelegateClass
30
- def initialize(entry)
31
- unless entry.is_a? Repository::DiffIndexEntry
32
- fail ArgumentError, "Expected a Repository::DiffIndexEntry but got a '#{entry.class.name}'"
29
+ class Repository
30
+ class File < DiffIndexEntryDelegateClass
31
+ def initialize(entry)
32
+ unless entry.is_a? Repository::DiffIndexEntry
33
+ fail ArgumentError, "Expected a Repository::DiffIndexEntry but got a '#{entry.class.name}'"
34
+ end
35
+ @file = entry
33
36
  end
34
- @file = entry
35
- end
36
37
 
37
- def __getobj__ # rubocop:disable TrivialAccessors
38
- @file
39
- end
38
+ def __getobj__ # rubocop:disable TrivialAccessors
39
+ @file
40
+ end
40
41
 
41
- def inspect
42
- attributes = [:name, :path, :type, :mode, :sha, :score].collect do |name|
43
- "#{name}=#{attribute_value(name).inspect}"
42
+ def inspect
43
+ attributes = [:name, :path, :type, :mode, :sha, :score].collect do |name|
44
+ "#{name}=#{attribute_value(name).inspect}"
45
+ end
46
+ "#<#{self.class.name} #{attributes.join(' ')} >"
44
47
  end
45
- "#<#{self.class.name} #{attributes.join(' ')} >"
46
- end
47
48
 
48
- def path
49
- to.path || from.path
50
- end
49
+ def path
50
+ to.path || from.path
51
+ end
51
52
 
52
- def name
53
- path.basename.to_s
54
- end
53
+ def name
54
+ path.basename.to_s
55
+ end
55
56
 
56
- # rubocop:disable CyclomaticComplexity
57
- def attribute_value(attribute)
58
- case attribute
59
- when :name then name
60
- when :path then path.to_s
61
- when :type then type
62
- when :mode then to.mode
63
- when :sha then to.sha
64
- when :score then score
65
- else fail ArgumentError,
66
- "Invalid attribute type '#{attribute}' - expected: :name, :path, :type, :mode, :sha, or :score"
57
+ # rubocop:disable CyclomaticComplexity
58
+ def attribute_value(attribute)
59
+ case attribute
60
+ when :name then name
61
+ when :path then path.to_s
62
+ when :type then type
63
+ when :mode then to.mode
64
+ when :sha then to.sha
65
+ when :score then score
66
+ else fail ArgumentError,
67
+ "Invalid attribute type '#{attribute}' - expected: :name, :path, :type, :mode, :sha, or :score"
68
+ end
67
69
  end
68
- end
69
70
 
70
- def match(type, _match)
71
- value = attribute_value(type)
72
- return _match.call(value) if _match.respond_to? :call
73
-
74
- case type
75
- when :name then _match.is_a?(Regexp) ? value =~ _match : value == _match
76
- when :path then _match.is_a?(Regexp) ? value =~ _match : value == _match
77
- when :type then _match.is_a?(Array) ? _match.include?(value) : _match == value
78
- when :mode then _match & value == _match
79
- when :sha then _match == value
80
- when :score then _match == value
71
+ def match(type, selector)
72
+ value = attribute_value(type)
73
+ return selector.call(value) if selector.respond_to? :call
74
+
75
+ case type
76
+ when :name then selector.is_a?(Regexp) ? value =~ selector : value == selector
77
+ when :path then selector.is_a?(Regexp) ? value =~ selector : value == selector
78
+ when :type then [*selector].include?(:any) ? true : [*selector].include?(value)
79
+ when :mode then selector & value == selector
80
+ when :sha then selector == value
81
+ when :score then selector == value
82
+ end
81
83
  end
82
- end
83
- # rubocop:enable CyclomaticComplexity
84
+ # rubocop:enable CyclomaticComplexity
84
85
 
85
- def fd
86
- case type
87
- when :deleted, :deletion then nil
88
- else path.open
86
+ def fd
87
+ case type
88
+ when :deleted, :deletion then nil
89
+ else path.open
90
+ end
89
91
  end
90
- end
91
92
 
92
- def realpath
93
- case type
94
- when :deleted, :deletion then path
95
- else path.realpath
93
+ def realpath
94
+ case type
95
+ when :deleted, :deletion then path
96
+ else path.realpath
97
+ end
96
98
  end
97
- end
98
99
 
99
- def contains?(string_or_regexp)
100
- if string_or_regexp.is_a?(Regexp)
101
- contents =~ string_or_regexp
102
- else
103
- contents.include? string_or_regexp
100
+ def contains?(string_or_regexp)
101
+ if string_or_regexp.is_a?(Regexp)
102
+ contents =~ string_or_regexp
103
+ else
104
+ contents.include? string_or_regexp
105
+ end
104
106
  end
105
- end
106
107
 
107
- def grep(regexp)
108
- lines(true).select_with_index { |line|
109
- line =~ regexp
110
- }.collect { |num, line|
111
- [num + 1, line] # line numbers start from 1, not 0
112
- }
113
- end
108
+ def grep(regexp)
109
+ lines(true).select_with_index { |line|
110
+ line =~ regexp
111
+ }.collect { |num, line|
112
+ [num + 1, line] # line numbers start from 1, not 0
113
+ }
114
+ end
114
115
 
115
- def contents
116
- return unless fd
117
- fd.read
118
- end
116
+ def contents
117
+ return unless fd
118
+ fd.read
119
+ end
119
120
 
120
- def lines(strip_newlines = false)
121
- return [] unless fd
122
- strip_newlines ? fd.readlines.collect(&:chomp!) : fd.readlines
121
+ def lines(strip_newlines = false)
122
+ return [] unless fd
123
+ strip_newlines ? fd.readlines.collect(&:chomp!) : fd.readlines
124
+ end
123
125
  end
124
126
  end
125
127
  end
@@ -17,38 +17,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
17
17
  =end
18
18
 
19
19
  module GitHooks
20
- class Repository::Limiter
21
- attr_reader :type, :only
20
+ class Repository
21
+ class Limiter
22
+ attr_reader :type, :only
22
23
 
23
- def initialize(type, options = {})
24
- @type = type
25
- @only = options.delete(:only) || options.delete(:to)
26
- end
24
+ def initialize(type, options = {})
25
+ @type = type
26
+ @only = options.delete(:only) || options.delete(:to)
27
+ @inverted = false
28
+ end
27
29
 
28
- def only(*args)
29
- return @only if args.empty?
30
- @only = args.flatten
31
- end
32
- alias_method :to, :only
33
-
34
- def limit(files)
35
- files.select! do |file|
36
- match_file(file, @only).tap do |result|
37
- if GitHooks.debug?
38
- result = (result ? 'success' : 'failure')
39
- puts " #{file.path.to_s} (#{file.attribute_value(@type).inspect}) was a #{result}"
30
+ def only(*args)
31
+ return @only if args.empty?
32
+ @only = args.flatten
33
+ self
34
+ end
35
+ alias_method :to, :only
36
+
37
+ def inverted
38
+ @inverted = true
39
+ end
40
+ alias_method :invert, :inverted
41
+
42
+ def limit(files)
43
+ files.select! do |file|
44
+ match_file(file, @only).tap do |result|
45
+ if GitHooks.debug?
46
+ result = (result ? 'success' : 'failure')
47
+ puts " #{file.path} (#{file.attribute_value(@type).inspect}) was a #{result}"
48
+ end
40
49
  end
41
50
  end
42
51
  end
43
- end
44
52
 
45
- private
53
+ private
46
54
 
47
- def match_file(file, match_value)
48
- if match_value.is_a? Array
49
- match_value.any? { |value| file.match(@type, value) }
50
- else
51
- file.match(@type, match_value)
55
+ def match_file(file, match_value)
56
+ if @inverted
57
+ [*match_value].none? { |value| file.match(@type, value) }
58
+ else
59
+ [*match_value].any? { |value| file.match(@type, value) }
60
+ end
52
61
  end
53
62
  end
54
63
  end
@@ -49,7 +49,7 @@ module GitHooks
49
49
  end
50
50
 
51
51
  if script && !(options['ignore-script'] || GitHooks.ignore_script)
52
- command = "#{script} #{Pathname.new($0).to_s} #{Shellwords.join(ARGV)};"
52
+ command = "#{script} #{Pathname.new($0)} #{Shellwords.join(ARGV)};"
53
53
  puts "Kernel#exec(#{command.inspect})" if GitHooks.verbose
54
54
  exec(command)
55
55
  elsif libpath
@@ -106,7 +106,7 @@ module GitHooks
106
106
 
107
107
  hook_phases.each do |hook|
108
108
  hook = (repo_hooks + hook).to_s
109
- puts "Linking #{gitrunner.to_s} -> #{hook}" if GitHooks.verbose
109
+ puts "Linking #{gitrunner} -> #{hook}" if GitHooks.verbose
110
110
  FileUtils.ln_sf gitrunner.to_s, hook
111
111
  end
112
112
  end
@@ -120,10 +120,9 @@ module GitHooks
120
120
  repo = Repository.instance(repo_path)
121
121
 
122
122
  hook_phases.each do |hook|
123
- if (repo_hook = repo_hooks + hook).symlink?
124
- puts "Removing hook '#{hook}' from repository at: #{repo_path}" if GitHooks.verbose
125
- FileUtils.rm_f repo_hook
126
- end
123
+ next unless (repo_hook = (repo_hooks + hook)).symlink?
124
+ puts "Removing hook '#{hook}' from repository at: #{repo_path}" if GitHooks.verbose
125
+ FileUtils.rm_f repo_hook
127
126
  end
128
127
 
129
128
  active_hooks = Hook::VALID_PHASES.select { |hook| (repo_hooks + hook).exist? }
@@ -137,7 +136,7 @@ module GitHooks
137
136
  end
138
137
  module_function :detach
139
138
 
140
- def list(repo_path)
139
+ def list(repo_path) # rubocop:disable Style/CyclomaticComplexity, Style/MethodLength
141
140
  repo_path ||= Pathname.new(Repository.root_path)
142
141
 
143
142
  repo = Repository.instance(repo_path)
@@ -223,14 +222,15 @@ module GitHooks
223
222
  end
224
223
  module_function :run_externals
225
224
 
226
- def start(options = {}) # rubocop:disable MethodLength
225
+ def start(options = {}) # rubocop:disable Style/CyclomaticComplexity, Style/MethodLength
227
226
  phase = options[:hook] || GitHooks.hook_name || 'pre-commit'
228
227
  puts "PHASE: #{phase}" if GitHooks.debug
229
228
 
230
- if active_hook = Hook.phases[phase]
229
+ if (active_hook = Hook.phases[phase])
231
230
  active_hook.args = options.delete(:args)
232
- active_hook.unstaged = options.delete(:unstaged)
231
+ active_hook.staged = options.delete(:staged)
233
232
  active_hook.untracked = options.delete(:untracked)
233
+ active_hook.tracked = options.delete(:tracked)
234
234
  active_hook.repository_path = options.delete(:repo)
235
235
  else
236
236
  fail Error::InvalidPhase, "Hook '#{phase}' is not defined - have you registered any tests for this hook yet?"
@@ -242,10 +242,10 @@ module GitHooks
242
242
 
243
243
  sections.each do |section|
244
244
  hash_tail_length = (section_length - section.title.length)
245
- printf "===== %s %s=====\n", section.colored_name(phase), ('=' * hash_tail_length)
245
+ printf "===== %s %s===== (%ds)\n", section.colored_name(phase), ('=' * hash_tail_length), section.benchmark
246
246
 
247
247
  section.actions.each_with_index do |action, index|
248
- printf " %d. [ %s ] %s\n", (index + 1), action.state_symbol, action.colored_title
248
+ printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.state_symbol, action.colored_title, action.benchmark
249
249
 
250
250
  action.errors.each do |error|
251
251
  printf " %s %s\n", color_bright_red(MARK_FAILURE), error
@@ -270,9 +270,9 @@ module GitHooks
270
270
  end
271
271
  module_function :start
272
272
 
273
- def load_tests(path, skip_bundler = false) # rubocop:disable MethodLength
273
+ def load_tests(path, skip_bundler = false) # rubocop:disable MethodLength,Style/CyclomaticComplexity
274
274
  hooks_root = Pathname.new(path).realpath
275
- hooks_path = hooks_root + 'hooks'
275
+ hooks_path = (p = (hooks_root + 'hooks')).exist? ? p : (hooks_root + '.hooks')
276
276
  hooks_libs = hooks_root + 'libs'
277
277
  gemfile = hooks_root + 'Gemfile'
278
278
 
@@ -285,18 +285,15 @@ module GitHooks
285
285
  # stupid RVM polluting my environment without asking via it's
286
286
  # executable-hooks gem preloading bundler. hence the following ...
287
287
  if defined? Bundler
288
- [:@bundle_path, :@configured, :@definition, :@load].each do |var|
288
+ [:@settings, :@bundle_path, :@configured, :@definition, :@load].each do |var|
289
289
  Bundler.instance_variable_set(var, nil)
290
290
  end
291
- # bundler tests for @settings using defined? - which means we need
292
- # to forcibly remove it.
293
- Bundler.send(:remove_instance_variable, :@settings)
294
291
  else
295
292
  require 'bundler'
296
293
  end
297
294
  Bundler.require(:default)
298
295
  rescue LoadError
299
- puts 'Unable to load bundler - please make sure it\'s installed.'
296
+ puts %Q|Unable to load bundler - please make sure it's installed.|
300
297
  raise # rubocop:disable SignalException
301
298
  rescue Bundler::GemNotFound => e
302
299
  puts "Error: #{e.message}"
@@ -306,6 +303,7 @@ module GitHooks
306
303
  end
307
304
 
308
305
  $LOAD_PATH.unshift hooks_libs.to_s
306
+
309
307
  Dir["#{hooks_path}/**/*.rb"].each do |lib|
310
308
  lib.gsub!('.rb', '')
311
309
  puts "Loading: #{lib}" if GitHooks.verbose