overcommit 0.19.0 → 0.20.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +2 -1
  3. data/lib/overcommit/cli.rb +41 -1
  4. data/lib/overcommit/configuration_validator.rb +1 -2
  5. data/lib/overcommit/exceptions.rb +3 -0
  6. data/lib/overcommit/git_repo.rb +9 -1
  7. data/lib/overcommit/hook/base.rb +54 -32
  8. data/lib/overcommit/hook/commit_msg/text_width.rb +23 -16
  9. data/lib/overcommit/hook/pre_commit/base.rb +61 -1
  10. data/lib/overcommit/hook/pre_commit/haml_lint.rb +9 -13
  11. data/lib/overcommit/hook/pre_commit/image_optim.rb +3 -3
  12. data/lib/overcommit/hook/pre_commit/jscs.rb +5 -13
  13. data/lib/overcommit/hook/pre_commit/jsxcs.rb +5 -13
  14. data/lib/overcommit/hook/pre_commit/reek.rb +8 -15
  15. data/lib/overcommit/hook/pre_commit/rubocop.rb +9 -15
  16. data/lib/overcommit/hook/pre_commit/scss_lint.rb +9 -13
  17. data/lib/overcommit/hook_context/base.rb +3 -2
  18. data/lib/overcommit/hook_context/pre_commit.rb +11 -3
  19. data/lib/overcommit/hook_context/run_all.rb +39 -0
  20. data/lib/overcommit/hook_context.rb +2 -2
  21. data/lib/overcommit/hook_loader/base.rb +1 -3
  22. data/lib/overcommit/hook_loader/plugin_hook_loader.rb +26 -16
  23. data/lib/overcommit/hook_runner.rb +19 -9
  24. data/lib/overcommit/installer.rb +9 -5
  25. data/lib/overcommit/logger.rb +27 -0
  26. data/lib/overcommit/message_processor.rb +132 -0
  27. data/lib/overcommit/printer.rb +21 -27
  28. data/lib/overcommit/subprocess.rb +25 -16
  29. data/lib/overcommit/utils.rb +20 -0
  30. data/lib/overcommit/version.rb +1 -1
  31. data/lib/overcommit.rb +0 -1
  32. data/template-dir/hooks/commit-msg +9 -38
  33. data/template-dir/hooks/overcommit-hook +9 -38
  34. data/template-dir/hooks/post-checkout +9 -38
  35. data/template-dir/hooks/pre-commit +9 -38
  36. metadata +63 -76
  37. data/lib/overcommit/user_input.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 996dd0cae1610ccd8862512890ce60c917eb9998
4
- data.tar.gz: 8a7a2dc6f4f36ec0d650fea031bff26a963ef510
3
+ metadata.gz: dcf7ae4335496b2ce26f507167d772d304a7afd2
4
+ data.tar.gz: 25ef2ce0e6350399b0814b94b7ac78929923b2bc
5
5
  SHA512:
6
- metadata.gz: 40272621da3bd248750062ae0cb16a7c8a5e08fd3cdef1d508579ac740a4ec80a69147e121f66316f3e14c3a68297b5e572b8805fac33294e1c568814aaec3c0
7
- data.tar.gz: 2321fa13f258e8d24952655e06f9a3c450a03365759c94dc4e9b60e0a2c6b087e8b7298c168c81a38a4a6577a8f8a86cd80e2f090d8f16df5d87147d10e91fff
6
+ metadata.gz: ab8e8e06501b756741038cc6ea651d215ffd47201390dbff2351fdc0fc668d51378402cf9f08d13ba887c50fa74cfbca7cafa954d908f5ca9f6f1405adc25eed
7
+ data.tar.gz: 9043450a453504dc2a41110bd184ef5d08ec4580cac9f78f29bfa5eb8cea715f84b660e1e9e024b7cad42049d564e771f2a475c93b0f33bb5c6882b2f6a9fb9c
data/config/default.yml CHANGED
@@ -31,6 +31,7 @@ PostCheckout:
31
31
  # created.
32
32
  PreCommit:
33
33
  ALL:
34
+ problem_on_unmodified_line: warn
34
35
  requires_files: true
35
36
  required: false
