overcommit-jeygeethanmedia 0.53.1

Sign up to get free protection for your applications and to get access to all the features.
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,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Overcommit
6
+ # Manages configuration file loading.
7
+ class ConfigurationLoader
8
+ DEFAULT_CONFIG_PATH = File.join(Overcommit::HOME, 'config', 'default.yml')
9
+
10
+ class << self
11
+ # Loads and returns the default configuration.
12
+ #
13
+ # @return [Overcommit::Configuration]
14
+ def default_configuration
15
+ @default_configuration ||= load_from_file(DEFAULT_CONFIG_PATH, default: true, verify: false)
16
+ end
17
+
18
+ # Loads configuration from file.
19
+ #
20
+ # @param file [String] path to file
21
+ # @param options [Hash]
22
+ # @option default [Boolean] whether this is the default built-in configuration
23
+ # @option verify [Boolean] whether to verify the signature of the configuration
24
+ # @option logger [Overcommit::Logger]
25
+ # @return [Overcommit::Configuration]
26
+ def load_from_file(file, options = {})
27
+ hash =
28
+ if yaml = YAML.load_file(file)
29
+ yaml.to_hash
30
+ else
31
+ {}
32
+ end
33
+
34
+ Overcommit::Configuration.new(hash, options)
35
+ end
36
+ end
37
+
38
+ # Create a configuration loader which writes warnings/errors to the given
39
+ # {Overcommit::Logger} instance.
40
+ #
41
+ # @param logger [Overcommit::Logger]
42
+ # @param options [Hash]
43
+ # @option verify [Boolean] whether to verify signatures
44
+ def initialize(logger, options = {})
45
+ @log = logger
46
+ @options = options
47
+ end
48
+
49
+ # Loads and returns the configuration for the repository we're running in.
50
+ #
51
+ # @return [Overcommit::Configuration]
52
+ def load_repo_config
53
+ overcommit_yml = File.join(Overcommit::Utils.repo_root,
54
+ Overcommit::CONFIG_FILE_NAME)
55
+
56
+ if File.exist?(overcommit_yml)
57
+ load_file(overcommit_yml)
58
+ else
59
+ self.class.default_configuration
60
+ end
61
+ end
62
+
63
+ # Loads a configuration, ensuring it extends the default configuration.
64
+ def load_file(file)
65
+ config = self.class.load_from_file(file, default: false, logger: @log)
66
+ config = self.class.default_configuration.merge(config)
67
+
68
+ if @options.fetch(:verify) { config.verify_signatures? }
69
+ verify_signatures(config)
70
+ end
71
+
72
+ config
73
+ rescue Overcommit::Exceptions::ConfigurationSignatureChanged
74
+ raise
75
+ rescue StandardError => error
76
+ raise Overcommit::Exceptions::ConfigurationError,
77
+ "Unable to load configuration from '#{file}': #{error}",
78
+ error.backtrace
79
+ end
80
+
81
+ private
82
+
83
+ def verify_signatures(config)
84
+ if !config.previous_signature?
85
+ raise Overcommit::Exceptions::ConfigurationSignatureChanged,
86
+ "No previously recorded signature for configuration file.\n" \
87
+ 'Run `overcommit --sign` if you trust the hooks in this repository.'
88
+
89
+ elsif config.signature_changed?
90
+ raise Overcommit::Exceptions::ConfigurationSignatureChanged,
91
+ "Signature of configuration file has changed!\n" \
92
+ "Run `overcommit --sign` once you've verified the configuration changes."
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength
4
+ module Overcommit
5
+ # Validates and normalizes a configuration.
6
+ class ConfigurationValidator
7
+ # Validates hash for any invalid options, normalizing where possible.
8
+ #
9
+ # @param config [Overcommit::Configuration]
10
+ # @param hash [Hash] hash representation of YAML config
11
+ # @param options[Hash]
12
+ # @option default [Boolean] whether hash represents the default built-in config
13
+ # @option logger [Overcommit::Logger] logger to output warnings to
14
+ # @return [Hash] validated hash (potentially modified)
15
+ def validate(config, hash, options)
16
+ @options = options.dup
17
+ @log = options[:logger]
18
+
19
+ hash = convert_nils_to_empty_hashes(hash)
20
+ ensure_hook_type_sections_exist(hash)
21
+ check_hook_name_format(hash)
22
+ check_hook_env(hash)
23
+ check_for_missing_enabled_option(hash) unless @options[:default]
24
+ check_for_too_many_processors(config, hash)
25
+ check_for_verify_plugin_signatures_option(hash)
26
+
27
+ hash
28
+ end
29
+
30
+ private
31
+
32
+ # Ensures that keys for all supported hook types exist (PreCommit,
33
+ # CommitMsg, etc.)
34
+ def ensure_hook_type_sections_exist(hash)
35
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
36
+ hash[hook_type] ||= {}
37
+ hash[hook_type]['ALL'] ||= {}
38
+ end
39
+ end
40
+
41
+ # Normalizes `nil` values to empty hashes.
42
+ #
43
+ # This is useful for when we want to merge two configuration hashes
44
+ # together, since it's easier to merge two hashes than to have to check if
45
+ # one of the values is nil.
46
+ def convert_nils_to_empty_hashes(hash)
47
+ hash.each_with_object({}) do |(key, value), h|
48
+ h[key] =
49
+ case value
50
+ when nil then {}
51
+ when Hash then convert_nils_to_empty_hashes(value)
52
+ else
53
+ value
54
+ end
55
+ end
56
+ end
57
+
58
+ def check_hook_env(hash)
59
+ errors = []
60
+
61
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
62
+ hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|
63
+ hook_env = hook_config.fetch('env') { {} }
64
+
65
+ unless hook_env.is_a?(Hash)
66
+ errors << "#{hook_type}::#{hook_name} has an invalid `env` specified: " \
67
+ 'must be a hash of environment variable name to string value.'
68
+ next
69
+ end
70
+
71
+ hook_env.each do |var_name, var_value|
72
+ if var_name.include?('=')
73
+ errors << "#{hook_type}::#{hook_name} has an invalid `env` specified: " \
74
+ "variable name `#{var_name}` cannot contain `=`."
75
+ end
76
+
77
+ unless var_value.nil? || var_value.is_a?(String)
78
+ errors << "#{hook_type}::#{hook_name} has an invalid `env` specified: " \
79
+ "value of `#{var_name}` must be a string or `nil`, but was " \
80
+ "#{var_value.inspect} (#{var_value.class})"
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ if errors.any?
87
+ if @log
88
+ @log.error errors.join("\n")
89
+ @log.newline
90
+ end
91
+ raise Overcommit::Exceptions::ConfigurationError,
92
+ 'One or more hooks had an invalid `env` configuration option'
93
+ end
94
+ end
95
+
96
+ # Prints an error message and raises an exception if a hook has an
97
+ # invalid name, since this can result in strange errors elsewhere.
98
+ def check_hook_name_format(hash)
99
+ errors = []
100
+
101
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
102
+ hash.fetch(hook_type) { {} }.each_key do |hook_name|
103
+ next if hook_name == 'ALL'
104
+
105
+ unless hook_name.match?(/\A[A-Za-z0-9]+\z/)
106
+ errors << "#{hook_type}::#{hook_name} has an invalid name " \
107
+ "#{hook_name}. It must contain only alphanumeric " \
108
+ 'characters (no underscores or dashes, etc.)'
109
+ end
110
+ end
111
+ end
112
+
113
+ if errors.any?
114
+ if @log
115
+ @log.error errors.join("\n")
116
+ @log.newline
117
+ end
118
+ raise Overcommit::Exceptions::ConfigurationError,
119
+ 'One or more hooks had invalid names'
120
+ end
121
+ end
122
+
123
+ # Prints a warning if there are any hooks listed in the configuration
124
+ # without `enabled` explicitly set.
125
+ def check_for_missing_enabled_option(hash)
126
+ return unless @log
127
+
128
+ any_warnings = false
129
+
130
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
131
+ hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|
132
+ next if hook_name == 'ALL'
133
+
134
+ if hook_config['enabled'].nil?
135
+ @log.warning "#{hook_type}::#{hook_name} hook does not explicitly " \
136
+ 'set `enabled` option in .overcommit.yml'
137
+ any_warnings = true
138
+ end
139
+ end
140
+ end
141
+
142
+ @log.newline if any_warnings
143
+ end
144
+
145
+ # Prints a warning if any hook has a number of processors larger than the
146
+ # global `concurrency` setting.
147
+ def check_for_too_many_processors(config, hash)
148
+ concurrency = config.concurrency
149
+
150
+ errors = []
151
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
152
+ hash.fetch(hook_type) { {} }.each do |hook_name, hook_config|
153
+ processors = hook_config.fetch('processors') { 1 }
154
+ if processors > concurrency
155
+ errors << "#{hook_type}::#{hook_name} `processors` value " \
156
+ "(#{processors}) is larger than the global `concurrency` " \
157
+ "option (#{concurrency})"
158
+ end
159
+ end
160
+ end
161
+
162
+ if errors.any?
163
+ if @log
164
+ @log.error errors.join("\n")
165
+ @log.newline
166
+ end
167
+ raise Overcommit::Exceptions::ConfigurationError,
168
+ 'One or more hooks had invalid `processor` value configured'
169
+ end
170
+ end
171
+
172
+ # Prints a warning if the `verify_plugin_signatures` option is used instead
173
+ # of the new `verify_signatures` option.
174
+ def check_for_verify_plugin_signatures_option(hash)
175
+ return unless @log
176
+
177
+ if hash.key?('verify_plugin_signatures')
178
+ @log.warning '`verify_plugin_signatures` has been renamed to ' \
179
+ '`verify_signatures`. Defaulting to verifying signatures.'
180
+ @log.warning "See change log at #{REPO_URL}/blob/v0.29.0/CHANGELOG.md for details."
181
+ @log.newline
182
+ end
183
+ end
184
+ end
185
+ end
186
+ # rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity, Metrics/MethodLength
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Global application constants.
4
+ module Overcommit
5
+ HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
6
+ CONFIG_FILE_NAME = '.overcommit.yml'
7
+
8
+ HOOK_DIRECTORY = File.join(HOME, 'lib', 'overcommit', 'hook').freeze
9
+
10
+ REPO_URL = 'https://github.com/sds/overcommit'
11
+ BUG_REPORT_URL = "#{REPO_URL}/issues"
12
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Overcommit::Exceptions
4
+ # Raised when a {Configuration} could not be loaded from a file.
5
+ class ConfigurationError < StandardError; end
6
+
7
+ # Raised when the Overcommit configuration file signature has changed.
8
+ class ConfigurationSignatureChanged < StandardError; end
9
+
10
+ # Raised when trying to read/write to/from the local repo git config fails.
11
+ class GitConfigError < StandardError; end
12
+
13
+ # Raised when there was a problem reading submodule information for a repo.
14
+ class GitSubmoduleError < StandardError; end
15
+
16
+ # Raised when there was a problem reading git revision information with `rev-list`.
17
+ class GitRevListError < StandardError; end
18
+
19
+ # Raised when a {HookContext} is unable to setup the environment before a run.
20
+ class HookSetupFailed < StandardError; end
21
+
22
+ # Raised when a {HookContext} is unable to clean the environment after a run.
23
+ class HookCleanupFailed < StandardError; end
24
+
25
+ # Raised when a hook run was cancelled by the user.
26
+ class HookCancelled < StandardError; end
27
+
28
+ # Raised when a hook could not be loaded by a {HookRunner}.
29
+ class HookLoadError < StandardError; end
30
+
31
+ # Raised when a {HookRunner} could not be loaded.
32
+ class HookContextLoadError < StandardError; end
33
+
34
+ # Raised when a pipe character is used in the `execute` helper, as this was
35
+ # likely used in error.
36
+ class InvalidCommandArgs < StandardError; end
37
+
38
+ # Raised when an installation target is not a valid git repository.
39
+ class InvalidGitRepo < StandardError; end
40
+
41
+ # Raised when a hook was defined incorrectly.
42
+ class InvalidHookDefinition < StandardError; end
43
+
44
+ # Raised when one or more hook plugin signatures have changed.
45
+ class InvalidHookSignature < StandardError; end
46
+
47
+ # Raised when there is a problem processing output into {Hook::Messages}s.
48
+ class MessageProcessingError < StandardError; end
49
+
50
+ # Raised when an installation target already contains non-Overcommit hooks.
51
+ class PreExistingHooks < StandardError; end
52
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'overcommit/utils'
4
+
5
+ module Overcommit
6
+ # Get configuration options from git
7
+ module GitConfig
8
+ module_function
9
+
10
+ def comment_character
11
+ char = `git config --get core.commentchar`.chomp
12
+ char = '#' if char == ''
13
+ char
14
+ end
15
+
16
+ def hooks_path
17
+ path = `git config --get core.hooksPath`.chomp
18
+ return File.join(Overcommit::Utils.git_dir, 'hooks') if path.empty?
19
+ File.absolute_path(path, Dir.pwd)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,286 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'iniparse'
4
+ require 'shellwords'
5
+
6
+ module Overcommit
7
+ # Provide a set of utilities for certain interactions with `git`.
8
+ module GitRepo
9
+ module_function
10
+
11
+ # Regular expression used to extract diff ranges from hunks of diff output.
12
+ DIFF_HUNK_REGEX = /
13
+ ^@@\s
14
+ [^\s]+\s # Ignore old file range
15
+ \+(\d+)(?:,(\d+))? # Extract range of hunk containing start line and number of lines
16
+ \s@@.*$
17
+ /x
18
+
19
+ # Regular expression used to extract information from lines of
20
+ # `git submodule status` output
21
+ SUBMODULE_STATUS_REGEX = /
22
+ ^\s*(?<prefix>[-+U]?)(?<sha1>\w+)
23
+ \s(?<path>[^\s]+?)
24
+ (?:\s\((?<describe>.+)\))?$
25
+ /x
26
+
27
+ # Struct encapsulating submodule information extracted from the
28
+ # output of `git submodule status`
29
+ SubmoduleStatus = Struct.new(:prefix, :sha1, :path, :describe) do
30
+ # Returns whether the submodule has not been initialized
31
+ def uninitialized?
32
+ prefix == '-'
33
+ end
34
+
35
+ # Returns whether the submodule is out of date with the current
36
+ # index, i.e. its checked-out commit differs from that stored in
37
+ # the index of the parent repo
38
+ def outdated?
39
+ prefix == '+'
40
+ end
41
+
42
+ # Returns whether the submodule reference has a merge conflict
43
+ def merge_conflict?
44
+ prefix == 'U'
45
+ end
46
+ end
47
+
48
+ # Returns a list of SubmoduleStatus objects, one for each submodule in the
49
+ # parent repository.
50
+ #
51
+ # @option options [Boolean] recursive check submodules recursively
52
+ # @return [Array<SubmoduleStatus>]
53
+ def submodule_statuses(options = {})
54
+ flags = '--recursive' if options[:recursive]
55
+
56
+ `git submodule status #{flags}`.
57
+ scan(SUBMODULE_STATUS_REGEX).
58
+ map do |prefix, sha1, path, describe|
59
+ SubmoduleStatus.new(prefix, sha1, path, describe)
60
+ end
61
+ end
62
+
63
+ # Extract the set of modified lines from a given file.
64
+ #
65
+ # @param file_path [String]
66
+ # @param options [Hash]
67
+ # @return [Set] line numbers that have been modified in file
68
+ def extract_modified_lines(file_path, options)
69
+ lines = Set.new
70
+
71
+ flags = '--cached' if options[:staged]
72
+ refs = options[:refs]
73
+ subcmd = options[:subcmd] || 'diff'
74
+
75
+ `git #{subcmd} --no-color --no-ext-diff -U0 #{flags} #{refs} -- "#{file_path}"`.
76
+ scan(DIFF_HUNK_REGEX) do |start_line, lines_added|
77
+ lines_added = (lines_added || 1).to_i # When blank, one line was added
78
+ cur_line = start_line.to_i
79
+
80
+ lines_added.times do
81
+ lines.add cur_line
82
+ cur_line += 1
83
+ end
84
+ end
85
+
86
+ lines
87
+ end
88
+
89
+ # Returns the names of all files that have been modified compared to HEAD.
90
+ #
91
+ # @param options [Hash]
92
+ # @return [Array<String>] list of absolute file paths
93
+ def modified_files(options)
94
+ flags = '--cached' if options[:staged]
95
+ refs = options[:refs]
96
+ subcmd = options[:subcmd] || 'diff'
97
+
98
+ `git #{subcmd} --name-only -z --diff-filter=ACMR --ignore-submodules=all #{flags} #{refs}`.
99
+ split("\0").
100
+ map(&:strip).
101
+ reject(&:empty?).
102
+ map { |relative_file| File.expand_path(relative_file) }
103
+ end
104
+
105
+ # Returns the names of files in the given paths that are tracked by git.
106
+ #
107
+ # @param paths [Array<String>] list of paths to check
108
+ # @option options [String] ref ('HEAD') Git ref to check
109
+ # @return [Array<String>] list of absolute file paths
110
+ def list_files(paths = [], options = {})
111
+ ref = options[:ref] || 'HEAD'
112
+ path_list =
113
+ if OS.windows?
114
+ paths = paths.map { |path| path.gsub('"', '""') }
115
+ paths.empty? ? '' : "\"#{paths.join('" "')}\""
116
+ else
117
+ paths.shelljoin
118
+ end
119
+ `git ls-tree --name-only #{ref} #{path_list}`.
120
+ split(/\n/).
121
+ map { |relative_file| File.expand_path(relative_file) }.
122
+ reject { |file| File.directory?(file) } # Exclude submodule directories
123
+ end
124
+
125
+ # Returns whether the specified file/path is tracked by this repository.
126
+ #
127
+ # @param path [String]
128
+ # @return [true,false]
129
+ def tracked?(path)
130
+ Overcommit::Utils.execute(%W[git ls-files #{path} --error-unmatch]).success?
131
+ end
132
+
133
+ # Returns the names of all files that are tracked by git.
134
+ #
135
+ # @return [Array<String>] list of absolute file paths
136
+ def all_files
137
+ `git ls-files`.
138
+ split(/\n/).
139
+ map { |relative_file| File.expand_path(relative_file) }.
140
+ reject { |file| File.directory?(file) } # Exclude submodule directories
141
+ end
142
+
143
+ # Returns whether the current git branch is empty (has no commits).
144
+ # @return [true,false]
145
+ def initial_commit?
146
+ !Overcommit::Utils.execute(%w[git rev-parse HEAD]).success?
147
+ end
148
+
149
+ # Store any relevant files that are present when repo is in the middle of a
150
+ # merge.
151
+ #
152
+ # Restored via [#restore_merge_state].
153
+ def store_merge_state
154
+ merge_head = `git rev-parse MERGE_HEAD 2> #{File::NULL}`.chomp
155
+
156
+ # Store the merge state if we're in the middle of resolving a merge
157
+ # conflict. This is necessary since stashing removes the merge state.
158
+ if merge_head != 'MERGE_HEAD'
159
+ @merge_head = merge_head
160
+ end
161
+
162
+ merge_msg_file = File.expand_path('MERGE_MSG', Overcommit::Utils.git_dir)
163
+ @merge_msg = File.open(merge_msg_file).read if File.exist?(merge_msg_file)
164
+ end
165
+
166
+ # Store any relevant files that are present when repo is in the middle of a
167
+ # cherry-pick.
168
+ #
169
+ # Restored via [#restore_cherry_pick_state].
170
+ def store_cherry_pick_state
171
+ cherry_head = `git rev-parse CHERRY_PICK_HEAD 2> #{File::NULL}`.chomp
172
+
173
+ # Store the merge state if we're in the middle of resolving a merge
174
+ # conflict. This is necessary since stashing removes the merge state.
175
+ if cherry_head != 'CHERRY_PICK_HEAD'
176
+ @cherry_head = cherry_head
177
+ end
178
+ end
179
+
180
+ # Restore any relevant files that were present when repo was in the middle
181
+ # of a merge.
182
+ def restore_merge_state
183
+ if @merge_head
184
+ FileUtils.touch(File.expand_path('MERGE_MODE', Overcommit::Utils.git_dir))
185
+
186
+ File.open(File.expand_path('MERGE_HEAD', Overcommit::Utils.git_dir), 'w') do |f|
187
+ f.write(@merge_head)
188
+ end
189
+ @merge_head = nil
190
+ end
191
+
192
+ if @merge_msg
193
+ File.open(File.expand_path('MERGE_MSG', Overcommit::Utils.git_dir), 'w') do |f|
194
+ f.write("#{@merge_msg}\n")
195
+ end
196
+ @merge_msg = nil
197
+ end
198
+ end
199
+
200
+ # Restore any relevant files that were present when repo was in the middle
201
+ # of a cherry-pick.
202
+ def restore_cherry_pick_state
203
+ if @cherry_head
204
+ File.open(File.expand_path('CHERRY_PICK_HEAD',
205
+ Overcommit::Utils.git_dir), 'w') do |f|
206
+ f.write(@cherry_head)
207
+ end
208
+ @cherry_head = nil
209
+ end
210
+ end
211
+
212
+ # Contains information about a registered submodule.
213
+ Submodule = Struct.new(:path, :url)
214
+
215
+ # Returns the submodules that have been staged for removal.
216
+ #
217
+ # `git` has an unexpected behavior where removing a submodule without
218
+ # committing (i.e. such that the submodule directory is removed and the
219
+ # changes to the index are staged) and then doing a hard reset results in
220
+ # the index being wiped but the empty directory of the once existent
221
+ # submodule being restored (but with no content).
222
+ #
223
+ # This prevents restoration of the stash of the submodule index changes,
224
+ # which breaks pre-commit hook restorations of the working index.
225
+ #
226
+ # Thus we expose this helper so the restoration code can manually delete the
227
+ # directory.
228
+ #
229
+ # @raise [Overcommit::Exceptions::GitSubmoduleError] when
230
+ def staged_submodule_removals
231
+ # There were no submodules before, so none could have been removed
232
+ return [] if `git ls-files .gitmodules`.empty?
233
+
234
+ previous = submodules(ref: 'HEAD')
235
+ current = submodules
236
+
237
+ previous - current
238
+ end
239
+
240
+ # Returns the current set of registered submodules.
241
+ #
242
+ # @param options [Hash]
243
+ # @return [Array<Overcommit::GitRepo::Submodule>]
244
+ def submodules(options = {})
245
+ ref = options[:ref]
246
+
247
+ modules = []
248
+ IniParse.parse(`git show #{ref}:.gitmodules 2> #{File::NULL}`).each do |section|
249
+ # git < 1.8.5 does not update the .gitmodules file with submodule
250
+ # changes, so when we are looking at the current state of the work tree,
251
+ # we need to check if the submodule actually exists via another method,
252
+ # since the .gitmodules file we parsed does not represent reality.
253
+ if ref.nil? && GIT_VERSION < '1.8.5'
254
+ result = Overcommit::Utils.execute(%W[
255
+ git submodule status #{section['path']}
256
+ ])
257
+ next unless result.success?
258
+ end
259
+
260
+ modules << Submodule.new(section['path'], section['url'])
261
+ end
262
+
263
+ modules
264
+ rescue IniParse::IniParseError => ex
265
+ raise Overcommit::Exceptions::GitSubmoduleError,
266
+ "Unable to read submodule information from #{ref}:.gitmodules file: #{ex.message}"
267
+ end
268
+
269
+ # Returns the names of all branches containing the given commit.
270
+ #
271
+ # @param commit_ref [String] git tree ref that resolves to a commit
272
+ # @return [Array<String>] list of branches containing the given commit
273
+ def branches_containing_commit(commit_ref)
274
+ `git branch --column=dense --contains #{commit_ref}`.
275
+ sub(/\((HEAD )?detached (from|at) .*?\)/, ''). # ignore detached HEAD
276
+ split(/\s+/).
277
+ reject { |s| s.empty? || s == '*' }
278
+ end
279
+
280
+ # Returns the name of the currently checked out branch.
281
+ # @return [String]
282
+ def current_branch
283
+ `git symbolic-ref --short -q HEAD`.chomp
284
+ end
285
+ end
286
+ end