jawshooah-overcommit 0.22.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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/bin/overcommit +8 -0
  3. data/config/default.yml +275 -0
  4. data/config/starter.yml +31 -0
  5. data/lib/overcommit.rb +20 -0
  6. data/lib/overcommit/cli.rb +205 -0
  7. data/lib/overcommit/configuration.rb +183 -0
  8. data/lib/overcommit/configuration_loader.rb +49 -0
  9. data/lib/overcommit/configuration_validator.rb +40 -0
  10. data/lib/overcommit/constants.rb +8 -0
  11. data/lib/overcommit/exceptions.rb +35 -0
  12. data/lib/overcommit/git_repo.rb +147 -0
  13. data/lib/overcommit/hook/base.rb +174 -0
  14. data/lib/overcommit/hook/commit_msg/base.rb +11 -0
  15. data/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +18 -0
  16. data/lib/overcommit/hook/commit_msg/hard_tabs.rb +13 -0
  17. data/lib/overcommit/hook/commit_msg/russian_novel.rb +14 -0
  18. data/lib/overcommit/hook/commit_msg/single_line_subject.rb +12 -0
  19. data/lib/overcommit/hook/commit_msg/text_width.rb +38 -0
  20. data/lib/overcommit/hook/commit_msg/trailing_period.rb +12 -0
  21. data/lib/overcommit/hook/post_checkout/base.rb +11 -0
  22. data/lib/overcommit/hook/post_checkout/index_tags.rb +26 -0
  23. data/lib/overcommit/hook/post_commit/base.rb +11 -0
  24. data/lib/overcommit/hook/post_commit/git_guilt.rb +9 -0
  25. data/lib/overcommit/hook/pre_commit/author_email.rb +18 -0
  26. data/lib/overcommit/hook/pre_commit/author_name.rb +17 -0
  27. data/lib/overcommit/hook/pre_commit/base.rb +70 -0
  28. data/lib/overcommit/hook/pre_commit/berksfile_check.rb +20 -0
  29. data/lib/overcommit/hook/pre_commit/brakeman.rb +12 -0
  30. data/lib/overcommit/hook/pre_commit/broken_symlinks.rb +15 -0
  31. data/lib/overcommit/hook/pre_commit/bundle_check.rb +25 -0
  32. data/lib/overcommit/hook/pre_commit/chamber_security.rb +11 -0
  33. data/lib/overcommit/hook/pre_commit/coffee_lint.rb +11 -0
  34. data/lib/overcommit/hook/pre_commit/css_lint.rb +11 -0
  35. data/lib/overcommit/hook/pre_commit/go_lint.rb +12 -0
  36. data/lib/overcommit/hook/pre_commit/haml_lint.rb +19 -0
  37. data/lib/overcommit/hook/pre_commit/hard_tabs.rb +14 -0
  38. data/lib/overcommit/hook/pre_commit/image_optim.rb +41 -0
  39. data/lib/overcommit/hook/pre_commit/js_hint.rb +13 -0
  40. data/lib/overcommit/hook/pre_commit/jscs.rb +22 -0
  41. data/lib/overcommit/hook/pre_commit/json_syntax.rb +22 -0
  42. data/lib/overcommit/hook/pre_commit/jsx_hint.rb +13 -0
  43. data/lib/overcommit/hook/pre_commit/jsxcs.rb +20 -0
  44. data/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb +14 -0
  45. data/lib/overcommit/hook/pre_commit/merge_conflicts.rb +14 -0
  46. data/lib/overcommit/hook/pre_commit/pry_binding.rb +14 -0
  47. data/lib/overcommit/hook/pre_commit/python_flake8.rb +11 -0
  48. data/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb +45 -0
  49. data/lib/overcommit/hook/pre_commit/reek.rb +22 -0
  50. data/lib/overcommit/hook/pre_commit/rubocop.rb +19 -0
  51. data/lib/overcommit/hook/pre_commit/scss_lint.rb +19 -0
  52. data/lib/overcommit/hook/pre_commit/shell_check.rb +19 -0
  53. data/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +13 -0
  54. data/lib/overcommit/hook/pre_commit/travis_lint.rb +11 -0
  55. data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +22 -0
  56. data/lib/overcommit/hook_context.rb +17 -0
  57. data/lib/overcommit/hook_context/base.rb +69 -0
  58. data/lib/overcommit/hook_context/commit_msg.rb +32 -0
  59. data/lib/overcommit/hook_context/post_checkout.rb +26 -0
  60. data/lib/overcommit/hook_context/post_commit.rb +19 -0
  61. data/lib/overcommit/hook_context/pre_commit.rb +148 -0
  62. data/lib/overcommit/hook_context/run_all.rb +39 -0
  63. data/lib/overcommit/hook_loader/base.rb +36 -0
  64. data/lib/overcommit/hook_loader/built_in_hook_loader.rb +12 -0
  65. data/lib/overcommit/hook_loader/plugin_hook_loader.rb +61 -0
  66. data/lib/overcommit/hook_runner.rb +129 -0
  67. data/lib/overcommit/hook_signer.rb +79 -0
  68. data/lib/overcommit/installer.rb +148 -0
  69. data/lib/overcommit/interrupt_handler.rb +87 -0
  70. data/lib/overcommit/logger.rb +79 -0
  71. data/lib/overcommit/message_processor.rb +132 -0
  72. data/lib/overcommit/printer.rb +116 -0
  73. data/lib/overcommit/subprocess.rb +46 -0
  74. data/lib/overcommit/utils.rb +163 -0
  75. data/lib/overcommit/version.rb +4 -0
  76. data/libexec/gerrit-change-id +174 -0
  77. data/libexec/index-tags +17 -0
  78. data/template-dir/hooks/commit-msg +81 -0
  79. data/template-dir/hooks/overcommit-hook +81 -0
  80. data/template-dir/hooks/post-checkout +81 -0
  81. data/template-dir/hooks/pre-commit +81 -0
  82. metadata +184 -0
