rabbitt-githooks 1.5.5 → 1.6.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: b61c53d1075587aa48903e5e8d458e64ad00e3d4
4
- data.tar.gz: 077f6f1aa451f7059bba3947647774fd06dfd772
3
+ metadata.gz: 3fc9f2e2715c624c84800b1e4acce192ec17d659
4
+ data.tar.gz: 82068a8270deeb4810e2c4f32b3f5748e9352423
5
5
  SHA512:
6
- metadata.gz: 84d194395b306f6cdca5837351f2fe750e0b85cf69562717dfec06c67e3d58d0292e93489f274ae77d75507280ffb31a13810c813d7445371ce3cba72164251f
7
- data.tar.gz: a8eede0ab599285938a71def5b385d25a3f76e7615b5164c84077b0c62bc240074fe8cce26d40564320186414d1f56dbe078b760f069375cd73cda5015776d47
6
+ metadata.gz: 665c5935ad4362aafd8ec3800dd04a0d0aeda3c030e12679a93067355c1009a5980b088ffe5593012c631094204fb22cb4d74012e5e0f71d4cc33e6a861bd31b
7
+ data.tar.gz: bc720d2df0b87b5ec92b3e5478a952bb3aa7b10f498f8607ddd987ee79642cad45c92707119d70bf1211797df5ae6025c0941097b410ed063890ff03ef9ea44e
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rabbitt-githooks (1.5.5)
4
+ rabbitt-githooks (1.6.0)
5
5
  rainbow (~> 2.0.0)
6
- thor (~> 0.18)
6
+ thor (~> 0.19.1)
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
@@ -60,3 +60,6 @@ DEPENDENCIES
60
60
  ruby-lint (~> 2.0)
61
61
  simplecov (~> 0.9)
62
62
  yard (~> 0.7)
63
+
64
+ BUNDLED WITH
65
+ 1.10.6
data/lib/githooks.rb CHANGED
@@ -35,6 +35,14 @@ module GitHooks
35
35
  class << self
36
36
  attr_reader :debug, :verbose, :ignore_script, :hooks_root
37
37
 
38
+ def quieted
39
+ od, ov = @debug, @verbose
40
+ @debug, @verbose = false, false
41
+ yield
42
+ ensure
43
+ @debug, @verbose = od, ov
44
+ end
45
+
38
46
  def debug?
39
47
  return true if ENV['GITHOOKS_DEBUG']
40
48
  return true if ARGV.include?('--debug')
@@ -133,6 +133,9 @@ module GitHooks
133
133
 
134
134
  # DSL Methods
135
135
 
136
+ # FIXME: these should be switched to behaviors that are included
137
+ # into this classs
138
+
136
139
  def skip!
137
140
  throw :skip, true
138
141
  end
data/lib/githooks/cli.rb CHANGED
@@ -75,8 +75,9 @@ module GitHooks
75
75
  # the argument list to the script
76
76
 
77
77
  desc :execute, 'Runs the selected hooks, passing the argument list to the script'
78
- method_option :staged, aliases: '-S', type: :boolean, desc: 'test staged files', default: nil
79
- method_option :tracked, aliases: '-A', type: :boolean, desc: 'test all tracked files', default: false
78
+ method_option :staged, aliases: '-S', type: :boolean, desc: 'test staged files (disabled if unstaged, tracked or untracked set)', default: true
79
+ method_option :unstaged, aliases: '-U', type: :boolean, desc: 'test unstaged files', default: false
80
+ method_option :tracked, aliases: '-A', type: :boolean, desc: 'test tracked files', default: false
80
81
  method_option :untracked, aliases: '-T', type: :boolean, desc: 'test untracked files', default: false
81
82
  method_option :script, aliases: '-s', type: :string, desc: 'Path to script to run', default: nil
82
83
  method_option :'hooks-path', aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
@@ -84,27 +85,22 @@ module GitHooks
84
85
  method_option :'skip-pre', type: :boolean, desc: 'Skip PreRun Scripts', default: false
85
86
  method_option :'skip-post', type: :boolean, desc: 'Skip PostRun Scripts', default: false
86
87
  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'
88
+ method_option :hook, type: :string, enum: Hook::VALID_PHASES, desc: 'Hook to run', default: 'pre-commit'
88
89
  method_option :args, type: :array, desc: 'Args to pass to pre/post scripts and main testing script', default: []
89
- def execute
90
+ def execute(hooks = [])
90
91
  GitHooks.verbose = options['verbose']
91
92
  GitHooks.debug = options['debug']
