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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f5a527f752152557546b7e9b09af917a80d2ac5
4
- data.tar.gz: 83fde6252879d9215bab34d6d35f8269b471c43c
3
+ metadata.gz: c2910eaac5a0a17a57550adbda1fb32c5663d511
4
+ data.tar.gz: beacd673a92ee04af1e4a876fc05ff83282a3115
5
5
  SHA512:
6
- metadata.gz: cb9e232c8acb6960027b60d9802e77b4242755440cec278a24f137dff0b9f77508aabf003ca222da849600fb05db8bf80408b3e4f820f7bc9a9de20445213521
7
- data.tar.gz: 4fbbcb4f21183799a1daddbed62c1a2b63bafd1d3fff591d18a168b5e8bba71d83852aedadf4d57685a236bade4569566d09db146a672a717b7e157709671aa3
6
+ metadata.gz: bdb2a859e74d8ce90bf8644f20a0409b056a35599bacb1d87b7dbab3cfd0fbe3ca793dc17023f61c6059a0d0897dfb29223ec96c57221868e84c027e72d3f973
7
+ data.tar.gz: 8e756225570a161cab9eabe5a22fbf3ed001d0d008c3e5a7058d850b905bf22c19f932f59b6b39fd36c7a183128b10cd8c7170a2e13d05ac65801ff1f26c5e44
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rabbitt-githooks (1.4.1)
4
+ rabbitt-githooks (1.5.0)
5
5
  rainbow (~> 2.0.0)
6
6
  thor (~> 0.18)
7
7
 
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
@@ -19,4 +19,5 @@ rescue GitHooks::Error => e
19
19
  puts e.message
20
20
  exit 1
21
21
  end
22
+
22
23
  exit result.to_i
@@ -34,7 +34,7 @@ module GitHooks
34
34
  @title = title
35
35
  @section = section
36
36
  @on = nil
37
- @limiters = section.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
- status_colorize title
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(&block)
155
- @on = -> { manifest.collect { |file| block.call(file) }.all? }
150
+ def on_each_file
151
+ @on = -> { manifest.collect { |file| yield file }.all? }
156
152
  end
157
153
 
158
- def on_all_files(&block)
159
- @on = -> { block.call manifest }
154
+ def on_all_files
155
+ @on = -> { yield manifest }
160
156
  end
161
157
 
162
- def on_argv(&block)
163
- @on = -> { block.call section.hook.args }
158
+ def on_argv
159
+ @on = -> { yield section.hook.args }
164
160
  end
165
161
 
166
- def on(*args, &block)
167
- @on = -> { block.call(*args) }
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
@@ -4,7 +4,7 @@ require 'githooks/repository'
4
4
  module GitHooks
5
5
  module CLI
6
6
  class Config < Thor
7
- VALID_CONFIG_OPTIONS = %w( path script pre-run-execute post-run-execute )
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(option_name) # rubocop:disable MethodLength, AbcSize
26
- unless VALID_CONFIG_OPTIONS.include? option_name
27
- puts "Invalid option '#{option_name}': expected one of #{VALID_CONFIG_OPTIONS.join(', ')}"
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
- repo_data = GitHooks::Repository::Config.new.get(
36
- option_name,
37
- repo_path: options['repo'],
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
- Array(repo_data).flatten.each do |value|
47
- value ||= 'not set'
48
- puts "#{option_name}: #{value}"
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(option_name, option_value) # rubocop:disable AbcSize
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
- GitHooks::Repository::Config.new.set(
65
- option_name,
66
- option_value,
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(option_name, option_value = nil) # rubocop:disable AbcSize
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
- GitHooks::Repository::Config.new.unset(
82
- option_name,
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'] ||= GitHooks::Repository.path
97
- config = GitHooks::Repository::Config.new
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
- data.each do |key, value|
106
- [value].flatten.each do |v|
107
- printf " %-#{key_size}s : %-#{value_size}s\n", key, v
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
- end
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']
@@ -26,6 +26,7 @@ class Array
26
26
  min
27
27
  end
28
28
  end
29
+ alias_method :min, :minimum
29
30
 
30
31
  def maximum(&_block)
31
32
  collection = block_given? ? collect { |obj| yield obj } : self
@@ -34,4 +35,5 @@ class Array
34
35
  max
35
36
  end
36
37
  end
38
+ alias_method :max, :maximum
37
39
  end
@@ -22,18 +22,18 @@ module Rainbow
22
22
  module Ext
23
23
  module String
24
24
  module InstanceMethods
25
- def success!
25
+ def color_success!
26
26
  color(:green).bright
27
27
  end
28
28
 
29
- def failure!
29
+ def color_failure!
30
30
  color(:red).bright
31
31
  end
32
32
 
33
- def unknown!
33
+ def color_unknown!
34
34
  color(:yellow).bright
35
35
  end
36
- alias_method :warning!, :unknown!
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' => { type: :path, multiple: false },
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
- unless OPTIONS.keys.include? option
50
- fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
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
- if option == 'path'
60
- new_path = Pathname.new(value)
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
- if errors.size > 0
69
- puts "Unable to change githooks path for [#{repo}]:"
70
- errors.each { |error| puts " #{error}" }
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, repo_chdir: repo, global: global)
73
+ unset(option, chdir: repo, global: global)
82
74
  end
83
75
 
84
76
  option = "githooks.#{repo}.#{option}"
85
- git(global ? '--global' : '--local', var_type, add_type, option, value, chdir: repo).tap do |result|
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) || repo_path
83
+ repo = options.delete(:repo_path) || @repository.path
92
84
  global = (opt = options.delete(:global)).nil? ? false : opt
93
- option = "githooks.#{repo}"
94
- git(global ? '--global' : '--local', '--remove-section', option, chdir: repo)
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, AbcSize
98
- unless OPTIONS.keys.include? option
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
- option = "githooks.#{repo}.#{option}"
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 ? '--global' : '--local', '--unset-all', option, chdir: repo)
98
+ git(global, '--unset-all', option, options)
111
99
  else