@@ -0,0 +1,174 @@
1
+ require 'forwardable'
2
+ require 'overcommit/message_processor'
3
+
4
+ # Container for top-level hook-related classes and constants.
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
+
16
+ # Functionality common to all hooks.
17
+ class Base
18
+ extend Forwardable
19
+
20
+ def_delegators :@context, :modified_files
21
+ attr_reader :config
22
+
23
+ # @param config [Overcommit::Configuration]
24
+ # @param context [Overcommit::HookContext]
25
+ def initialize(config, context)
26
+ @config = config.for_hook(self)
27
+ @context = context
28
+ end
29
+
30
+ # Runs the hook.
31
+ def run
32
+ raise NotImplementedError, 'Hook must define `run`'
33
+ end
34
+
35
+ # Runs the hook and transforms the status returned based on the hook's
36
+ # configuration.
37
+ #
38
+ # Poorly named because we already have a bunch of hooks in the wild that
39
+ # implement `#run`, and we needed a wrapper step to transform the status
40
+ # based on any custom configuration.
41
+ def run_and_transform
42
+ if output = check_for_executable
43
+ status = :fail
44
+ else
45
+ status, output = process_hook_return_value(run)
46
+ end
47
+
48
+ [transform_status(status), output]
49
+ end
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
+
77
+ def name
78
+ self.class.name.split('::').last
79
+ end
80
+
81
+ def description
82
+ @config['description'] || "Running #{name}"
83
+ end
84
+
85
+ def required?
86
+ @config['required']
87
+ end
88
+
89
+ def quiet?
90
+ @config['quiet']
91
+ end
92
+
93
+ def enabled?
94
+ @config['enabled'] != false
95
+ end
96
+
97
+ def skip?
98
+ @config['skip']
99
+ end
100
+
101
+ def run?
102
+ enabled? &&
103
+ !(@config['requires_files'] && applicable_files.empty?)
104
+ end
105
+
106
+ def in_path?(cmd)
107
+ Overcommit::Utils.in_path?(cmd)
108
+ end
109
+
110
+ def execute(cmd)
111
+ Overcommit::Utils.execute(cmd)
112
+ end
113
+
114
+ def executable
115
+ @config['required_executable']
116
+ end
117
+
118
+ # Gets a list of staged files that apply to this hook based on its
119
+ # configured `include` and `exclude` lists.
120
+ def applicable_files
121
+ @applicable_files ||= modified_files.select { |file| applicable_file?(file) }
122
+ end
123
+
124
+ private
125
+
126
+ def applicable_file?(file)
127
+ includes = Array(@config['include']).map do |glob|
128
+ Overcommit::Utils.convert_glob_to_absolute(glob)
129
+ end
130
+
131
+ included = includes.empty? || includes.any? do |glob|
132
+ Overcommit::Utils.matches_path?(glob, file)
133
+ end
134
+
135
+ excludes = Array(@config['exclude']).map do |glob|
136
+ Overcommit::Utils.convert_glob_to_absolute(glob)
137
+ end
138
+
139
+ excluded = excludes.any? do |glob|
140
+ Overcommit::Utils.matches_path?(glob, file)
141
+ end
142
+
143
+ included && !excluded
144
+ end
145
+
146
+ # If the hook defines a required executable, check if it's in the path and
147
+ # display the install command if one exists.
148
+ def check_for_executable
149
+ return unless executable && !in_path?(executable)
150
+
151
+ output = "'#{executable}' is not installed (or is not in your PATH)"
152
+
153
+ if install_command = @config['install_command']
154
+ output += "\nInstall it by running: #{install_command}"
155
+ end
156
+
157
+ output
158
+ end
159
+
160
+ # Transforms the hook's status based on custom configuration.
161
+ #
162
+ # This allows users to change failures into warnings, or vice versa.
163
+ def transform_status(status)
164
+ case status
165
+ when :fail, :bad
166
+ @config.fetch('on_fail', :fail).to_sym
167
+ when :warn
168
+ @config.fetch('on_warn', :warn).to_sym
169
+ else
170
+ status
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,11 @@
1
+ require 'forwardable'
2
+
3
+ module Overcommit::Hook::CommitMsg
4
+ # Functionality common to all commit-msg hooks.
5
+ class Base < Overcommit::Hook::Base
6
+ extend Forwardable
7
+
8
+ def_delegators :@context, :commit_message, :update_commit_message,
9
+ :commit_message_lines, :commit_message_file
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Ensures a Gerrit Change-Id line is included in the commit message.
3
+ #
4
+ # It may seem odd to do this here instead of in a prepare-commit-msg hook, but
5
+ # the reality is that if you want to _ensure_ the Change-Id is included then
6
+ # you need to do it in a commit-msg hook. This is because the user could still
7
+ # edit the message after a prepare-commit-msg hook was run.
8
+ class GerritChangeId < Base
9
+ SCRIPT_LOCATION = Overcommit::Utils.script_path('gerrit-change-id')
10
+
11
+ def run
12
+ result = execute([SCRIPT_LOCATION, commit_message_file])
13
+ return :pass if result.success?
14
+
15
+ [:fail, result.stdout]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Checks for hard tabs in commit messages.
3
+ class HardTabs < Base
4
+ def run
5
+ # Catches hard tabs entered by the user (not auto-generated)
6
+ if commit_message.index(/\t/)
7
+ return :warn, "Don't use hard tabs in commit messages"
8
+ end
9
+
10
+ :pass
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Checks for long commit messages (not good or bad--just fun to point out)
3
+ class RussianNovel < Base
4
+ RUSSIAN_NOVEL_LENGTH = 30
5
+
6
+ def run
7
+ if commit_message_lines.length >= RUSSIAN_NOVEL_LENGTH
8
+ return :warn, 'You seem to have authored a Russian novel; congratulations!'
9
+ end
10
+
11
+ :pass
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Ensures commit message subject lines are followed by a blank line.
3
+ class SingleLineSubject < Base
4
+ def run
5
+ unless commit_message_lines[1].to_s.strip.empty?
6
+ return :warn, 'Subject should be one line and followed by a blank line'
7
+ end
8
+
9
+ :pass
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Ensures the number of columns the subject and commit message lines occupy is
3
+ # under the preferred limits.
4
+ class TextWidth < Base
5
+ def run
6
+ @errors = []
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)
19
+ max_subject_width = config['max_subject_width']
20
+ return unless subject.length > max_subject_width
21
+
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']
29
+
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 > " \
33
+ "#{max_body_width} characters"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ module Overcommit::Hook::CommitMsg
2
+ # Ensures commit message subject lines do not have a trailing period
3
+ class TrailingPeriod < Base
4
+ def run
5
+ if commit_message_lines.first.rstrip.end_with?('.')
6
+ return :warn, 'Please omit trailing period from commit message subject'
7
+ end
8
+
9
+ :pass
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'forwardable'
2
+
3
+ module Overcommit::Hook::PostCheckout
4
+ # Functionality common to all post-checkout hooks.
5
+ class Base < Overcommit::Hook::Base
6
+ extend Forwardable
7
+
8
+ def_delegators :@context,
9
+ :previous_head, :new_head, :branch_checkout?, :file_checkout?
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module Overcommit::Hook::PostCheckout
2
+ # Scans source code each time HEAD changes to generate an up-to-date index of
3
+ # all function/variable definitions, etc.
4
+ class IndexTags < Base
5
+ def run
6
+ unless in_path?('ctags')
7
+ return :pass # Silently ignore
8
+ end
9
+
10
+ index_tags_in_background
11
+
12
+ :pass
13
+ end
14
+
15
+ private
16
+
17
+ SCRIPT_LOCATION = Overcommit::Utils.script_path('index-tags')
18
+
19
+ def index_tags_in_background
20
+ ctags_args = config['ctags_arguments']
21
+
22
+ # TODO: come up with Ruby 1.8-friendly way to do this
23
+ Process.detach(Process.spawn("#{SCRIPT_LOCATION} #{ctags_args}"))
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ require 'forwardable'
2
+
3
+ module Overcommit::Hook::PostCommit
4
+ # Functionality common to all post-commit hooks.
5
+ class Base < Overcommit::Hook::Base
6
+ extend Forwardable
7
+
8
+ def_delegators :@context, :modified_files_last_commit,
9
+ :modified_lines_in_file_last_commit
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Overcommit::Hook::PostCommit
2
+ # Calculates the change in blame since the last revision.
3
+ class GitGuilt < Base
4
+ def run
5
+ execute(%w[git guilt HEAD~1 HEAD])
6
+ :pass
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Overcommit::Hook::PreCommit
2
+ # Checks the format of an author's email address.
3
+ class AuthorEmail < Base
4
+ def run
5
+ result = execute(%w[git config --get user.email])
6
+ email = result.stdout.chomp
7
+
8
+ unless email =~ /#{config['pattern']}/
9
+ return :fail,
10
+ "Author has an invalid email address: '#{email}'\n" \
11
+ 'Set your email with ' \
12
+ '`git config --global user.email your_email@example.com`'
13
+ end
14
+
15
+ :pass
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Overcommit::Hook::PreCommit
2
+ # Ensures that a commit author has a name with at least first and last names.
3
+ class AuthorName < Base
4
+ def run
5
+ result = execute(%w[git config --get user.name])
6
+ name = result.stdout.chomp
7
+
8
+ unless name.split(' ').count >= 2
9
+ return :fail,
10
+ "Author must have at least first and last name, but was: #{name}." \
11
+ "\nSet your name with `git config --global user.name 'Your Name'`"
12
+ end
13
+
14
+ :pass
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,70 @@
1
+ require 'forwardable'
2
+
3
+ module Overcommit::Hook::PreCommit
4
+ # Functionality common to all pre-commit hooks.
5
+ class Base < Overcommit::Hook::Base
6
+ extend Forwardable
7
+
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
69
+ end
70
+ end