jawshooah-overcommit 0.22.0

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