92
93
 
93
94
  opts = options.dup
94
- opts['staged'] ||= !(opts['tracked'] || opts['untracked'])
95
-
96
- if opts['staged']
97
- if opts['tracked']
98
- warn '--tracked conflicts with --staged. Dropping --tracked...'
99
- opts['tracked'] = false
100
- elsif opts['untracked']
101
- warn '--untracked conflicts with --staged. Dropping --untracked...'
102
- opts['untracked'] = false
103
- end
95
+
96
+ if opts['tracked'] || opts['untracked'] || opts['unstaged']
97
+ opts['staged'] = false
104
98
  end
105
99
 
106
100
  opts['skip-bundler'] ||= !!ENV['GITHOOKS_SKIP_BUNDLER']
107
101
 
102
+ opts['hook'] = hooks unless hooks.empty?
103
+
108
104
  Runner.new(opts).run
109
105
  end
110
106
 
@@ -16,9 +16,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
16
16
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
17
  =end
18
18
 
19
- =begin
20
- Mostly borrowed from Rails' ActiveSupport::Inflections
21
- =end
19
+ # Mostly borrowed from Rails' ActiveSupport::Inflections
22
20
 
23
21
  class String
24
22
  def constantize
@@ -38,7 +36,7 @@ class String
38
36
 
39
37
  def camelize!
40
38
  tap do
41
- gsub!('-', '_')
39
+ tr!('-', '_')
42
40
  sub!(/^[a-z\d]*/, &:capitalize)
43
41
  gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
44
42
  gsub!('/', '::')
@@ -79,7 +77,7 @@ class String
79
77
  def dasherize!
80
78
  tap do
81
79
  underscore!
82
- gsub!(/_/, '-')
80
+ tr!('_', '-')
83
81
  end
84
82
  end
85
83
  end
@@ -7,7 +7,7 @@ module GitHooks
7
7
  class AlreadyAttached < GitHooks::Error; end
8
8
  class NotAttached < GitHooks::Error; end
9
9
  class InvalidPhase < GitHooks::Error; end
10
-
10
+ class InvalidLimiterCallable < GitHooks::Error; end
11
11
  class RemoteNotSet < GitHooks::Error
12
12
  attr_accessor :branch
13
13
  end
data/lib/githooks/hook.rb CHANGED
@@ -117,6 +117,9 @@ module GitHooks
117
117
 
118
118
  # DSL methods
119
119
 
120
+ # FIXME: these should be switched to behaviors that are included
121
+ # into this classs
122
+
120
123
  def config_path
121
124
  GitHooks.hooks_root.join('configs')
122
125
  end
@@ -173,7 +176,7 @@ module GitHooks
173
176
  @hook.repository
174
177
  end
175
178
 
176
- def files # rubocop:disable Metrics/AbcSize
179
+ def files # rubocop:disable AbcSize,MethodLength
177
180
  @files ||= begin
