overcommit-jeygeethanmedia 0.53.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/overcommit +50 -0
- data/config/default.yml +1356 -0
- data/config/starter.yml +33 -0
- data/lib/overcommit.rb +26 -0
- data/lib/overcommit/cli.rb +223 -0
- data/lib/overcommit/command_splitter.rb +146 -0
- data/lib/overcommit/configuration.rb +350 -0
- data/lib/overcommit/configuration_loader.rb +96 -0
- data/lib/overcommit/configuration_validator.rb +186 -0
- data/lib/overcommit/constants.rb +12 -0
- data/lib/overcommit/exceptions.rb +52 -0
- data/lib/overcommit/git_config.rb +22 -0
- data/lib/overcommit/git_repo.rb +286 -0
- data/lib/overcommit/git_version.rb +17 -0
- data/lib/overcommit/hook/base.rb +294 -0
- data/lib/overcommit/hook/commit_msg/base.rb +14 -0
- data/lib/overcommit/hook/commit_msg/capitalized_subject.rb +25 -0
- data/lib/overcommit/hook/commit_msg/empty_message.rb +12 -0
- data/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +22 -0
- data/lib/overcommit/hook/commit_msg/hard_tabs.rb +17 -0
- data/lib/overcommit/hook/commit_msg/message_format.rb +31 -0
- data/lib/overcommit/hook/commit_msg/russian_novel.rb +16 -0
- data/lib/overcommit/hook/commit_msg/single_line_subject.rb +16 -0
- data/lib/overcommit/hook/commit_msg/spell_check.rb +45 -0
- data/lib/overcommit/hook/commit_msg/text_width.rb +56 -0
- data/lib/overcommit/hook/commit_msg/trailing_period.rb +16 -0
- data/lib/overcommit/hook/post_checkout/base.rb +22 -0
- data/lib/overcommit/hook/post_checkout/bower_install.rb +13 -0
- data/lib/overcommit/hook/post_checkout/bundle_install.rb +13 -0
- data/lib/overcommit/hook/post_checkout/composer_install.rb +13 -0
- data/lib/overcommit/hook/post_checkout/index_tags.rb +12 -0
- data/lib/overcommit/hook/post_checkout/npm_install.rb +13 -0
- data/lib/overcommit/hook/post_checkout/submodule_status.rb +12 -0
- data/lib/overcommit/hook/post_checkout/yarn_install.rb +13 -0
- data/lib/overcommit/hook/post_commit/base.rb +12 -0
- data/lib/overcommit/hook/post_commit/bower_install.rb +13 -0
- data/lib/overcommit/hook/post_commit/bundle_install.rb +13 -0
- data/lib/overcommit/hook/post_commit/commitplease.rb +16 -0
- data/lib/overcommit/hook/post_commit/composer_install.rb +13 -0
- data/lib/overcommit/hook/post_commit/git_guilt.rb +43 -0
- data/lib/overcommit/hook/post_commit/index_tags.rb +12 -0
- data/lib/overcommit/hook/post_commit/npm_install.rb +13 -0
- data/lib/overcommit/hook/post_commit/submodule_status.rb +12 -0
- data/lib/overcommit/hook/post_commit/yarn_install.rb +13 -0
- data/lib/overcommit/hook/post_merge/base.rb +12 -0
- data/lib/overcommit/hook/post_merge/bower_install.rb +13 -0
- data/lib/overcommit/hook/post_merge/bundle_install.rb +13 -0
- data/lib/overcommit/hook/post_merge/composer_install.rb +13 -0
- data/lib/overcommit/hook/post_merge/index_tags.rb +12 -0
- data/lib/overcommit/hook/post_merge/npm_install.rb +13 -0
- data/lib/overcommit/hook/post_merge/submodule_status.rb +12 -0
- data/lib/overcommit/hook/post_merge/yarn_install.rb +13 -0
- data/lib/overcommit/hook/post_rewrite/base.rb +12 -0
- data/lib/overcommit/hook/post_rewrite/bower_install.rb +13 -0
- data/lib/overcommit/hook/post_rewrite/bundle_install.rb +13 -0
- data/lib/overcommit/hook/post_rewrite/composer_install.rb +13 -0
- data/lib/overcommit/hook/post_rewrite/index_tags.rb +19 -0
- data/lib/overcommit/hook/post_rewrite/npm_install.rb +13 -0
- data/lib/overcommit/hook/post_rewrite/submodule_status.rb +12 -0
- data/lib/overcommit/hook/post_rewrite/yarn_install.rb +13 -0
- data/lib/overcommit/hook/pre_commit/author_email.rb +26 -0
- data/lib/overcommit/hook/pre_commit/author_name.rb +25 -0
- data/lib/overcommit/hook/pre_commit/base.rb +19 -0
- data/lib/overcommit/hook/pre_commit/berksfile_check.rb +24 -0
- data/lib/overcommit/hook/pre_commit/broken_symlinks.rb +17 -0
- data/lib/overcommit/hook/pre_commit/bundle_audit.rb +24 -0
- data/lib/overcommit/hook/pre_commit/bundle_check.rb +32 -0
- data/lib/overcommit/hook/pre_commit/bundle_outdated.rb +25 -0
- data/lib/overcommit/hook/pre_commit/case_conflicts.rb +27 -0
- data/lib/overcommit/hook/pre_commit/chamber_compare.rb +43 -0
- data/lib/overcommit/hook/pre_commit/chamber_security.rb +15 -0
- data/lib/overcommit/hook/pre_commit/chamber_verification.rb +36 -0
- data/lib/overcommit/hook/pre_commit/code_spell_check.rb +36 -0
- data/lib/overcommit/hook/pre_commit/coffee_lint.rb +35 -0
- data/lib/overcommit/hook/pre_commit/cook_style.rb +35 -0
- data/lib/overcommit/hook/pre_commit/credo.rb +27 -0
- data/lib/overcommit/hook/pre_commit/css_lint.rb +26 -0
- data/lib/overcommit/hook/pre_commit/dogma.rb +33 -0
- data/lib/overcommit/hook/pre_commit/es_lint.rb +38 -0
- data/lib/overcommit/hook/pre_commit/execute_permissions.rb +76 -0
- data/lib/overcommit/hook/pre_commit/fasterer.rb +25 -0
- data/lib/overcommit/hook/pre_commit/file_size.rb +47 -0
- data/lib/overcommit/hook/pre_commit/fix_me.rb +17 -0
- data/lib/overcommit/hook/pre_commit/flay.rb +38 -0
- data/lib/overcommit/hook/pre_commit/foodcritic.rb +149 -0
- data/lib/overcommit/hook/pre_commit/forbidden_branches.rb +26 -0
- data/lib/overcommit/hook/pre_commit/ginkgo_focus.rb +23 -0
- data/lib/overcommit/hook/pre_commit/go_fmt.rb +17 -0
- data/lib/overcommit/hook/pre_commit/go_lint.rb +29 -0
- data/lib/overcommit/hook/pre_commit/go_vet.rb +24 -0
- data/lib/overcommit/hook/pre_commit/golangci_lint.rb +21 -0
- data/lib/overcommit/hook/pre_commit/hadolint.rb +27 -0
- data/lib/overcommit/hook/pre_commit/haml_lint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/hard_tabs.rb +15 -0
- data/lib/overcommit/hook/pre_commit/hlint.rb +34 -0
- data/lib/overcommit/hook/pre_commit/html_hint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/html_tidy.rb +30 -0
- data/lib/overcommit/hook/pre_commit/image_optim.rb +28 -0
- data/lib/overcommit/hook/pre_commit/java_checkstyle.rb +27 -0
- data/lib/overcommit/hook/pre_commit/js_hint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/js_lint.rb +22 -0
- data/lib/overcommit/hook/pre_commit/jscs.rb +27 -0
- data/lib/overcommit/hook/pre_commit/jsl.rb +28 -0
- data/lib/overcommit/hook/pre_commit/json_syntax.rb +21 -0
- data/lib/overcommit/hook/pre_commit/kt_lint.rb +19 -0
- data/lib/overcommit/hook/pre_commit/license_finder.rb +14 -0
- data/lib/overcommit/hook/pre_commit/license_header.rb +48 -0
- data/lib/overcommit/hook/pre_commit/line_endings.rb +77 -0
- data/lib/overcommit/hook/pre_commit/local_paths_in_gemfile.rb +16 -0
- data/lib/overcommit/hook/pre_commit/mdl.rb +29 -0
- data/lib/overcommit/hook/pre_commit/merge_conflicts.rb +16 -0
- data/lib/overcommit/hook/pre_commit/nginx_test.rb +26 -0
- data/lib/overcommit/hook/pre_commit/pep257.rb +23 -0
- data/lib/overcommit/hook/pre_commit/pep8.rb +23 -0
- data/lib/overcommit/hook/pre_commit/php_cs.rb +43 -0
- data/lib/overcommit/hook/pre_commit/php_cs_fixer.rb +57 -0
- data/lib/overcommit/hook/pre_commit/php_lint.rb +44 -0
- data/lib/overcommit/hook/pre_commit/php_stan.rb +30 -0
- data/lib/overcommit/hook/pre_commit/pronto.rb +12 -0
- data/lib/overcommit/hook/pre_commit/puppet_lint.rb +26 -0
- data/lib/overcommit/hook/pre_commit/puppet_metadata_json_lint.rb +29 -0
- data/lib/overcommit/hook/pre_commit/pycodestyle.rb +23 -0
- data/lib/overcommit/hook/pre_commit/pydocstyle.rb +23 -0
- data/lib/overcommit/hook/pre_commit/pyflakes.rb +32 -0
- data/lib/overcommit/hook/pre_commit/pylint.rb +32 -0
- data/lib/overcommit/hook/pre_commit/python_flake8.rb +32 -0
- data/lib/overcommit/hook/pre_commit/rails_best_practices.rb +34 -0
- data/lib/overcommit/hook/pre_commit/rails_schema_up_to_date.rb +58 -0
- data/lib/overcommit/hook/pre_commit/rake_target.rb +12 -0
- data/lib/overcommit/hook/pre_commit/reek.rb +26 -0
- data/lib/overcommit/hook/pre_commit/rst_lint.rb +27 -0
- data/lib/overcommit/hook/pre_commit/rubo_cop.rb +35 -0
- data/lib/overcommit/hook/pre_commit/ruby_lint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/ruby_syntax.rb +27 -0
- data/lib/overcommit/hook/pre_commit/scalariform.rb +22 -0
- data/lib/overcommit/hook/pre_commit/scalastyle.rb +31 -0
- data/lib/overcommit/hook/pre_commit/scss_lint.rb +43 -0
- data/lib/overcommit/hook/pre_commit/semi_standard.rb +23 -0
- data/lib/overcommit/hook/pre_commit/shell_check.rb +23 -0
- data/lib/overcommit/hook/pre_commit/slim_lint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/sqlint.rb +26 -0
- data/lib/overcommit/hook/pre_commit/standard.rb +23 -0
- data/lib/overcommit/hook/pre_commit/stylelint.rb +23 -0
- data/lib/overcommit/hook/pre_commit/swift_lint.rb +19 -0
- data/lib/overcommit/hook/pre_commit/terraform_format.rb +19 -0
- data/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +15 -0
- data/lib/overcommit/hook/pre_commit/travis_lint.rb +15 -0
- data/lib/overcommit/hook/pre_commit/ts_lint.rb +28 -0
- data/lib/overcommit/hook/pre_commit/vint.rb +22 -0
- data/lib/overcommit/hook/pre_commit/w3c_css.rb +67 -0
- data/lib/overcommit/hook/pre_commit/w3c_html.rb +64 -0
- data/lib/overcommit/hook/pre_commit/xml_lint.rb +24 -0
- data/lib/overcommit/hook/pre_commit/xml_syntax.rb +21 -0
- data/lib/overcommit/hook/pre_commit/yaml_lint.rb +18 -0
- data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +20 -0
- data/lib/overcommit/hook/pre_commit/yard_coverage.rb +90 -0
- data/lib/overcommit/hook/pre_commit/yarn_check.rb +37 -0
- data/lib/overcommit/hook/pre_push/base.rb +33 -0
- data/lib/overcommit/hook/pre_push/brakeman.rb +15 -0
- data/lib/overcommit/hook/pre_push/cargo_test.rb +12 -0
- data/lib/overcommit/hook/pre_push/go_test.rb +14 -0
- data/lib/overcommit/hook/pre_push/golangci_lint.rb +16 -0
- data/lib/overcommit/hook/pre_push/minitest.rb +20 -0
- data/lib/overcommit/hook/pre_push/php_unit.rb +16 -0
- data/lib/overcommit/hook/pre_push/pronto.rb +12 -0
- data/lib/overcommit/hook/pre_push/protected_branches.rb +74 -0
- data/lib/overcommit/hook/pre_push/pytest.rb +16 -0
- data/lib/overcommit/hook/pre_push/python_nose.rb +16 -0
- data/lib/overcommit/hook/pre_push/r_spec.rb +16 -0
- data/lib/overcommit/hook/pre_push/rake_target.rb +12 -0
- data/lib/overcommit/hook/pre_push/test_unit.rb +16 -0
- data/lib/overcommit/hook/pre_rebase/base.rb +14 -0
- data/lib/overcommit/hook/pre_rebase/merged_commits.rb +31 -0
- data/lib/overcommit/hook/prepare_commit_msg/base.rb +25 -0
- data/lib/overcommit/hook/prepare_commit_msg/replace_branch.rb +57 -0
- data/lib/overcommit/hook/shared/bower_install.rb +15 -0
- data/lib/overcommit/hook/shared/bundle_install.rb +15 -0
- data/lib/overcommit/hook/shared/composer_install.rb +15 -0
- data/lib/overcommit/hook/shared/index_tags.rb +14 -0
- data/lib/overcommit/hook/shared/npm_install.rb +15 -0
- data/lib/overcommit/hook/shared/pronto.rb +21 -0
- data/lib/overcommit/hook/shared/rake_target.rb +26 -0
- data/lib/overcommit/hook/shared/submodule_status.rb +32 -0
- data/lib/overcommit/hook/shared/yarn_install.rb +15 -0
- data/lib/overcommit/hook_context.rb +19 -0
- data/lib/overcommit/hook_context/base.rb +139 -0
- data/lib/overcommit/hook_context/commit_msg.rb +48 -0
- data/lib/overcommit/hook_context/post_checkout.rb +36 -0
- data/lib/overcommit/hook_context/post_commit.rb +33 -0
- data/lib/overcommit/hook_context/post_merge.rb +37 -0
- data/lib/overcommit/hook_context/post_rewrite.rb +49 -0
- data/lib/overcommit/hook_context/pre_commit.rb +212 -0
- data/lib/overcommit/hook_context/pre_push.rb +89 -0
- data/lib/overcommit/hook_context/pre_rebase.rb +38 -0
- data/lib/overcommit/hook_context/prepare_commit_msg.rb +34 -0
- data/lib/overcommit/hook_context/run_all.rb +48 -0
- data/lib/overcommit/hook_loader/base.rb +48 -0
- data/lib/overcommit/hook_loader/built_in_hook_loader.rb +14 -0
- data/lib/overcommit/hook_loader/plugin_hook_loader.rb +102 -0
- data/lib/overcommit/hook_runner.rb +219 -0
- data/lib/overcommit/hook_signer.rb +123 -0
- data/lib/overcommit/installer.rb +193 -0
- data/lib/overcommit/interrupt_handler.rb +91 -0
- data/lib/overcommit/logger.rb +92 -0
- data/lib/overcommit/message_processor.rb +148 -0
- data/lib/overcommit/os.rb +38 -0
- data/lib/overcommit/printer.rb +145 -0
- data/lib/overcommit/subprocess.rb +98 -0
- data/lib/overcommit/utils.rb +309 -0
- data/lib/overcommit/utils/file_utils.rb +71 -0
- data/lib/overcommit/utils/messages_utils.rb +77 -0
- data/lib/overcommit/version.rb +6 -0
- data/libexec/gerrit-change-id +174 -0
- data/libexec/index-tags +17 -0
- data/template-dir/hooks/commit-msg +116 -0
- data/template-dir/hooks/overcommit-hook +116 -0
- data/template-dir/hooks/post-checkout +116 -0
- data/template-dir/hooks/post-commit +116 -0
- data/template-dir/hooks/post-merge +116 -0
- data/template-dir/hooks/post-rewrite +116 -0
- data/template-dir/hooks/pre-commit +116 -0
- data/template-dir/hooks/pre-push +116 -0
- data/template-dir/hooks/pre-rebase +116 -0
- data/template-dir/hooks/prepare-commit-msg +116 -0
- 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
|