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,87 @@
1
+ require 'singleton'
2
+
3
+ # Provides a handler for interrupt signals (SIGINT), allowing the application to
4
+ # finish what it's currently working on.
5
+ class InterruptHandler
6
+ include Singleton
7
+
8
+ attr_accessor :isolate_signals, :signal_received, :reenable_on_interrupt
9
+
10
+ def initialize
11
+ self.isolate_signals = false
12
+ self.signal_received = false
13
+ self.reenable_on_interrupt = false
14
+
15
+ Signal.trap('INT') do
16
+ if isolate_signals
17
+ self.signal_received = true
18
+ else
19
+ if reenable_on_interrupt
20
+ self.reenable_on_interrupt = false
21
+ self.isolate_signals = true
22
+ end
23
+
24
+ raise Interrupt # Allow interrupt to propagate to code
25
+ end
26
+ end
27
+ end
28
+
29
+ class << self
30
+ # Provide a way to allow a single Ctrl-C interrupt to happen and atomically
31
+ # re-enable interrupt protections once that interrupt is propagated.
32
+ #
33
+ # This prevents a race condition where code like the following:
34
+ #
35
+ # begin
36
+ # InterruptHandler.disable!
37
+ # ... do stuff ...
38
+ # rescue Interrupt
39
+ # ... handle it ...
40
+ # ensure
41
+ # InterruptHandler.enable!
42
+ # end
43
+ #
44
+ # ...could have the `enable!` call to the interrupt handler not called in
45
+ # the event another interrupt was received in between the interrupt being
46
+ # handled and the `ensure` block being entered.
47
+ #
48
+ # Thus you should always write:
49
+ #
50
+ # begin
51
+ # InterruptHandler.disable_until_finished_or_interrupted do
52
+ # ... do stuff ...
53
+ # end
54
+ # rescue Interrupt
55
+ # ... handle it ...
56
+ # rescue
57
+ # ... handle any other exceptions ...
58
+ # end
59
+ def disable_until_finished_or_interrupted
60
+ instance.reenable_on_interrupt = true
61
+ instance.isolate_signals = false
62
+ yield
63
+ ensure
64
+ instance.isolate_signals = true
65
+ end
66
+
67
+ def disable!
68
+ instance.isolate_signals = false
69
+ end
70
+
71
+ def enable!
72
+ instance.isolate_signals = true
73
+ end
74
+
75
+ def isolate_from_interrupts
76
+ instance.signal_received = false
77
+ instance.isolate_signals = true
78
+ result = yield
79
+ instance.isolate_signals = false
80
+ result
81
+ end
82
+
83
+ def signal_received?
84
+ instance.signal_received
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,79 @@
1
+ module Overcommit
2
+ # Encapsulates all communication to an output source.
3
+ class Logger
4
+ # Helper for creating a logger which outputs nothing.
5
+ def self.silent
6
+ new(File.open('/dev/null', 'w'))
7
+ end
8
+
9
+ # Creates a logger that will write to the given output stream.
10
+ #
11
+ # @param out [IO]
12
+ def initialize(out)
13
+ @out = out
14
+ end
15
+
16
+ # Write output without a trailing newline.
17
+ def partial(*args)
18
+ @out.print(*args)
19
+ end
20
+
21
+ # Prints a newline character (alias for readability).
22
+ def newline
23
+ log
24
+ end
25
+
26
+ # Write a line of output.
27
+ #
28
+ # A newline character will always be appended.
29
+ def log(*args)
30
+ @out.puts(*args)
31
+ end
32
+
33
+ # Write a line of output that is intended to be emphasized.
34
+ def bold(*args)
35
+ color('1', *args)
36
+ end
37
+
38
+ # Write a line of output indicating a problem or error.
39
+ def error(*args)
40
+ color(31, *args)
41
+ end
42
+
43
+ # Write a line of output indicating a problem or error which is emphasized
44
+ # over a regular problem or error.
45
+ def bold_error(*args)
46
+ color('1;31', *args)
47
+ end
48
+
49
+ # Write a line of output indicating a successful or noteworthy event.
50
+ def success(*args)
51
+ color(32, *args)
52
+ end
53
+
54
+ # Write a line of output indicating a potential cause for concern, but not
55
+ # an actual error.
56
+ def warning(*args)
57
+ color(33, *args)
58
+ end
59
+
60
+ # Write a line of output indicating a potential cause for concern, but with
61
+ # greater emphasize compared to other warnings.
62
+ def bold_warning(*args)
63
+ color('1;33', *args)
64
+ end
65
+
66
+ private
67
+
68
+ # Outputs text wrapped in ANSI escape code necessary to produce a given
69
+ # color/text display.
70
+ #
71
+ # @param code [String] ANSI escape code, e.g. '1;33' for "bold yellow"
72
+ # @param str [String] string to wrap
73
+ # @param partial [true,false] whether to omit a newline
74
+ def color(code, str, partial = false)
75
+ send(partial ? :partial : :log,
76
+ @out.tty? ? "\033[#{code}m#{str}\033[0m" : str)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,132 @@
1
+ module Overcommit
2
+ # Utility class that encapsulates the handling of hook messages and whether
3
+ # they affect lines the user has modified or not.
4
+ #
5
+ # This class exposes an endpoint that extracts an appropriate hook/status
6
+ # output tuple from an array of {Overcommit::Hook::Message}s, respecting the
7
+ # configuration settings for the given hook.
8
+ class MessageProcessor
9
+ ERRORS_MODIFIED_HEADER = 'Errors on modified lines:'
10
+ WARNINGS_MODIFIED_HEADER = 'Warnings on modified lines:'
11
+ ERRORS_UNMODIFIED_HEADER = "Errors on lines you didn't modify:"
12
+ WARNINGS_UNMODIFIED_HEADER = "Warnings on lines you didn't modify:"
13
+
14
+ # @param hook [Overcommit::Hook::Base]
15
+ # @param unmodified_lines_setting [String] how to treat messages on
16
+ # unmodified lines
17
+ def initialize(hook, unmodified_lines_setting)
18
+ @hook = hook
19
+ @setting = unmodified_lines_setting
20
+ end
21
+
22
+ # Returns a hook status/output tuple from the messages this processor was
23
+ # initialized with.
24
+ #
25
+ # @return [Array<Symbol,String>]
26
+ def hook_result(messages)
27
+ status, output = basic_status_and_output(messages)
28
+
29
+ # Nothing to do if there are no problems to begin with
30
+ return [status, output] if status == :pass
31
+
32
+ # Return as-is if this type of hook doesn't have the concept of modified lines
33
+ return [status, output] unless @hook.respond_to?(:modified_lines_in_file)
34
+
35
+ handle_modified_lines(messages, status)
36
+ end
37
+
38
+ private
39
+
40
+ def handle_modified_lines(messages, status)
41
+ messages = remove_ignored_messages(messages)
42
+
43
+ messages_on_modified_lines, messages_on_unmodified_lines =
44
+ messages.partition { |message| message_on_modified_line?(message) }
45
+
46
+ output = print_messages(
47
+ messages_on_modified_lines,
48
+ ERRORS_MODIFIED_HEADER,
49
+ WARNINGS_MODIFIED_HEADER
50
+ )
51
+ output += print_messages(
52
+ messages_on_unmodified_lines,
53
+ ERRORS_UNMODIFIED_HEADER,
54
+ WARNINGS_UNMODIFIED_HEADER
55
+ )
56
+
57
+ [transform_status(status, messages_on_modified_lines), output]
58
+ end
59
+
60
+ def transform_status(status, messages_on_modified_lines)
61
+ # `report` indicates user wants the original status
62
+ return status if @setting == 'report'
63
+
64
+ error_messages, warning_messages =
65
+ messages_on_modified_lines.partition { |msg| msg.type == :error }
66
+
67
+ if can_upgrade_to_warning?(status, error_messages)
68
+ status = :warn
69
+ end
70
+
71
+ if can_upgrade_to_passing?(status, warning_messages)
72
+ status = :pass
73
+ end
74
+
75
+ status
76
+ end
77
+
78
+ def can_upgrade_to_warning?(status, error_messages)
79
+ status == :fail && error_messages.empty?
80
+ end
81
+
82
+ def can_upgrade_to_passing?(status, warning_messages)
83
+ status == :warn && @setting == 'ignore' && warning_messages.empty?
84
+ end
85
+
86
+ # Returns status and output for messages assuming no special treatment of
87
+ # messages occurring on unmodified lines.
88
+ def basic_status_and_output(messages)
89
+ status =
90
+ if messages.any? { |message| message.type == :error }
91
+ :fail
92
+ elsif messages.any? { |message| message.type == :warning }
93
+ :warn
94
+ else
95
+ :pass
96
+ end
97
+
98
+ output = ''
99
+ if messages.any?
100
+ output += messages.join("\n") + "\n"
101
+ end
102
+
103
+ [status, output]
104
+ end
105
+
106
+ def print_messages(messages, error_heading, warning_heading)
107
+ output = ''
108
+ errors, warnings = messages.partition { |msg| msg.type == :error }
109
+
110
+ if errors.any?
111
+ output += "#{error_heading}\n#{errors.join("\n")}\n"
112
+ end
113
+
114
+ if warnings.any?
115
+ output += "#{warning_heading}\n#{warnings.join("\n")}\n"
116
+ end
117
+
118
+ output
119
+ end
120
+
121
+ def remove_ignored_messages(messages)
122
+ # If user wants to ignore messages on unmodified lines, simply remove them
123
+ return messages unless @setting == 'ignore'
124
+
125
+ messages.select { |message| message_on_modified_line?(message) }
126
+ end
127
+
128
+ def message_on_modified_line?(message)
129
+ @hook.modified_lines_in_file(message.file).include?(message.line)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: utf-8
2
+
3
+ module Overcommit
4
+ # Provide a set of callbacks which can be executed as events occur during the
5
+ # course of {HookRunner#run}.
6
+ class Printer
7
+ attr_reader :log
8
+
9
+ def initialize(logger, context)
10
+ @log = logger
11
+ @context = context
12
+ end
13
+
14
+ # Executed at the very beginning of running the collection of hooks.
15
+ def start_run
16
+ log.bold "Running #{hook_script_name} hooks"
17
+ end
18
+
19
+ def nothing_to_run
20
+ log.success "✓ No applicable #{hook_script_name} hooks to run"
21
+ end
22
+
23
+ # Executed at the start of an individual hook run.
24
+ def start_hook(hook)
25
+ unless hook.quiet?
26
+ print_header(hook)
27
+ end
28
+ end
29
+
30
+ def hook_skipped(hook)
31
+ log.warning "Skipping #{hook.name}"
32
+ end
33
+
34
+ def required_hook_not_skipped(hook)
35
+ log.warning "Cannot skip #{hook.name} since it is required"
36
+ end
37
+
38
+ # Executed at the end of an individual hook run.
39
+ def end_hook(hook, status, output)
40
+ # Want to print the header for quiet hooks only if the result wasn't good
41
+ # so that the user knows what failed
42
+ print_header(hook) if hook.quiet? && ![:good, :pass].include?(status)
43
+
44
+ print_result(hook, status, output)
45
+ end
46
+
47
+ # Executed when a hook run was interrupted/cancelled by user.
48
+ def run_interrupted
49
+ log.newline
50
+ log.warning '⚠ Hook run interrupted by user'
51
+ log.newline
52
+ end
53
+
54
+ # Executed when one or more hooks by the end of the run.
55
+ def run_failed
56
+ log.newline
57
+ log.error "✗ One or more #{hook_script_name} hooks failed"
58
+ log.newline
59
+ end
60
+
61
+ # Executed when no hooks failed by the end of the run.
62
+ def run_succeeded
63
+ log.newline
64
+ log.success "✓ All #{hook_script_name} hooks passed"
65
+ log.newline
66
+ end
67
+
68
+ private
69
+
70
+ def print_header(hook)
71
+ hook_name = "[#{hook.name}] "
72
+ log.partial hook.description
73
+ log.partial '.' * [70 - hook.description.length - hook_name.length, 0].max
74
+ log.partial hook_name
75
+ end
76
+
77
+ def print_result(hook, status, output) # rubocop:disable CyclomaticComplexity, MethodLength
78
+ case status
79
+ when :pass
80
+ log.success 'OK' unless hook.quiet?
81
+ when :good
82
+ log.success 'OK'
83
+ log.bold_error 'Hook returned a status of `:good`. This is deprecated ' \
84
+ 'in favor of `:pass` and will be removed in a future ' \
85
+ 'version of Overcommit'
86
+ when :warn
87
+ log.warning 'WARNING'
88
+ print_report(output, :bold_warning)
89
+ when :bad
90
+ log.error 'FAILED'
91
+ log.bold_error 'Hook returned a status of `:bad`. This is deprecated ' \
92
+ 'in favor of `:fail` and will be removed in a future ' \
93
+ 'version of Overcommit'
94
+ print_report(output, :bold_error)
95
+ when :fail
96
+ log.error 'FAILED'
97
+ print_report(output, :bold_error)
98
+ when :interrupt
99
+ log.error 'INTERRUPTED'
100
+ print_report(output, :bold_error)
101
+ else
102
+ log.error '???'
103
+ print_report("Hook returned unknown status `#{status.inspect}` -- ignoring.",
104
+ :bold_error)
105
+ end
106
+ end
107
+
108
+ def print_report(output, format = :log)
109
+ log.send(format, output) unless output.nil? || output.empty?
110
+ end
111
+
112
+ def hook_script_name
113
+ @context.hook_script_name
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,46 @@
1
+ require 'childprocess'
2
+ require 'tempfile'
3
+
4
+ module Overcommit
5
+ # Manages execution of a child process, collecting the exit status and
6
+ # standard out/error output.
7
+ class Subprocess
8
+ # Encapsulates the result of a process.
9
+ Result = Struct.new(:status, :stdout, :stderr) do
10
+ def success?
11
+ status == 0
12
+ end
13
+ end
14
+
15
+ class << self
16
+ # Spawns a new process using the given array of arguments (the first
17
+ # element is the command).
18
+ def spawn(args)
19
+ process = ChildProcess.build(*args)
20
+
21
+ out, err = assign_output_streams(process)
22
+
23
+ process.start
24
+ process.wait
25
+
26
+ err.rewind
27
+ out.rewind
28
+
29
+ Result.new(process.exit_code, out.read, err.read)
30
+ end
31
+
32
+ private
33
+
34
+ # @param process [ChildProcess]
35
+ # @return [Array<IO>]
36
+ def assign_output_streams(process)
37
+ %w[out err].map do |stream_name|
38
+ ::Tempfile.new(stream_name).tap do |stream|
39
+ stream.sync = true
40
+ process.io.send("std#{stream_name}=", stream)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end