36
37
  quiet: false
@@ -154,7 +155,7 @@ PreCommit:
154
155
  include: '**/*.jsx'
155
156
 
156
157
  LocalPathsInGemfile:
157
- description: 'Checking for references to local paths in your Gemfile'
158
+ description: 'Checking for local paths in Gemfile'
158
159
  include: '**/Gemfile'
159
160
 
160
161
  MergeConflicts:
@@ -18,7 +18,14 @@ module Overcommit
18
18
  install_or_uninstall
19
19
  when :template_dir
20
20
  print_template_directory_path
21
+ when :sign_plugins
22
+ sign_plugins
23
+ when :run_all
24
+ run_all
21
25
  end
26
+ rescue Overcommit::Exceptions::HookContextLoadError => ex
27
+ puts ex
28
+ exit 64 # EX_USAGE
22
29
  end
23
30
 
24
31
  private
@@ -77,9 +84,18 @@ module Overcommit
77
84
  opts.on('-f', '--force', 'Overwrite any previously installed hooks') do
78
85
  @options[:force] = true
79
86
  end
87
+
88
+ opts.on('-r', '--run', 'Run pre-commit hook against all git tracked files') do
89
+ @options[:action] = :run_all
90
+ end
80
91
  end
81
92
 
82
93
  def add_other_options(opts)
94
+ opts.on('-s', '--sign hook', 'Update plugin signatures for hook', String) do |hook|
95
+ @options[:action] = :sign_plugins
96
+ @options[:hook_to_sign] = hook
97
+ end
98
+
83
99
  opts.on('-t', '--template-dir', 'Print location of template directory') do
84
100
  @options[:action] = :template_dir
85
101
  end
@@ -152,11 +168,35 @@ module Overcommit
152
168
  if repo_config.plugin_hook?(hook_type, hook_name)
153
169
  log.warning(' (plugin)')
154
170
  else
155
- log.log
171
+ log.newline
156
172
  end
157
173
  end
158
174
  end
159
175
 
176
+ def sign_plugins
177
+ config = Overcommit::ConfigurationLoader.load_repo_config
178
+ context = Overcommit::HookContext.create(@options[:hook_to_sign],
179
+ config,
180
+ @arguments)
181
+ Overcommit::HookLoader::PluginHookLoader.new(config,
182
+ context,
183
+ log).update_signatures
184
+ halt
185
+ end
186
+
187
+ def run_all
188
+ config = Overcommit::ConfigurationLoader.load_repo_config
189
+ context = Overcommit::HookContext.create('run-all', config, @arguments)
190
+ config.apply_environment!(context, ENV)
191
+
192
+ printer = Overcommit::Printer.new(log, context)
193
+ runner = Overcommit::HookRunner.new(config, log, context, printer)
194
+
195
+ status = runner.run
196
+
197
+ halt(status ? 0 : 65)
198
+ end
199
+
160
200
  # Used for ease of stubbing in tests
161
201
  def halt(status = 0)
162
202
  exit status
@@ -26,7 +26,7 @@ module Overcommit
26
26
  # together, since it's easier to merge two hashes than to have to check if
27
27
  # one of the values is nil.
28
28
  def convert_nils_to_empty_hashes(hash)
29
- hash.inject({}) do |h, (key, value)|
29
+ hash.each_with_object({}) do |(key, value), h|
30
30
  h[key] =
31
31
  case value
32
32
  when nil then {}
@@ -34,7 +34,6 @@ module Overcommit
34
34
  else
35
35
  value
36
36
  end
37
- h
38
37
  end
39
38
  end
40
39
  end
@@ -27,6 +27,9 @@ module Overcommit::Exceptions
27
27
  # Raised when an installation target is not a valid git repository.
28
28
  class InvalidGitRepo < StandardError; end
29
29
 
30
+ # Raised when one or more hook plugin signatures have changed.
31
+ class InvalidHookSignature < StandardError; end
32
+
30
33
  # Raised when an installation target already contains non-Overcommit hooks.
31
34
  class PreExistingHooks < StandardError; end
32
35
  end
@@ -23,7 +23,6 @@ module Overcommit
23
23
 