178
181
  options = {
179
182
  staged: hook.staged,
@@ -183,10 +186,12 @@ module GitHooks
183
186
 
184
187
  if %w[ commit-msg pre-push ].include? hook.phase
185
188
  begin
186
- if (parent_sha = repository.last_unpushed_commit_parent)
187
- options.merge!(ref: parent_sha)
188
- end
189
+ parent_sha = repository.last_unpushed_commit_parent_sha || \
190
+ repository.branch_point_sha
191
+ options.merge!(ref: parent_sha) if parent_sha
189
192
  rescue Error::RemoteNotSet
193
+ STDERR.puts 'Couldn\'t find starting reference point for push ' \
194
+ 'manifest generation. Falling back to all tracked files.'
190
195
  # remote not set yet, so let's only focus on what's tracked for now
191
196
  options[:tracked] = true
192
197
  options[:untracked] = false
@@ -119,25 +119,53 @@ module GitHooks
119
119
  end
120
120
 
121
121
  def unpushed_commits
122
- result = git('log', '--format=%H', '@{upstream}..')
123
- if result.failure?
124
- if result.error =~ /no upstream configured for branch (["'])((?:(?!\1).)+)\1\z/i
125
- fail Error::RemoteNotSet, "No upstream remote configured for '#{$2}'"
126
- else
127
- fail Error::CommandExecutionFailure, result.error
128
- end
122
+ unless remote_branch
123
+ fail Error::RemoteNotSet, "No upstream remote configured for branch '#{current_branch}'"
129
124
  end
130
- result.output.split(/\s*\n\s*/).collect(&:strip)
125
+
126
+ git('log', '--format=%H', '@{upstream}..') do |result|
127
+ fail(Error::CommandExecutionFailure, result.error) if result.failure?
128
+ end.output.split(/\s*\n\s*/).collect(&:strip)
131
129
  end
132
130
 
133
- def revision_parent(revision)
134
- return unless (result = git('rev-parse', "#{revision}~1")).status.success?
131
+ def revision_sha(revision)
132
+ return unless (result = git('rev-parse', revision)).status.success?
135
133
  result.output.strip
136
134
  end
137
135
 
138
- def last_unpushed_commit_parent
139
- oldest_sha = unpushed_commits.last
140
- revision_parent(oldest_sha) unless oldest_sha.nil?
136
+ def current_branch
137
+ @branch ||= begin
138
+ branch = git('symbolic-ref', '--short', '--quiet', 'HEAD').output.strip
139
+ if branch.empty?
140
+ hash = git('rev-parse', 'HEAD').output.strip
141
+ branch = git('name-rev', '--name-only', hash).output.strip
142
+ end
143
+ branch
144
+ end
145
+ end
146
+
147
+ def remote_branch
148
+ result = git('rev-parse', '--symbolic-full-name', '--abbrev-ref', "#{current_branch}@{u}")
149
+ result.success? ? result.output.strip.split('/').last : nil
150
+ end
151
+
152
+ def branch_point_sha
153
+ # Try to backtrack back to where we branched from, and use that as our
154
+ # sha to compare against.
155
+
156
+ # HACK: there's a better way but, it's too late and I'm too tired to
157
+ # think of it right now.
158
+ refs = 0.upto(100).to_a.collect { |x| "#{current_branch}~#{x}" }
159
+ previous_branch = git('name-rev', '--name-only', *refs).
160
+ output_lines.find { |x| x.strip != current_branch }
161
+ revision_sha(previous_branch) if previous_branch != current_branch
162
+ end
163
+
164
+ def last_unpushed_commit_parent_sha
165
+ last_unpushed_sha = unpushed_commits.last
166
+ revision_sha("#{last_unpushed_sha}~1") unless last_unpushed_sha.nil?
167
+ rescue Error::RemoteNotSet
168
+ nil
141
169
  end
142
170
 
143
171
  private
@@ -58,8 +58,7 @@ module GitHooks
58
58
  path.basename.to_s
59
59
  end
60
60
 
61
- # rubocop:disable CyclomaticComplexity
62
- def attribute_value(attribute)
61
+ def attribute_value(attribute) # rubocop:disable Metrics/CyclomaticComplexity
63
62
  case attribute
64
63
  when :name then name
65
64
  when :path then path.to_s
@@ -72,10 +71,32 @@ module GitHooks
72
71
  end
73
72
  end
74
73
 
75
- def match(type, selector) # rubocop:disable AbcSize
74
+ def match(type, selector)
75
+ if selector.respond_to? :call
76
+ match_callable(type, selector)
77
+ else
78
+ match_type(type, selector)
79
+ end
80
+ end
81
+
82
+ # rubocop:disable ElseAlignment, IndentationWidth
83
+ def match_callable(type, selector)
76
84
  value = attribute_value(type)
77
- return selector.call(value) if selector.respond_to? :call
78
85
 
86
+ case (arity = selector.arity)
87
+ when 0 then fail ArgumentError, 'limiter recieves no parameters'
88
+ when -4..-1, 3 then selector.call(value, type, self)
89
+ when 1 then selector.call(value)
90
+ when 2 then selector.call(value, type)
91
+ else
92
+ fail ArgumentError, 'expected limiter to receive at most 3 parameters, ' \
93
+ "but it receives #{arity}"
94
+ end
95
+ end
96
+ # rubocop:enable ElseAlignment, IndentationWidth
97
+
98
+ def match_type(type, selector) # rubocop:disable AbcSize,CyclomaticComplexity
99
+ value = attribute_value(type)
79
100
  case type
80
101
  when :name then selector.is_a?(Regexp) ? value =~ selector : value == selector
81
102
  when :path then selector.is_a?(Regexp) ? value =~ selector : value == selector
@@ -85,7 +106,6 @@ module GitHooks
85
106
  when :score then selector == value
86
107
  end
87
108
  end
88
- # rubocop:enable CyclomaticComplexity
89
109
 
90
110
  def fd
91
111
  case type
@@ -21,23 +21,29 @@ module GitHooks
21
21
  class Limiter
22
22
  attr_reader :type, :only
23
23
 
24
- def initialize(type, options = {})
24
+ def initialize(type)
25
25
  @type = type
26
- @only = options.delete(:only) || options.delete(:to)
26
+ @only = nil
27
27
  @inverted = false
28
28
  end
29
29
 
30
30
  def only(*args)
31
31
  return @only if args.empty?
32
+ file, line = caller.first.split(':')[0..1]
32
33
  @only = args.flatten
34
+ @only.each do |selector|
35
+ if selector.respond_to?(:call) && selector.arity == 0
36
+ fail Error::InvalidLimiterCallable, "Bad #{@type} limiter at #{file}:#{line}; " \
37
+ 'expected callable to recieve at least one parameter but receives none.'
38
+ end
39
+ end
33
40
  self
34
41
  end
35
42
  alias_method :to, :only
36
43
 
37
- def inverted
38
- @inverted = true
44
+ def except(*args)
45
+ only(*args).tap { invert! }
39
46
  end
40
- alias_method :invert, :inverted
41
47
 
42
48
  def limit(files)
43
49
  files.select! do |file|
@@ -52,6 +58,10 @@ module GitHooks
52
58
 
53
59
  private
54
60
 
61
+ def invert!
62
+ @inverted = true
63
+ end
64
+
55
65
  def match_file(file, match_value)
56
66
  if @inverted
57
67
  [*match_value].none? { |value| file.match(@type, value) }
@@ -145,9 +145,9 @@ module GitHooks
145
145
  puts " #{hook_path}"
146
146
  puts
147
147
 
148
- SystemUtils.quiet { load_tests(true) }
148
+ GitHooks.quieted { load_tests(true) }
149
149
 
150
- %w{ pre-commit commit-msg }.each do |phase|
150
+ Hook::VALID_PHASES.each do |phase|
151
151
  next unless Hook.phases[phase]
152
152
 
153
153
  puts " Phase #{phase.camelize}:"
@@ -102,6 +102,9 @@ module GitHooks
102
102
 
103
103
  ## DSL
104
104
 
105
+ # FIXME: these should be switched to behaviors that are included
106
+ # into this classs
107
+
105
108
  def config_path
106
109
  GitHooks.hooks_root.join('configs')
107
110
  end
@@ -13,13 +13,11 @@ module GitHooks
13
13
  module_function :which
14
14
 
15
15
  def find_bin(name)
16
- # rubocop:disable MultilineBlockChain, Blocks
17
16
  ENV['PATH'].split(/:/).collect { |path|
18
17
  Pathname.new(path) + name.to_s
19
18
  }.select { |path|
20
19
  path.exist? && path.executable?
21
20
  }.collect(&:to_s)
22
- # rubocop:enable MultilineBlockChain, Blocks
23
21
  end
24
22
  module_function :find_bin
25
23
 
@@ -35,15 +33,6 @@ module GitHooks
35
33
  end
36
34
  module_function :with_path
37
35
 
38
- def quiet(&_block)
39
- od, ov = GitHooks.debug, GitHooks.verbose
40
- GitHooks.debug, GitHooks.verbose = false, false
41
- yield
42
- ensure
43
- GitHooks.debug, GitHooks.verbose = od, ov
44
- end
45
- module_function :quiet
46
-
47
36
  def command(name)
48
37
  (@commands ||= {})[name] ||= begin
49
38
  Command.new(name).tap { |cmd|
@@ -134,22 +123,32 @@ module GitHooks
134
123
  options.delete(:use_name) ? name : bin_path.to_s
135
124
  end
136
125
 
137
- def prep_env(env = ENV, options = {})
126
+ def sanitize_env(env = ENV.to_h, options = {})
138
127
  include_keys = options.delete(:include) || ENV_WHITELIST
139
128
  exclude_keys = options.delete(:exclude) || []
140
129
 
141
- if exclude_keys.size > 0 && include_keys.size > 0
142
- raise ArgumentError, "include and exclude are mutually exclusive"
130
+ unless exclude_keys.empty? ^ include_keys.empty?
131
+ fail ArgumentError, 'include and exclude are mutually exclusive'
143
132
  end
144
133
 
145
- Hash[env].each_with_object([]) do |(key, value), array|
146
- if exclude_keys.size > 0
147
- next if exclude_keys.include?(key)
148
- elsif include_keys.size > 0
149
- next unless include_keys.include?(key)
150
- end
134
+ env.to_h.reject do |key, _|
135
+ exclude_keys.include?(key) || !include_keys.include?(key)
136
+ end
137
+ end
151
138
 
152
- array << %Q'#{key}=#{value.inspect}'
139
+ def with_sanitized_env(env = {})
140
+ env ||= {}
141
+ old_env = ENV.to_h
142
+ new_env = sanitize_env(
143
+ ENV.to_h.merge(env),
144
+ include: ENV_WHITELIST | env.keys
145
+ )
146
+
147
+ begin
148
+ ENV.replace(new_env)
149
+ yield
150
+ ensure
151
+ ENV.replace(old_env)
153
152
  end
154
153
  end
155
154
 
@@ -169,10 +168,6 @@ module GitHooks
169
168
  command.push options.delete(:post_run) if options[:post_run]
170
169
  command = shellwords(command.flatten.join(';'))
171
170
 
172
- command_env = options.delete(:env) || {}
173
- whitelist_keys = ENV_WHITELIST | command_env.keys
174
- environment = prep_env(ENV.to_h.merge(command_env), include: whitelist_keys).join(' ')
175
-
176
171
  error_file = Tempfile.new('ghstderr')
177
172
 
178
173
  script_file = Tempfile.new('ghscript')
@@ -182,27 +177,25 @@ module GitHooks
182
177
  script_file.rewind
183
178
 
184
179
  begin
185
- real_command = "/usr/bin/env -i #{environment} bash #{script_file.path}"
180
+ real_command = "/usr/bin/env bash #{script_file.path}"
186
181
 
187
- if GitHooks.verbose?
188
- $stderr.puts "Command Line :\n----\n#{real_command}\n----\n"
189
- $stderr.puts "Command Script:\n----\n#{script_file.read}\n----\n"
182
+ output = with_sanitized_env(options.delete(:env)) do
183
+ %x{ #{real_command} }
190
184
  end
191
185
 
192
- output = %x{ #{real_command} }
193
186
  result = Result.new(output, error_file.read, $?)
194
187
 
195
188
  if GitHooks.verbose?
196
189
  if result.failure?
197
- STDERR.puts "Command failed with exit code [#{result.status.exitstatus}]",
198
- "ENVIRONMENT:\n\t#{environment}\n\n",
199
- "COMMAND:\n\t#{command.join(' ')}\n\n",
200
- "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
201
- "ERROR:\n-----\n#{result.error}\n-----\n\n"
190
+ STDERR.puts "---\nCommand failed with exit code [#{result.status.exitstatus}]",
191
+ "COMMAND: #{command.join(' ')}\n",
192
+ result.output.strip.empty? ? '' : "OUTPUT:\n#{result.output}\n---\n",
193
+ result.error.strip.empty? ? '' : "ERROR:\n#{result.error}\n---\n"
202
194
  else
203
- STDERR.puts "Command succeeded with exit code [#{result.status.exitstatus}]",
204
- "OUTPUT:\n-----\n#{result.output}\n-----\n\n",
205
- "ERROR:\n-----\n#{result.error}\n-----\n\n"
195
+ STDERR.puts "---\nCommand succeeded with exit code [#{result.status.exitstatus}]",
196
+ "COMMAND: #{command.join(' ')}\n",
197
+ result.output.strip.empty? ? '' : "OUTPUT:\n#{result.output}\n---\n",
198
+ result.error.strip.empty? ? '' : "ERROR:\n#{result.error}\n---\n"
206
199
  end
207
200
  end
208
201
 
@@ -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.5.5' unless defined? VERSION
21
+ VERSION = '1.6.0' unless defined? VERSION
22
22
  end
@@ -39,7 +39,7 @@ begin
39
39
  spec.extra_rdoc_files = ['README.md', 'LICENSE.txt']
40
40
 
41
41
  spec.add_dependency 'rainbow', '~> 2.0.0'
42
- spec.add_dependency 'thor', '~> 0.18'
42
+ spec.add_dependency 'thor', '~> 0.19.1'
43
43
 
44
44
  spec.add_development_dependency 'rake', '~> 10.1'
45
45
  spec.add_development_dependency 'bundler', '~> 1.3'
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.5.5
4
+ version: 1.6.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-09-25 00:00:00.000000000 Z
11
+ date: 2015-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.18'
33
+ version: 0.19.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.18'
40
+ version: 0.19.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -209,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
209
  version: '0'
210
210
  requirements: []
211
211
  rubyforge_project:
212
- rubygems_version: 2.2.2
212
+ rubygems_version: 2.4.8
213
213
  signing_key:
214
214
  specification_version: 4
215
215
  summary: framework for building git hooks tests