overcommit 0.19.0 → 0.20.0

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