24
24
  `git diff --no-ext-diff -U0 #{flags} -- #{file_path}`.
25
25
  scan(DIFF_HUNK_REGEX) do |start_line, lines_added|
26
-
27
26
  lines_added = (lines_added || 1).to_i # When blank, one line was added
28
27
  cur_line = start_line.to_i
29
28
 
@@ -49,6 +48,15 @@ module Overcommit
49
48
  map { |relative_file| File.expand_path(relative_file) }
50
49
  end
51
50
 
51
+ # Returns the names of all files that are tracked by git.
52
+ #
53
+ # @return [Array<String>] list of absolute file paths
54
+ def all_files
55
+ `git ls-files`.
56
+ split(/\n/).
57
+ map { |relative_file| File.expand_path(relative_file) }
58
+ end
59
+
52
60
  # Returns whether the current git branch is empty (has no commits).
53
61
  # @return [true,false]
54
62
  def initial_commit?
@@ -1,6 +1,18 @@
1
1
  require 'forwardable'
2
+ require 'overcommit/message_processor'
2
3
 
4
+ # Container for top-level hook-related classes and constants.
3
5
  module Overcommit::Hook
6
+ # Helper containing metadata about error/warning messages returned by hooks.
7
+ Message = Struct.new(:type, :file, :line, :content) do
8
+ def to_s
9
+ content
10
+ end
11
+ end
12
+
13
+ # Possible types of messages.
14
+ MESSAGE_TYPES = [:error, :warning]
15
+
4
16
  # Functionality common to all hooks.
5
17
  class Base
6
18
  extend Forwardable
@@ -30,12 +42,38 @@ module Overcommit::Hook
30
42
  if output = check_for_executable
31
43
  status = :fail
32
44
  else
33
- status, output = run
45
+ status, output = process_hook_return_value(run)
34
46
  end
35
47
 
36
48
  [transform_status(status), output]
37
49
  end
38
50
 
51
+ # Converts the hook's return value into a canonical form of a tuple
52
+ # containing status (pass/warn/fail) and output.
53
+ #
54
+ # This is intended to support various shortcuts for writing hooks so that
55
+ # hook authors don't need to work with {Overcommit::Hook::Message} objects
56
+ # for simple pass/fail hooks. It also saves you from needing to manually
57
+ # encode logic like "if there are errors, fail; if there are warnings, warn,
58
+ # otherwise pass." by simply returning an array of
59
+ # {Overcommit::Hook::Message} objects.
60
+ #
61
+ # @param hook_return_value [Symbol, Array<Symbol,String>, Array<Message>]
62
+ # @return [Array<Symbol,String>] tuple of status and output
63
+ def process_hook_return_value(hook_return_value)
64
+ if hook_return_value.is_a?(Array) &&
65
+ hook_return_value.first.is_a?(Message)
66
+ # Process messages into a status and output
67
+ Overcommit::MessageProcessor.new(
68
+ self,
69
+ @config['problem_on_unmodified_line'],
70
+ ).hook_result(hook_return_value)
71
+ else
72
+ # Otherwise return as-is
73
+ hook_return_value
74
+ end
75
+ end
76
+
39
77
  def name
40
78
  self.class.name.split('::').last
41
79
  end
@@ -62,8 +100,7 @@ module Overcommit::Hook
62
100
 
63
101
  def run?
64
102
  enabled? &&
65
- (!skip? || required?) &&
66
- !(requires_modified_files? && applicable_files.empty?)
103
+ !(@config['requires_files'] && applicable_files.empty?)
67
104
  end
68
105
 
69
106
  def in_path?(cmd)
@@ -78,10 +115,6 @@ module Overcommit::Hook
78
115
  @config['required_executable']
79
116
  end
80
117
 
81
- def install_command
82
- @config['install_command']
83
- end
84
-
85
118
  # Gets a list of staged files that apply to this hook based on its
86
119
  # configured `include` and `exclude` lists.
87
120
  def applicable_files
@@ -90,35 +123,24 @@ module Overcommit::Hook
90
123
 
91
124
  private
92
125
 
93
- def requires_modified_files?
94
- @config['requires_files']
95
- end
96
-
97
126
  def applicable_file?(file)