112
- git(global ? '--global' : '--local', '--unset', option, value_regex, chdir: repo)
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
- unless OPTIONS.keys.include? option
120
- fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
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) || 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 repo_path
136
- @repository.path
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
- args = ['config', *args].flatten
141
- @repository.git(*args)
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(path = nil) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
145
- path ||= repo_path
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
- while key_parts.size > 1 && (part = key_parts.shift)
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
- path = Pathname.new(path)
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, path.stat.mode, 0, 0, (tracked ? '^' : '?'), path.to_s)
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
- def initialize(entry)
31
- unless entry.is_a? Repository::DiffIndexEntry
32
- fail ArgumentError, "Expected a Repository::DiffIndexEntry but got a '#{entry.class.name}'"
33
- end
34
- @file = entry
35
- end
30
+ attr_reader :repo, :file
31
+
32
+ private :repo
33
+ protected :file
36
34
 
37
- def __getobj__ # rubocop:disable TrivialAccessors
38
- @file
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 path.open
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
@@ -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
- @__instance__ = {}
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
- @__mutex__.synchronize do
52
- return @__instance__[strpath] if @__instance__[strpath]
53
- @__instance__[strpath] = new(path)
54
- end
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 self.method_missing(method, *args, &block)
58
- return super unless instance.public_methods.include? method
59
- instance.public_send(method, *args, &block)
54
+ def hooks_script
55
+ config['script']
60
56
  end
61
57
 
62
- attr_reader :path, :hooks
63
- alias_method :path, :path
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( stash -q --keep-index -a)).status.success?
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 unless list.include?(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 unless list.include?(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| DiffIndexEntry.from_file_path(path, true).to_repo_file }
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| DiffIndexEntry.from_file_path(path).to_repo_file }
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
- git(*cmd.flatten.compact).output_lines.collect do |diff_data|
146
- DiffIndexEntry.new(diff_data).to_repo_file
147
- end
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(' ')}"
@@ -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') || 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
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(hook_path, options.skip_bundler)
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 #{repo}."
73
- puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks'
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(options.script || options.path).realdirpath
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 path = repository.config['path'] # rubocop:disable AssignmentInCondition
84
- fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to path #{path} - Detach to continue."
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 path = repository.config['script'] # rubocop:disable AssignmentInCondition
89
- fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to script #{path}. Detach to continue."
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
- repo.config.remove_section(repo_path: repository.path)
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(hook_path, true) }
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.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
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
- fail Error::InvalidPhase, "Hook '#{phase}' is not defined - have you registered any tests for this hook yet?"
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
- printf " %s %s\n", GitHooks::FAILURE_SYMBOL, error
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::UNKNOWN_SYMBOL
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(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
261
+ def load_tests(skip_bundler = nil)
262
+ skip_bundler = skip_bundler.nil? ? options.skip_bundler : skip_bundler
270
263
 
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')
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 = hook_data_path
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.send(:remove_instance_variable, :@settings)
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
@@ -21,14 +21,14 @@ require 'delegate'
21
21
 
22
22
  module GitHooks
23
23
  class Section < DelegateClass(Array)
24
- attr_reader :name, :hook, :success, :actions, :benchmark, :limiters
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.downcase.gsub(/[\W\s]+/, '_').to_sym
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
- status_colorize name(phase)
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
@@ -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.4.1' unless defined? VERSION
21
+ VERSION = '1.5.0' unless defined? VERSION
22
22
  end
data/lib/githooks.rb CHANGED
@@ -73,9 +73,10 @@ module GitHooks
73
73
  end
74
74
  end
75
75
 
76
- SUCCESS_SYMBOL = '✓'.success! unless defined? SUCCESS_SYMBOL
77
- FAILURE_SYMBOL = 'X'.failure! unless defined? FAILURE_SYMBOL
78
- UNKNOWN_SYMBOL = '?'.unknown! unless defined? 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.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-05-22 00:00:00.000000000 Z
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.4.3
212
+ rubygems_version: 2.2.2
212
213
  signing_key:
213
214
  specification_version: 4
214
215
  summary: framework for building git hooks tests