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,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