98
- includes = Array(@config['include']).map { |glob| convert_glob_to_absolute(glob) }
99
- included = includes.empty? ||
100
- includes.any? { |glob| matches_path?(glob, file) }
127
+ includes = Array(@config['include']).map do |glob|
128
+ Overcommit::Utils.convert_glob_to_absolute(glob)
129
+ end
101
130
 
102
- excludes = Array(@config['exclude']).map { |glob| convert_glob_to_absolute(glob) }
103
- excluded = excludes.any? { |glob| matches_path?(glob, file) }
131
+ included = includes.empty? || includes.any? do |glob|
132
+ Overcommit::Utils.matches_path?(glob, file)
133
+ end
104
134
 
105
- included && !excluded
106
- end
135
+ excludes = Array(@config['exclude']).map do |glob|
136
+ Overcommit::Utils.convert_glob_to_absolute(glob)
137
+ end
107
138
 
108
- def convert_glob_to_absolute(glob)
109
- repo_root = Overcommit::Utils.repo_root
110
- File.join(repo_root, glob)
111
- end
139
+ excluded = excludes.any? do |glob|
140
+ Overcommit::Utils.matches_path?(glob, file)
141
+ end
112
142
 
113
- # Return whether a pattern matches the given path.
114
- #
115
- # @param pattern [String]
116
- # @param path [String]
117
- def matches_path?(pattern, path)
118
- File.fnmatch?(pattern, path,
119
- File::FNM_PATHNAME | # Wildcard doesn't match separator
120
- File::FNM_DOTMATCH # Wildcards match dotfiles
121
- )
143
+ included && !excluded
122
144
  end
123
145
 
124
146
  # If the hook defines a required executable, check if it's in the path and
@@ -128,7 +150,7 @@ module Overcommit::Hook
128
150
 
129
151
  output = "'#{executable}' is not installed (or is not in your PATH)"
130
152
 
131
- if install_command
153
+ if install_command = @config['install_command']
132
154
  output += "\nInstall it by running: #{install_command}"
133
155
  end
134
156
 
@@ -3,29 +3,36 @@ module Overcommit::Hook::CommitMsg
3
3
  # under the preferred limits.
4
4
  class TextWidth < Base
5
5
  def run
6
- errors = []
6
+ @errors = []
7
7
 
8
+ find_errors_in_subject(commit_message_lines.first)
9
+ find_errors_in_body(commit_message_lines)
10
+
11
+ return :warn, @errors.join("\n") if @errors.any?
12
+
13
+ :pass
14
+ end
15
+
16
+ private
17
+
18
+ def find_errors_in_subject(subject)
8
19
  max_subject_width = config['max_subject_width']
9
- max_body_width = config['max_body_width']
20
+ return unless subject.length > max_subject_width
10
21
 
11
- if commit_message_lines.first.size > max_subject_width
12
- errors << "Please keep the subject <= #{max_subject_width} characters"
13
- end
22
+ @errors << "Please keep the subject <= #{max_subject_width} characters"
23
+ end
24
+
25
+ def find_errors_in_body(lines)
26
+ return unless lines.count > 2
27
+
28
+ max_body_width = config['max_body_width']
14
29
 
15
- if commit_message_lines.size > 2
16
- commit_message_lines[2..-1].each_with_index do |line, index|
17
- chomped = line.chomp
18
- if chomped.size > max_body_width
19
- error = "Line #{index + 3} of commit message has > " \
30
+ lines[2..-1].each_with_index do |line, index|
31
+ if line.chomp.size > max_body_width
32
+ @errors << "Line #{index + 3} of commit message has > " \
20
33
  "#{max_body_width} characters"
21
- errors << error
22
- end
23
34
  end
24
35
  end
25
-
26
- return :warn, errors.join("\n") if errors.any?
27
-
28
- :pass
29
36
  end
30
37
  end
31
38
  end
@@ -5,6 +5,66 @@ module Overcommit::Hook::PreCommit
5
5
  class Base < Overcommit::Hook::Base
6
6
  extend Forwardable
7
7
 
