overcommit-jeygeethanmedia 0.53.1

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 (226) hide show
  1. checksums.yaml +7 -0
  2. data/bin/overcommit +50 -0
  3. data/config/default.yml +1356 -0
  4. data/config/starter.yml +33 -0
  5. data/lib/overcommit.rb +26 -0
  6. data/lib/overcommit/cli.rb +223 -0
  7. data/lib/overcommit/command_splitter.rb +146 -0
  8. data/lib/overcommit/configuration.rb +350 -0
  9. data/lib/overcommit/configuration_loader.rb +96 -0
  10. data/lib/overcommit/configuration_validator.rb +186 -0
  11. data/lib/overcommit/constants.rb +12 -0
  12. data/lib/overcommit/exceptions.rb +52 -0
  13. data/lib/overcommit/git_config.rb +22 -0
  14. data/lib/overcommit/git_repo.rb +286 -0
  15. data/lib/overcommit/git_version.rb +17 -0
  16. data/lib/overcommit/hook/base.rb +294 -0
  17. data/lib/overcommit/hook/commit_msg/base.rb +14 -0
  18. data/lib/overcommit/hook/commit_msg/capitalized_subject.rb +25 -0
  19. data/lib/overcommit/hook/commit_msg/empty_message.rb +12 -0
  20. data/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +22 -0
  21. data/lib/overcommit/hook/commit_msg/hard_tabs.rb +17 -0
  22. data/lib/overcommit/hook/commit_msg/message_format.rb +31 -0
  23. data/lib/overcommit/hook/commit_msg/russian_novel.rb +16 -0
  24. data/lib/overcommit/hook/commit_msg/single_line_subject.rb +16 -0
  25. data/lib/overcommit/hook/commit_msg/spell_check.rb +45 -0
  26. data/lib/overcommit/hook/commit_msg/text_width.rb +56 -0
  27. data/lib/overcommit/hook/commit_msg/trailing_period.rb +16 -0
  28. data/lib/overcommit/hook/post_checkout/base.rb +22 -0
  29. data/lib/overcommit/hook/post_checkout/bower_install.rb +13 -0
  30. data/lib/overcommit/hook/post_checkout/bundle_install.rb +13 -0
  31. data/lib/overcommit/hook/post_checkout/composer_install.rb +13 -0
  32. data/lib/overcommit/hook/post_checkout/index_tags.rb +12 -0
  33. data/lib/overcommit/hook/post_checkout/npm_install.rb +13 -0
  34. data/lib/overcommit/hook/post_checkout/submodule_status.rb +12 -0
  35. data/lib/overcommit/hook/post_checkout/yarn_install.rb +13 -0
  36. data/lib/overcommit/hook/post_commit/base.rb +12 -0
  37. data/lib/overcommit/hook/post_commit/bower_install.rb +13 -0
  38. data/lib/overcommit/hook/post_commit/bundle_install.rb +13 -0
  39. data/lib/overcommit/hook/post_commit/commitplease.rb +16 -0
  40. data/lib/overcommit/hook/post_commit/composer_install.rb +13 -0
  41. data/lib/overcommit/hook/post_commit/git_guilt.rb +43 -0
  42. data/lib/overcommit/hook/post_commit/index_tags.rb +12 -0
  43. data/lib/overcommit/hook/post_commit/npm_install.rb +13 -0
  44. data/lib/overcommit/hook/post_commit/submodule_status.rb +12 -0
  45. data/lib/overcommit/hook/post_commit/yarn_install.rb +13 -0
  46. data/lib/overcommit/hook/post_merge/base.rb +12 -0
  47. data/lib/overcommit/hook/post_merge/bower_install.rb +13 -0
  48. data/lib/overcommit/hook/post_merge/bundle_install.rb +13 -0
  49. data/lib/overcommit/hook/post_merge/composer_install.rb +13 -0
  50. data/lib/overcommit/hook/post_merge/index_tags.rb +12 -0
  51. data/lib/overcommit/hook/post_merge/npm_install.rb +13 -0
  52. data/lib/overcommit/hook/post_merge/submodule_status.rb +12 -0
  53. data/lib/overcommit/hook/post_merge/yarn_install.rb +13 -0
  54. data/lib/overcommit/hook/post_rewrite/base.rb +12 -0
  55. data/lib/overcommit/hook/post_rewrite/bower_install.rb +13 -0
  56. data/lib/overcommit/hook/post_rewrite/bundle_install.rb +13 -0
  57. data/lib/overcommit/hook/post_rewrite/composer_install.rb +13 -0
  58. data/lib/overcommit/hook/post_rewrite/index_tags.rb +19 -0
  59. data/lib/overcommit/hook/post_rewrite/npm_install.rb +13 -0
  60. data/lib/overcommit/hook/post_rewrite/submodule_status.rb +12 -0
  61. data/lib/overcommit/hook/post_rewrite/yarn_install.rb +13 -0
  62. data/lib/overcommit/hook/pre_commit/author_email.rb +26 -0
  63. data/lib/overcommit/hook/pre_commit/author_name.rb +25 -0
  64. data/lib/overcommit/hook/pre_commit/base.rb +19 -0
  65. data/lib/overcommit/hook/pre_commit/berksfile_check.rb +24 -0
  66. data/lib/overcommit/hook/pre_commit/broken_symlinks.rb +17 -0
  67. data/lib/overcommit/hook/pre_commit/bundle_audit.rb +24 -0
  68. data/lib/overcommit/hook/pre_commit/bundle_check.rb +32 -0
  69. data/lib/overcommit/hook/pre_commit/bundle_outdated.rb +25 -0
  70. data/lib/overcommit/hook/pre_commit/case_conflicts.rb +27 -0
  71. data/lib/overcommit/hook/pre_commit/chamber_compare.rb +43 -0
  72. data/lib/overcommit/hook/pre_commit/chamber_security.rb +15 -0
  73. data/lib/overcommit/hook/pre_commit/chamber_verification.rb +36 -0
  74. data/lib/overcommit/hook/pre_commit/code_spell_check.rb +36 -0
  75. data/lib/overcommit/hook/pre_commit/coffee_lint.rb +35 -0
  76. data/lib/overcommit/hook/pre_commit/cook_style.rb +35 -0
  77. data/lib/overcommit/hook/pre_commit/credo.rb +27 -0
  78. data/lib/overcommit/hook/pre_commit/css_lint.rb +26 -0
  79. data/lib/overcommit/hook/pre_commit/dogma.rb +33 -0
  80. data/lib/overcommit/hook/pre_commit/es_lint.rb +38 -0
  81. data/lib/overcommit/hook/pre_commit/execute_permissions.rb +76 -0
  82. data/lib/overcommit/hook/pre_commit/fasterer.rb +25 -0
  83. data/lib/overcommit/hook/pre_commit/file_size.rb +47 -0
  84. data/lib/overcommit/hook/pre_commit/fix_me.rb +17 -0
  85. data/lib/overcommit/hook/pre_commit/flay.rb +38 -0
  86. data/lib/overcommit/hook/pre_commit/foodcritic.rb +149 -0
  87. data/lib/overcommit/hook/pre_commit/forbidden_branches.rb +26 -0
  88. data/lib/overcommit/hook/pre_commit/ginkgo_focus.rb +23 -0
  89. data/lib/overcommit/hook/pre_commit/go_fmt.rb +17 -0
  90. data/lib/overcommit/hook/pre_commit/go_lint.rb +29 -0
  91. data/lib/overcommit/hook/pre_commit/go_vet.rb +24 -0
  92. data/lib/overcommit/hook/pre_commit/golangci_lint.rb +21 -0
  93. data/lib/overcommit/hook/pre_commit/hadolint.rb +27 -0
  94. data/lib/overcommit/hook/pre_commit/haml_lint.rb +23 -0
  95. data/lib/overcommit/hook/pre_commit/hard_tabs.rb +15 -0
  96. data/lib/overcommit/hook/pre_commit/hlint.rb +34 -0
  97. data/lib/overcommit/hook/pre_commit/html_hint.rb +23 -0
  98. data/lib/overcommit/hook/pre_commit/html_tidy.rb +30 -0
  99. data/lib/overcommit/hook/pre_commit/image_optim.rb +28 -0
  100. data/lib/overcommit/hook/pre_commit/java_checkstyle.rb +27 -0
  101. data/lib/overcommit/hook/pre_commit/js_hint.rb +23 -0
  102. data/lib/overcommit/hook/pre_commit/js_lint.rb +22 -0
  103. data/lib/overcommit/hook/pre_commit/jscs.rb +27 -0
  104. data/lib/overcommit/hook/pre_commit/jsl.rb +28 -0
  105. data/lib/overcommit/hook/pre_commit/json_syntax.rb +21 -0
  106. data/lib/overcommit/hook/pre_commit/kt_lint.rb +19 -0
  107. data/lib/overcommit/hook/pre_commit/license_finder.rb +14 -0
  108. data/lib/overcommit/hook/pre_commit/license_header.rb +48 -0
  109. data/lib/overcommit/hook/pre_commit/line_endings.rb +77 -0
  110. data/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb +16 -0
  111. data/lib/overcommit/hook/pre_commit/mdl.rb +29 -0
  112. data/lib/overcommit/hook/pre_commit/merge_conflicts.rb +16 -0
  113. data/lib/overcommit/hook/pre_commit/nginx_test.rb +26 -0
  114. data/lib/overcommit/hook/pre_commit/pep257.rb +23 -0
  115. data/lib/overcommit/hook/pre_commit/pep8.rb +23 -0
  116. data/lib/overcommit/hook/pre_commit/php_cs.rb +43 -0
  117. data/lib/overcommit/hook/pre_commit/php_cs_fixer.rb +57 -0
  118. data/lib/overcommit/hook/pre_commit/php_lint.rb +44 -0
  119. data/lib/overcommit/hook/pre_commit/php_stan.rb +30 -0
  120. data/lib/overcommit/hook/pre_commit/pronto.rb +12 -0
  121. data/lib/overcommit/hook/pre_commit/puppet_lint.rb +26 -0
  122. data/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb +29 -0
  123. data/lib/overcommit/hook/pre_commit/pycodestyle.rb +23 -0
  124. data/lib/overcommit/hook/pre_commit/pydocstyle.rb +23 -0
  125. data/lib/overcommit/hook/pre_commit/pyflakes.rb +32 -0
  126. data/lib/overcommit/hook/pre_commit/pylint.rb +32 -0
  127. data/lib/overcommit/hook/pre_commit/python_flake8.rb +32 -0
  128. data/lib/overcommit/hook/pre_commit/rails_best_practices.rb +34 -0
  129. data/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb +58 -0
  130. data/lib/overcommit/hook/pre_commit/rake_target.rb +12 -0
  131. data/lib/overcommit/hook/pre_commit/reek.rb +26 -0
  132. data/lib/overcommit/hook/pre_commit/rst_lint.rb +27 -0
  133. data/lib/overcommit/hook/pre_commit/rubo_cop.rb +35 -0
  134. data/lib/overcommit/hook/pre_commit/ruby_lint.rb +23 -0
  135. data/lib/overcommit/hook/pre_commit/ruby_syntax.rb +27 -0
  136. data/lib/overcommit/hook/pre_commit/scalariform.rb +22 -0
  137. data/lib/overcommit/hook/pre_commit/scalastyle.rb +31 -0
  138. data/lib/overcommit/hook/pre_commit/scss_lint.rb +43 -0
  139. data/lib/overcommit/hook/pre_commit/semi_standard.rb +23 -0
  140. data/lib/overcommit/hook/pre_commit/shell_check.rb +23 -0
  141. data/lib/overcommit/hook/pre_commit/slim_lint.rb +23 -0
  142. data/lib/overcommit/hook/pre_commit/sqlint.rb +26 -0
  143. data/lib/overcommit/hook/pre_commit/standard.rb +23 -0
  144. data/lib/overcommit/hook/pre_commit/stylelint.rb +23 -0
  145. data/lib/overcommit/hook/pre_commit/swift_lint.rb +19 -0
  146. data/lib/overcommit/hook/pre_commit/terraform_format.rb +19 -0
  147. data/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +15 -0
  148. data/lib/overcommit/hook/pre_commit/travis_lint.rb +15 -0
  149. data/lib/overcommit/hook/pre_commit/ts_lint.rb +28 -0
  150. data/lib/overcommit/hook/pre_commit/vint.rb +22 -0
  151. data/lib/overcommit/hook/pre_commit/w3c_css.rb +67 -0
  152. data/lib/overcommit/hook/pre_commit/w3c_html.rb +64 -0
  153. data/lib/overcommit/hook/pre_commit/xml_lint.rb +24 -0
  154. data/lib/overcommit/hook/pre_commit/xml_syntax.rb +21 -0
  155. data/lib/overcommit/hook/pre_commit/yaml_lint.rb +18 -0
  156. data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +20 -0
  157. data/lib/overcommit/hook/pre_commit/yard_coverage.rb +90 -0
  158. data/lib/overcommit/hook/pre_commit/yarn_check.rb +37 -0
  159. data/lib/overcommit/hook/pre_push/base.rb +33 -0
  160. data/lib/overcommit/hook/pre_push/brakeman.rb +15 -0
  161. data/lib/overcommit/hook/pre_push/cargo_test.rb +12 -0
  162. data/lib/overcommit/hook/pre_push/go_test.rb +14 -0
  163. data/lib/overcommit/hook/pre_push/golangci_lint.rb +16 -0
  164. data/lib/overcommit/hook/pre_push/minitest.rb +20 -0
  165. data/lib/overcommit/hook/pre_push/php_unit.rb +16 -0
  166. data/lib/overcommit/hook/pre_push/pronto.rb +12 -0
  167. data/lib/overcommit/hook/pre_push/protected_branches.rb +74 -0
  168. data/lib/overcommit/hook/pre_push/pytest.rb +16 -0
  169. data/lib/overcommit/hook/pre_push/python_nose.rb +16 -0
  170. data/lib/overcommit/hook/pre_push/r_spec.rb +16 -0
  171. data/lib/overcommit/hook/pre_push/rake_target.rb +12 -0
  172. data/lib/overcommit/hook/pre_push/test_unit.rb +16 -0
  173. data/lib/overcommit/hook/pre_rebase/base.rb +14 -0
  174. data/lib/overcommit/hook/pre_rebase/merged_commits.rb +31 -0
  175. data/lib/overcommit/hook/prepare_commit_msg/base.rb +25 -0
  176. data/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb +57 -0
  177. data/lib/overcommit/hook/shared/bower_install.rb +15 -0
  178. data/lib/overcommit/hook/shared/bundle_install.rb +15 -0
  179. data/lib/overcommit/hook/shared/composer_install.rb +15 -0
  180. data/lib/overcommit/hook/shared/index_tags.rb +14 -0
  181. data/lib/overcommit/hook/shared/npm_install.rb +15 -0
  182. data/lib/overcommit/hook/shared/pronto.rb +21 -0
  183. data/lib/overcommit/hook/shared/rake_target.rb +26 -0
  184. data/lib/overcommit/hook/shared/submodule_status.rb +32 -0
  185. data/lib/overcommit/hook/shared/yarn_install.rb +15 -0
  186. data/lib/overcommit/hook_context.rb +19 -0
  187. data/lib/overcommit/hook_context/base.rb +139 -0
  188. data/lib/overcommit/hook_context/commit_msg.rb +48 -0
  189. data/lib/overcommit/hook_context/post_checkout.rb +36 -0
  190. data/lib/overcommit/hook_context/post_commit.rb +33 -0
  191. data/lib/overcommit/hook_context/post_merge.rb +37 -0
  192. data/lib/overcommit/hook_context/post_rewrite.rb +49 -0
  193. data/lib/overcommit/hook_context/pre_commit.rb +212 -0
  194. data/lib/overcommit/hook_context/pre_push.rb +89 -0
  195. data/lib/overcommit/hook_context/pre_rebase.rb +38 -0
  196. data/lib/overcommit/hook_context/prepare_commit_msg.rb +34 -0
  197. data/lib/overcommit/hook_context/run_all.rb +48 -0
  198. data/lib/overcommit/hook_loader/base.rb +48 -0
  199. data/lib/overcommit/hook_loader/built_in_hook_loader.rb +14 -0
  200. data/lib/overcommit/hook_loader/plugin_hook_loader.rb +102 -0
  201. data/lib/overcommit/hook_runner.rb +219 -0
  202. data/lib/overcommit/hook_signer.rb +123 -0
  203. data/lib/overcommit/installer.rb +193 -0
  204. data/lib/overcommit/interrupt_handler.rb +91 -0
  205. data/lib/overcommit/logger.rb +92 -0
  206. data/lib/overcommit/message_processor.rb +148 -0
  207. data/lib/overcommit/os.rb +38 -0
  208. data/lib/overcommit/printer.rb +145 -0
  209. data/lib/overcommit/subprocess.rb +98 -0
  210. data/lib/overcommit/utils.rb +309 -0
  211. data/lib/overcommit/utils/file_utils.rb +71 -0
  212. data/lib/overcommit/utils/messages_utils.rb +77 -0
  213. data/lib/overcommit/version.rb +6 -0
  214. data/libexec/gerrit-change-id +174 -0
  215. data/libexec/index-tags +17 -0
  216. data/template-dir/hooks/commit-msg +116 -0
  217. data/template-dir/hooks/overcommit-hook +116 -0
  218. data/template-dir/hooks/post-checkout +116 -0
  219. data/template-dir/hooks/post-commit +116 -0
  220. data/template-dir/hooks/post-merge +116 -0
  221. data/template-dir/hooks/post-rewrite +116 -0
  222. data/template-dir/hooks/pre-commit +116 -0
  223. data/template-dir/hooks/pre-push +116 -0
  224. data/template-dir/hooks/pre-rebase +116 -0
  225. data/template-dir/hooks/prepare-commit-msg +116 -0
  226. metadata +303 -0
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Overcommit::HookContext
6
+ # Simulates a pre-commit context pretending that all files have been changed.
7
+ #
8
+ # This results in pre-commit hooks running against the entire repository,
9
+ # which is useful for automated CI scripts.
10
+ class RunAll < Base
11
+ def modified_files
12
+ @modified_files ||= all_files
13
+ end
14
+
15
+ # Returns all lines in the file since in this context the entire repo is
16
+ # being scrutinized.
17
+ #
18
+ # @param file [String]
19
+ # @return [Set]
20
+ def modified_lines_in_file(file)
21
+ @modified_lines_in_file ||= {}
22
+ @modified_lines_in_file[file] ||= Set.new(1..count_lines(file))
23
+ end
24
+
25
+ def hook_class_name
26
+ 'PreCommit'
27
+ end
28
+
29
+ def hook_type_name
30
+ 'pre_commit'
31
+ end
32
+
33
+ def hook_script_name
34
+ 'pre-commit'
35
+ end
36
+
37
+ def initial_commit?
38
+ return @initial_commit unless @initial_commit.nil?
39
+ @initial_commit = Overcommit::GitRepo.initial_commit?
40
+ end
41
+
42
+ private
43
+
44
+ def count_lines(file)
45
+ File.foreach(file).count
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Overcommit::HookLoader
4
+ # Responsible for loading hooks from a file.
5
+ class Base
6
+ # @param config [Overcommit::Configuration]
7
+ # @param context [Overcommit::HookContext]
8
+ # @param logger [Overcommit::Logger]
9
+ def initialize(config, context, logger)
10
+ @config = config
11
+ @context = context
12
+ @log = logger
13
+ end
14
+
15
+ # When implemented in subclasses, loads the hooks for which that subclass is
16
+ # responsible.
17
+ #
18
+ # @return [Array<Hook>]
19
+ def load_hooks
20
+ raise NotImplementedError
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :log
26
+
27
+ # Load and return a {Hook} from a CamelCase hook name.
28
+ def create_hook(hook_name)
29
+ hook_type_class = Overcommit::Hook.const_get(@context.hook_class_name)
30
+ hook_base_class = hook_type_class.const_get(:Base)
31
+ hook_class = hook_type_class.const_get(hook_name)
32
+ unless hook_class < hook_base_class
33
+ raise Overcommit::Exceptions::HookLoadError,
34
+ "Class #{hook_name} is not a subclass of #{hook_base_class}."
35
+ end
36
+
37
+ begin
38
+ Overcommit::Hook.const_get(@context.hook_class_name).
39
+ const_get(hook_name).
40
+ new(@config, @context)
41
+ rescue LoadError, NameError => error
42
+ raise Overcommit::Exceptions::HookLoadError,
43
+ "Unable to load hook '#{hook_name}': #{error}",
44
+ error.backtrace
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Overcommit::HookLoader
4
+ # Responsible for loading hooks that ship with Overcommit.
5
+ class BuiltInHookLoader < Base
6
+ def load_hooks
7
+ @config.enabled_builtin_hooks(@context).map do |hook_name|
8
+ underscored_hook_name = Overcommit::Utils.snake_case(hook_name)
9
+ require "overcommit/hook/#{@context.hook_type_name}/#{underscored_hook_name}"
10
+ create_hook(hook_name)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Overcommit::HookLoader
6
+ # Responsible for loading hooks that are specific to the repository Overcommit
7
+ # is running in.
8
+ class PluginHookLoader < Base
9
+ def load_hooks
10
+ check_for_modified_plugins if @config.verify_signatures?
11
+
12
+ hooks = plugin_paths.map do |plugin_path|
13
+ require plugin_path
14
+
15
+ hook_name = Overcommit::Utils.camel_case(File.basename(plugin_path, '.rb'))
16
+ create_hook(hook_name)
17
+ end
18
+
19
+ hooks + ad_hoc_hook_names.map do |hook_name|
20
+ create_ad_hoc_hook(hook_name)
21
+ end
22
+ end
23
+
24
+ def update_signatures
25
+ log.success('No plugin signatures have changed') if modified_plugins.empty?
26
+
27
+ modified_plugins.each do |plugin|
28
+ plugin.update_signature!
29
+ log.warning "Updated signature of plugin #{plugin.hook_name}"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def plugin_paths
36
+ directory = File.join(@config.plugin_directory, @context.hook_type_name)
37
+ Dir[File.join(directory, '*.rb')].sort
38
+ end
39
+
40
+ def plugin_hook_names
41
+ plugin_paths.map do |path|
42
+ Overcommit::Utils.camel_case(File.basename(path, '.rb'))
43
+ end
44
+ end
45
+
46
+ def ad_hoc_hook_names
47
+ @config.enabled_ad_hoc_hooks(@context)
48
+ end
49
+
50
+ def modified_plugins
51
+ (plugin_hook_names + ad_hoc_hook_names).
52
+ map { |hook_name| Overcommit::HookSigner.new(hook_name, @config, @context) }.
53
+ select(&:signature_changed?)
54
+ end
55
+
56
+ def check_for_modified_plugins
57
+ return if modified_plugins.empty?
58
+
59
+ log.bold_warning "The following #{@context.hook_script_name} plugins " \
60
+ 'have been added, changed, or had their configuration modified:'
61
+ log.newline
62
+
63
+ modified_plugins.each do |signer|
64
+ log.warning " * #{signer.hook_name} in #{signer.hook_path}"
65
+ end
66
+
67
+ log.newline
68
+ log.bold_warning 'You should verify the changes and then run:'
69
+ log.newline
70
+ log.warning "overcommit --sign #{@context.hook_script_name}"
71
+ log.newline
72
+ log.log "For more information, see #{Overcommit::REPO_URL}#security"
73
+
74
+ raise Overcommit::Exceptions::InvalidHookSignature
75
+ end
76
+
77
+ def create_ad_hoc_hook(hook_name)
78
+ hook_module = Overcommit::Hook.const_get(@context.hook_class_name)
79
+ hook_base = hook_module.const_get('Base')
80
+
81
+ # Implement a simple class that executes the command and returns pass/fail
82
+ # based on the exit status
83
+ hook_class = Class.new(hook_base) do
84
+ def run
85
+ result = @context.execute_hook(command)
86
+
87
+ if result.success?
88
+ :pass
89
+ else
90
+ [:fail, result.stdout + result.stderr]
91
+ end
92
+ end
93
+ end
94
+
95
+ hook_module.const_set(hook_name, hook_class).new(@config, @context)
96
+ rescue LoadError, NameError => error
97
+ raise Overcommit::Exceptions::HookLoadError,
98
+ "Unable to load hook '#{hook_name}': #{error}",
99
+ error.backtrace
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Overcommit
4
+ # Responsible for loading the hooks the repository has configured and running
5
+ # them, collecting and displaying the results.
6
+ class HookRunner # rubocop:disable Metrics/ClassLength
7
+ # @param config [Overcommit::Configuration]
8
+ # @param logger [Overcommit::Logger]
9
+ # @param context [Overcommit::HookContext]
10
+ # @param printer [Overcommit::Printer]
11
+ def initialize(config, logger, context, printer)
12
+ @config = config
13
+ @log = logger
14
+ @context = context
15
+ @printer = printer
16
+ @hooks = []
17
+
18
+ @lock = Mutex.new
19
+ @resource = ConditionVariable.new
20
+ @slots_available = @config.concurrency
21
+ end
22
+
23
+ # Loads and runs the hooks registered for this {HookRunner}.
24
+ def run
25
+ # ASSUMPTION: we assume the setup and cleanup calls will never need to be
26
+ # interrupted, i.e. they will finish quickly. Should further evidence
27
+ # suggest this assumption does not hold, we will have to separately wrap
28
+ # these calls to allow some sort of "are you sure?" double-interrupt
29
+ # functionality, but until that's deemed necessary let's keep it simple.
30
+ InterruptHandler.isolate_from_interrupts do
31
+ # Load hooks before setting up the environment so that the repository
32
+ # has not been touched yet. This way any load errors at this point don't
33
+ # result in Overcommit leaving the repository in a bad state.
34
+ load_hooks
35
+
36
+ # Setup the environment without automatically calling
37
+ # `cleanup_environment` on an error. This is because it's possible that
38
+ # the `setup_environment` code did not fully complete, so there's no
39
+ # guarantee that `cleanup_environment` will be able to accomplish
40
+ # anything of value. The safest thing to do is therefore nothing in the
41
+ # unlikely case of failure.
42
+ @context.setup_environment
43
+
44
+ begin
45
+ run_hooks
46
+ ensure
47
+ @context.cleanup_environment
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ attr_reader :log
55
+
56
+ def run_hooks # rubocop:disable Metrics/MethodLength
57
+ if @hooks.any?(&:enabled?)
58
+ @printer.start_run
59
+
60
+ # Sort so hooks requiring fewer processors get queued first. This
61
+ # ensures we make better use of our available processors
62
+ @hooks_left = @hooks.sort_by { |hook| processors_for_hook(hook) }
63
+ @threads = Array.new(@config.concurrency) { Thread.new(&method(:consume)) }
64
+
65
+ begin
66
+ InterruptHandler.disable_until_finished_or_interrupted do
67
+ @threads.each(&:join)
68
+ end
69
+ rescue Interrupt
70
+ @printer.interrupt_triggered
71
+ # We received an interrupt on the main thread, so alert the
72
+ # remaining workers that an exception occurred
73
+ @interrupted = true
74
+ @threads.each { |thread| thread.raise Interrupt }
75
+ end
76
+
77
+ print_results
78
+
79
+ hook_failed = @failed || @interrupted
80
+
81
+ if hook_failed
82
+ message = @context.post_fail_message
83
+ @printer.hook_run_failed(message) unless message.nil?
84
+ end
85
+
86
+ !hook_failed
87
+ else
88
+ @printer.nothing_to_run
89
+ true # Run was successful
90
+ end
91
+ end
92
+
93
+ def consume
94
+ loop do
95
+ hook = @lock.synchronize { @hooks_left.pop }
96
+ break unless hook
97
+ run_hook(hook)
98
+ end
99
+ end
100
+
101
+ def wait_for_slot(hook)
102
+ @lock.synchronize do
103
+ slots_needed = processors_for_hook(hook)
104
+
105
+ loop do
106
+ if @slots_available >= slots_needed
107
+ @slots_available -= slots_needed
108
+
109
+ # Give another thread a chance since there are still slots available
110
+ @resource.signal if @slots_available > 0
111
+ break
112
+ elsif @slots_available > 0
113
+ # It's possible that another hook that requires fewer slots can be
114
+ # served, so give another a chance
115
+ @resource.signal
116
+
117
+ # Wait for a signal from another thread to try again
118
+ @resource.wait(@lock)
119
+ else
120
+ # Otherwise there are not slots left, so just wait for signal
121
+ @resource.wait(@lock)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def release_slot(hook)
128
+ @lock.synchronize do
129
+ slots_released = processors_for_hook(hook)
130
+ @slots_available += slots_released
131
+
132
+ # Signal every time in case there are threads that are already waiting for
133
+ # these slots to be released
134
+ @resource.signal
135
+ end
136
+ end
137
+
138
+ def processors_for_hook(hook)
139
+ hook.parallelize? ? hook.processors : @config.concurrency
140
+ end
141
+
142
+ def print_results
143
+ if @interrupted
144
+ @printer.run_interrupted
145
+ elsif @failed
146
+ @printer.run_failed
147
+ elsif @warned
148
+ @printer.run_warned
149
+ else
150
+ @printer.run_succeeded
151
+ end
152
+ end
153
+
154
+ def run_hook(hook) # rubocop:disable Metrics/CyclomaticComplexity
155
+ status, output = nil, nil
156
+
157
+ begin
158
+ wait_for_slot(hook)
159
+ return if should_skip?(hook)
160
+
161
+ status, output = hook.run_and_transform
162
+ rescue Overcommit::Exceptions::MessageProcessingError => ex
163
+ status = :fail
164
+ output = ex.message
165
+ rescue StandardError => ex
166
+ status = :fail
167
+ output = "Hook raised unexpected error\n#{ex.message}\n#{ex.backtrace.join("\n")}"
168
+ end
169
+
170
+ @failed = true if status == :fail
171
+ @warned = true if status == :warn
172
+
173
+ @printer.end_hook(hook, status, output) unless @interrupted
174
+
175
+ status
176
+ rescue Interrupt
177
+ @interrupted = true
178
+ ensure
179
+ release_slot(hook)
180
+ end
181
+
182
+ def should_skip?(hook)
183
+ return true if @interrupted || !hook.enabled?
184
+
185
+ if hook.skip?
186
+ if hook.required?
187
+ @printer.required_hook_not_skipped(hook)
188
+ else
189
+ # Tell user if hook was skipped only if it actually would have run
190
+ @printer.hook_skipped(hook) if hook.run?
191
+ return true
192
+ end
193
+ end
194
+
195
+ !hook.run?
196
+ end
197
+
198
+ def load_hooks
199
+ require "overcommit/hook/#{@context.hook_type_name}/base"
200
+
201
+ @hooks += HookLoader::BuiltInHookLoader.new(@config, @context, @log).load_hooks
202
+
203
+ # Load plugin hooks after so they can subclass existing hooks
204
+ @hooks += HookLoader::PluginHookLoader.new(@config, @context, @log).load_hooks
205
+ rescue LoadError => ex
206
+ # Include a more helpful message that will probably save some confusion
207
+ message = 'A load error occurred. ' +
208
+ if @config['gemfile']
209
+ "Did you forget to specify a gem in your `#{@config['gemfile']}`?"
210
+ else
211
+ 'Did you forget to install a gem?'
212
+ end
213
+
214
+ raise Overcommit::Exceptions::HookLoadError,
215
+ "#{message}\n#{ex.message}",
216
+ ex.backtrace
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Overcommit
4
+ # Calculates, stores, and retrieves stored signatures of hook plugins.
5
+ class HookSigner
6
+ attr_reader :hook_name
7
+
8
+ # We don't want to include the skip setting as it is set by Overcommit
9
+ # itself
10
+ IGNORED_CONFIG_KEYS = %w[skip].freeze
11
+
12
+ # @param hook_name [String] name of the hook
13
+ # @param config [Overcommit::Configuration]
14
+ # @param context [Overcommit::HookContext]
15
+ def initialize(hook_name, config, context)
16
+ @hook_name = hook_name
17
+ @config = config
18
+ @context = context
19
+ end
20
+
21
+ # Returns the path of the file that should be incorporated into this hooks
22
+ # signature.
23
+ #
24
+ # @return [String]
25
+ def hook_path
26
+ @hook_path ||= begin
27
+ plugin_path = File.join(@config.plugin_directory,
28
+ @context.hook_type_name,
29
+ "#{Overcommit::Utils.snake_case(@hook_name)}.rb")
30
+
31
+ if File.exist?(plugin_path)
32
+ plugin_path
33
+ else
34
+ # Otherwise this is an ad hoc hook using an existing hook script
35
+ hook_config = @config.for_hook(@hook_name, @context.hook_class_name)
36
+
37
+ command = Array(hook_config['command'] || hook_config['required_executable'])
38
+
39
+ if @config.verify_signatures? &&
40
+ signable_file?(command.first) &&
41
+ !Overcommit::GitRepo.tracked?(command.first)
42
+ raise Overcommit::Exceptions::InvalidHookDefinition,
43
+ 'Hook specified a `required_executable` or `command` that ' \
44
+ 'is a path relative to the root of the repository, and so ' \
45
+ 'must be tracked by Git in order to be signed'
46
+ end
47
+
48
+ File.join(Overcommit::Utils.repo_root, command.first.to_s)
49
+ end
50
+ end
51
+ end
52
+
53
+ def signable_file?(file)
54
+ return unless file
55
+ sep = Overcommit::OS.windows? ? '\\' : File::SEPARATOR
56
+ file.start_with?(".#{sep}") ||
57
+ file.start_with?(Overcommit::Utils.repo_root)
58
+ end
59
+
60
+ # Return whether the signature for this hook has changed since it was last
61
+ # calculated.
62
+ #
63
+ # @return [true,false]
64
+ def signature_changed?
65
+ signature != stored_signature
66
+ end
67
+
68
+ # Update the current stored signature for this hook.
69
+ def update_signature!
70
+ result = Overcommit::Utils.execute(
71
+ %w[git config --local] + [signature_config_key, signature]
72
+ )
73
+
74
+ unless result.success?
75
+ raise Overcommit::Exceptions::GitConfigError,
76
+ "Unable to write to local repo git config: #{result.stderr}"
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ # Calculates a hash of a hook using a combination of its configuration and
83
+ # file contents.
84
+ #
85
+ # This way, if either the plugin code changes or its configuration changes,
86
+ # the hash will change and we can alert the user to this change.
87
+ def signature
88
+ hook_config = @config.for_hook(@hook_name, @context.hook_class_name).
89
+ dup.
90
+ tap { |config| IGNORED_CONFIG_KEYS.each { |k| config.delete(k) } }
91
+
92
+ content_to_sign =
93
+ if signable_file?(hook_path) && Overcommit::GitRepo.tracked?(hook_path)
94
+ hook_contents
95
+ end
96
+
97
+ Digest::SHA256.hexdigest(content_to_sign.to_s + hook_config.to_s)
98
+ end
99
+
100
+ def hook_contents
101
+ File.read(hook_path)
102
+ end
103
+
104
+ def stored_signature
105
+ result = Overcommit::Utils.execute(
106
+ %w[git config --local --get] + [signature_config_key]
107
+ )
108
+
109
+ if result.status == 1 # Key doesn't exist
110
+ return ''
111
+ elsif result.status != 0
112
+ raise Overcommit::Exceptions::GitConfigError,
113
+ "Unable to read from local repo git config: #{result.stderr}"
114
+ end
115
+
116
+ result.stdout.chomp
117
+ end
118
+
119
+ def signature_config_key
120
+ "overcommit.#{@context.hook_class_name}.#{@hook_name}.signature"
121
+ end
122
+ end
123
+ end