rabbitt-githooks 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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