8
- def_delegators :@context, :modified_lines
8
+ def_delegators :@context, :modified_lines_in_file
9
+
10
+ private
11
+
12
+ # Extract file, line number, and type of message from an error/warning
13
+ # messages in output.
14
+ #
15
+ # Assumes each element of `output` is a separate error/warning with all
16
+ # information necessary to identify it.
17
+ #
18
+ # @param output_messages [Array<String>] unprocessed error/warning messages
19
+ # @param regex [Regexp] regular expression defining `file`, `line` and
20
+ # `type` capture groups used to extract file locations and error/warning
21
+ # type from each line of output
22
+ # @param type_categorizer [Proc] function executed against the `type`
23
+ # capture group to convert it to a `:warning` or `:error` symbol. Assumes
24
+ # `:error` if `nil`.
25
+ # @raise [RuntimeError] line of output did not match regex
26
+ # @return [Array<Message>]
27
+ def extract_messages(output_messages, regex, type_categorizer = nil)
28
+ output_messages.map do |message|
29
+ unless match = message.match(regex)
30
+ raise 'Unexpected output: unable to determine line number or type ' \
31
+ "of error/warning for message '#{message}'"
32
+ end
33
+
34
+ file = extract_file(match, message)
35
+ line = extract_line(match, message)
36
+ type = extract_type(match, message, type_categorizer)
37
+
38
+ Overcommit::Hook::Message.new(type, file, line, message)
39
+ end
40
+ end
41
+
42
+ def extract_file(match, message)
43
+ if match[:file].nil? || match[:file].empty?
44
+ raise "Unexpected output: no file found in '#{message}'"
45
+ end
46
+
47
+ match[:file]
48
+ end
49
+
50
+ def extract_line(match, message)
51
+ Integer(match[:line])
52
+ rescue ArgumentError, TypeError
53
+ raise "Unexpected output: invalid line number found in '#{message}'"
54
+ end
55
+
56
+ def extract_type(match, message, type_categorizer)
57
+ if type_categorizer
58
+ type_match = match.names.include?('type') ? match[:type] : nil
59
+ type = type_categorizer.call(type_match)
60
+ unless Overcommit::Hook::MESSAGE_TYPES.include?(type)
61
+ raise "Invalid message type '#{type}' for '#{message}': must " \
62
+ "be one of #{Overcommit::Hook::MESSAGE_TYPES.inspect}"
63
+ end
64
+ type
65
+ else
66
+ :error # Assume error since no categorizer was defined
67
+ end
68
+ end
9
69
  end
10
70
  end
@@ -1,23 +1,19 @@
1
1
  module Overcommit::Hook::PreCommit
2
2
  # Runs `haml-lint` against any modified HAML files.
3
3
  class HamlLint < Base
4
+ MESSAGE_TYPE_CATEGORIZER = lambda do |type|
5
+ type.include?('W') ? :warning : :error
6
+ end
7
+
4
8
  def run
5
9
  result = execute([executable] + applicable_files)
6
10
  return :pass if result.success?
7
11
 
8
- # Keep lines from the output for files that we actually modified
9
- error_lines, warning_lines = result.stdout.split("\n").partition do |output_line|
10
- if match = output_line.match(/^([^:]+):(\d+)/)
11
- file = match[1]
12
- line = match[2]
13
- end
14
- modified_lines(file).include?(line.to_i)
15
- end
16
-
17
- return :fail, error_lines.join("\n") unless error_lines.empty?
18
-
19
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
20
- warning_lines.join("\n")]
12
+ extract_messages(
13
+ result.stdout.split("\n"),
14
+ /^(?<file>[^:]+):(?<line>\d+)[^ ]* (?<type>[^ ]+)/,
15
+ MESSAGE_TYPE_CATEGORIZER,
16
+ )
21
17
  end
22
18
  end
23
19
  end
@@ -19,7 +19,7 @@ module Overcommit::Hook::PreCommit
19
19
  return :fail,
20
20
  "The following images are optimizable:\n#{optimized_images.join("\n")}" \
21
21
  "\n\nOptimize them by running:\n" \
22
- " image_optim --no-pngout #{optimized_images.join(' ')}"
22
+ " image_optim --skip-missing-workers #{optimized_images.join(' ')}"
23
23
  end
