rabbitt-githooks 1.5.5 → 1.6.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: 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