24
24
 
25
25
  :pass
@@ -28,10 +28,10 @@ module Overcommit::Hook::PreCommit
28
28
  private
29
29
 
30
30
  def optimize_images(image_paths)
31
- image_optim = ::ImageOptim.new(:pngout => false)
31
+ image_optim = ::ImageOptim.new(skip_missing_workers: true)
32
32
 
33
33
  optimized_images =
34
- image_optim.optimize_images!(image_paths) do |path, optimized|
34
+ image_optim.optimize_images(image_paths) do |path, optimized|
35
35
  path if optimized
36
36
  end
37
37
 
@@ -7,22 +7,14 @@ module Overcommit::Hook::PreCommit
7
7
  return :pass if result.success?
8
8
 
9
9
  if result.status == 1
10
+ # No configuration was found
10
11
  return :warn, result.stderr.chomp
11
12
  end
12
13
 
13
- # Keep lines from the output for files that we actually modified
14
- error_lines, warning_lines = result.stdout.split("\n").partition do |output_line|
15
- if match = output_line.match(/^([^:]+):[^\d]+(\d+)/)
16
- file = match[1]
17
- line = match[2]
18
- end
19
- modified_lines(file).include?(line.to_i)
20
- end
21
-
22
- return :fail, error_lines.join("\n") unless error_lines.empty?
23
-
24
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
25
- warning_lines.join("\n")]
14
+ extract_messages(
15
+ result.stdout.split("\n"),
16
+ /^(?<file>[^:]+):[^\d]+(?<line>\d+)/,
17
+ )
26
18
  end
27
19
  end
28
20
  end
@@ -7,22 +7,14 @@ module Overcommit::Hook::PreCommit
7
7
  return :pass if result.success?
8
8
 
9
9
  if result.status == 1
10
+ # No configuration found
10
11
  return :warn, result.stderr.chomp
11
12
  end
12
13
 
13
- # Keep lines from the output for files that we actually modified
14
- error_lines, warning_lines = result.stdout.split("\n").partition do |output_line|
15
- if match = output_line.match(/^([^:]+):[^\d]+(\d+)/)
16
- file = match[1]
17
- line = match[2]
18
- end
19
- modified_lines(file).include?(line.to_i)
20
- end
21
-
22
- return :fail, error_lines.join("\n") unless error_lines.empty?
23
-
24
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
25
- warning_lines.join("\n")]
14
+ extract_messages(
15
+ result.stdout.split("\n"),
16
+ /^(?<file>[^:]+):[^\d]+(?<line>\d+)/,
17
+ )
26
18
  end
27
19
  end
28
20
  end
@@ -5,24 +5,17 @@ module Overcommit::Hook::PreCommit
5
5
  result = execute(%W[#{executable} --single-line --no-color] + applicable_files)
6
6
  return :pass if result.success?
7
7
 
8
- output = cleanup_output(result.stdout + result.stderr)
8
+ output = scrub_output(result.stdout + result.stderr)
9
9
 
10
- # Keep lines from the output for files that we actually modified
11
- error_lines, warning_lines = output.partition do |output_line|
12
- if (match = output_line.match(/^([^:]+):(\d+)/))
13
- file = match[1]
14
- line = match[2]
15
- end
16
- modified_lines(file).include?(line.to_i)
17
- end
18
-
19
- return :fail, error_lines.join("\n") unless error_lines.empty?
20
-
21
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
22
- warning_lines.join("\n")]
10
+ extract_messages(
11
+ output,
12
+ /^(?<file>[^:]+):(?<line>\d+):/,
13
+ )
23
14
  end
24
15
 
25
- def cleanup_output(raw_output)
16
+ private
17
+
18
+ def scrub_output(raw_output)
26
19
  raw_output.split("\n").grep(/^(.(?!warning))*$/)
27
20
  end
28
21
  end
@@ -1,25 +1,19 @@
1
1
  module Overcommit::Hook::PreCommit
2
2
  # Runs `rubocop` against any modified Ruby files.
3
3
  class Rubocop < Base
4
+ MESSAGE_TYPE_CATEGORIZER = lambda do |type|
5
+ type.include?('W') ? :warning : :error
6
+ end
7
+
4
8
  def run
5
9
  result = execute(%W[#{executable} --format=emacs --force-exclusion] + applicable_files)
6
10
  return :pass if result.success?
7
11
 
8
- output = result.stdout + result.stderr
9
-
10
- # Keep lines from the output for files that we actually modified
11
- error_lines, warning_lines = output.split("\n").partition do |output_line|
12
- if match = output_line.match(/^([^:]+):(\d+)/)
13
- file = match[1]
14
- line = match[2]
15
- end
16
- modified_lines(file).include?(line.to_i)
17
- end
18
-
19
- return :fail, error_lines.join("\n") unless error_lines.empty?
20
-
21
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
22
- warning_lines.join("\n")]
12
+ extract_messages(
13
+ result.stdout.split("\n"),
14
+ /^(?<file>[^:]+):(?<line>\d+):[^ ]+ (?<type>[^ ]+)/,
15
+ MESSAGE_TYPE_CATEGORIZER,
16
+ )
23
17
  end
24
18
  end
25
19
  end
@@ -1,23 +1,19 @@
1
1
  module Overcommit::Hook::PreCommit
2
2
  # Runs `scss-lint` against any modified SCSS files.
3
3
  class ScssLint < Base
4
+ MESSAGE_TYPE_CATEGORIZER = lambda do |type|
5
+ type.include?('W') ? :warning : :error
6
+ end
7
+
4
8
  def run
5
9
  result = execute([executable] + applicable_files)
6
10
  return :pass if result.success?
7
11
 
8
- # Keep lines from the output for files that we actually modified
9
- error_lines, warning_lines = result.stdout.split("\n").partition do |output_line|
10
- if match = output_line.match(/^([^:]+):(\d+)/)
11
- file = match[1]
12
- line = match[2]
13
- end
14
- modified_lines(file).include?(line.to_i)
15
- end
16
-
17
- return :fail, error_lines.join("\n") unless error_lines.empty?
18
-
19
- [:warn, "Modified files have lints (on lines you didn't modify)\n" <<
20
- warning_lines.join("\n")]
12
+ extract_messages(
13
+ result.stdout.split("\n"),
14
+ /^(?<file>[^:]+):(?<line>\d+)[^ ]* (?<type>[^ ]+)/,
15
+ MESSAGE_TYPE_CATEGORIZER,
16
+ )
21
17
  end
22
18
  end
23
19
  end
@@ -11,10 +11,11 @@ module Overcommit::HookContext
11
11
  # calculations can be memoized across all hooks in a single object, which
12
12
  # helps with performance.
13
13
  class Base
14
- def initialize(config, args, input)
14
+ # @param config [Overcommit::Configuration]
15
+ # @param args [Array<String>]
16
+ def initialize(config, args)
15
17
  @config = config
16
18
  @args = args
17
- @input = input
18
19
  end
19
20
 
20
21
  # Returns the camel-cased type of this hook (e.g. PreCommit)
@@ -64,14 +64,22 @@ module Overcommit::HookContext
64
64
  # Get a list of added, copied, or modified files that have been staged.
65
65
  # Renames and deletions are ignored, since there should be nothing to check.
66
66
  def modified_files
67
- @modified_files ||= Overcommit::GitRepo.modified_files(:staged => true)
67
+ @modified_files ||= Overcommit::GitRepo.modified_files(staged: true)
68
+ end
69
+
70
+ # @deprecated
71
+ # TODO: Remove this once we've moved all existing hooks to stop using this
72
+ # endpoint
73
+ def modified_lines(file)
74
+ modified_lines_in_file(file)
68
75
  end
69
76
 
70
77
  # Returns the set of line numbers corresponding to the lines that were
71
78
  # changed in a specified file.
72
- def modified_lines(file)
79
+ def modified_lines_in_file(file)
73
80
  @modified_lines ||= {}
74
- @modified_lines[file] ||= Overcommit::GitRepo.extract_modified_lines(file, :staged => true)
81
+ @modified_lines[file] ||=
82
+ Overcommit::GitRepo.extract_modified_lines(file, staged: true)
75
83
  end
76
84
 
77
85
  private