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.
- 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,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rbconfig'
|
|
4
|
+
|
|
5
|
+
module Overcommit
|
|
6
|
+
# Methods relating to the current operating system
|
|
7
|
+
module OS
|
|
8
|
+
class << self
|
|
9
|
+
def windows?
|
|
10
|
+
!(/mswin|msys|mingw|bccwin|wince|emc/ =~ host_os).nil?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def cygwin?
|
|
14
|
+
!(/cygwin/ =~ host_os).nil?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def mac?
|
|
18
|
+
!(/darwin|mac os/ =~ host_os).nil?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def unix?
|
|
22
|
+
!windows?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def linux?
|
|
26
|
+
unix? && !mac? && !cygwin?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def host_os
|
|
32
|
+
@host_os ||= ::RbConfig::CONFIG['host_os'].freeze
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
SEPARATOR = (windows? ? '\\' : File::SEPARATOR).freeze
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'monitor'
|
|
4
|
+
|
|
5
|
+
module Overcommit
|
|
6
|
+
# Provide a set of callbacks which can be executed as events occur during the
|
|
7
|
+
# course of {HookRunner#run}.
|
|
8
|
+
class Printer
|
|
9
|
+
attr_reader :log
|
|
10
|
+
|
|
11
|
+
def initialize(config, logger, context)
|
|
12
|
+
@config = config
|
|
13
|
+
@log = logger
|
|
14
|
+
@context = context
|
|
15
|
+
@lock = Monitor.new # Need to use monitor so we can have re-entrant locks
|
|
16
|
+
synchronize_all_methods
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Executed at the very beginning of running the collection of hooks.
|
|
20
|
+
def start_run
|
|
21
|
+
log.bold "Running #{hook_script_name} hooks" unless @config['quiet']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def nothing_to_run
|
|
25
|
+
log.debug "✓ No applicable #{hook_script_name} hooks to run"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def hook_skipped(hook)
|
|
29
|
+
log.warning "Skipping #{hook.name}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def required_hook_not_skipped(hook)
|
|
33
|
+
log.warning "Cannot skip #{hook.name} since it is required"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Executed at the end of an individual hook run.
|
|
37
|
+
def end_hook(hook, status, output)
|
|
38
|
+
# Want to print the header for quiet hooks only if the result wasn't good
|
|
39
|
+
# so that the user knows what failed
|
|
40
|
+
print_header(hook) if (!hook.quiet? && !@config['quiet']) || status != :pass
|
|
41
|
+
|
|
42
|
+
print_result(hook, status, output)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def interrupt_triggered
|
|
46
|
+
log.newline
|
|
47
|
+
log.error 'Interrupt signal received. Stopping hooks...'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Executed when a hook run was interrupted/cancelled by user.
|
|
51
|
+
def run_interrupted
|
|
52
|
+
log.newline
|
|
53
|
+
log.warning '⚠ Hook run interrupted by user'
|
|
54
|
+
log.warning '⚠ If files appear modified/missing, check your stash to recover them'
|
|
55
|
+
log.newline
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Executed when one or more hooks by the end of the run.
|
|
59
|
+
def run_failed
|
|
60
|
+
log.newline
|
|
61
|
+
log.error "✗ One or more #{hook_script_name} hooks failed"
|
|
62
|
+
log.newline
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Executed when no hooks failed by the end of the run, but some warned.
|
|
66
|
+
def run_warned
|
|
67
|
+
log.newline
|
|
68
|
+
log.warning "⚠ All #{hook_script_name} hooks passed, but with warnings"
|
|
69
|
+
log.newline
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Executed when no hooks failed by the end of the run.
|
|
73
|
+
def run_succeeded
|
|
74
|
+
unless @config['quiet']
|
|
75
|
+
log.newline
|
|
76
|
+
log.success "✓ All #{hook_script_name} hooks passed"
|
|
77
|
+
log.newline
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def hook_run_failed(message)
|
|
82
|
+
log.newline
|
|
83
|
+
log.log message
|
|
84
|
+
log.newline
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def print_header(hook)
|
|
90
|
+
hook_name = "[#{hook.name}] "
|
|
91
|
+
log.partial hook.description
|
|
92
|
+
log.partial '.' * [70 - hook.description.length - hook_name.length, 0].max
|
|
93
|
+
log.partial hook_name
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def print_result(hook, status, output) # rubocop:disable Metrics/CyclomaticComplexity
|
|
97
|
+
case status
|
|
98
|
+
when :pass
|
|
99
|
+
log.success 'OK' unless @config['quiet'] || hook.quiet?
|
|
100
|
+
print_report(output)
|
|
101
|
+
when :warn
|
|
102
|
+
log.warning 'WARNING'
|
|
103
|
+
print_report(output, :bold_warning)
|
|
104
|
+
when :fail
|
|
105
|
+
log.error 'FAILED'
|
|
106
|
+
print_report(output, :bold_error)
|
|
107
|
+
when :interrupt
|
|
108
|
+
log.error 'INTERRUPTED'
|
|
109
|
+
print_report(output, :bold_error)
|
|
110
|
+
else
|
|
111
|
+
log.error '???'
|
|
112
|
+
print_report("Hook returned unknown status `#{status.inspect}` -- ignoring.",
|
|
113
|
+
:bold_error)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def print_report(output, format = :log)
|
|
118
|
+
log.send(format, output) unless output.nil? || output.empty?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def hook_script_name
|
|
122
|
+
@context.hook_script_name
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Get all public methods that were defined on this class and wrap them with
|
|
126
|
+
# synchronization locks so we ensure the output isn't interleaved amongst
|
|
127
|
+
# the various threads.
|
|
128
|
+
def synchronize_all_methods
|
|
129
|
+
methods = self.class.instance_methods - self.class.superclass.instance_methods
|
|
130
|
+
|
|
131
|
+
methods.each do |method_name|
|
|
132
|
+
old_method = :"old_#{method_name}"
|
|
133
|
+
new_method = :"synchronized_#{method_name}"
|
|
134
|
+
|
|
135
|
+
self.class.__send__(:alias_method, old_method, method_name)
|
|
136
|
+
|
|
137
|
+
self.class.send(:define_method, new_method) do |*args|
|
|
138
|
+
@lock.synchronize { __send__(old_method, *args) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
self.class.__send__(:alias_method, method_name, new_method)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'childprocess'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
module Overcommit
|
|
7
|
+
# Manages execution of a child process, collecting the exit status and
|
|
8
|
+
# standard out/error output.
|
|
9
|
+
class Subprocess
|
|
10
|
+
# Encapsulates the result of a process.
|
|
11
|
+
#
|
|
12
|
+
# @attr_reader status [Integer] exit status code returned by process
|
|
13
|
+
# @attr_reader stdout [String] standard output stream output
|
|
14
|
+
# @attr_reader stderr [String] standard error stream output
|
|
15
|
+
Result = Struct.new(:status, :stdout, :stderr) do
|
|
16
|
+
def success?
|
|
17
|
+
status == 0
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# Spawns a new process using the given array of arguments (the first
|
|
23
|
+
# element is the command).
|
|
24
|
+
#
|
|
25
|
+
# @param args [Array<String>]
|
|
26
|
+
# @param options [Hash]
|
|
27
|
+
# @option options [String] input string to pass via standard input stream
|
|
28
|
+
# @return [Result]
|
|
29
|
+
def spawn(args, options = {})
|
|
30
|
+
args = win32_prepare_args(args) if OS.windows?
|
|
31
|
+
|
|
32
|
+
process = ChildProcess.build(*args)
|
|
33
|
+
|
|
34
|
+
out, err = assign_output_streams(process)
|
|
35
|
+
|
|
36
|
+
process.duplex = true if options[:input] # Make stdin available if needed
|
|
37
|
+
process.start
|
|
38
|
+
if options[:input]
|
|
39
|
+
begin
|
|
40
|
+
process.io.stdin.puts(options[:input])
|
|
41
|
+
rescue StandardError # rubocop:disable Lint/HandleExceptions
|
|
42
|
+
# Silently ignore if the standard input stream of the spawned
|
|
43
|
+
# process is closed before we get a chance to write to it. This
|
|
44
|
+
# happens on JRuby a lot.
|
|
45
|
+
ensure
|
|
46
|
+
process.io.stdin.close
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
process.wait
|
|
50
|
+
|
|
51
|
+
err.rewind
|
|
52
|
+
out.rewind
|
|
53
|
+
|
|
54
|
+
Result.new(process.exit_code, out.read, err.read)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Spawns a new process in the background using the given array of
|
|
58
|
+
# arguments (the first element is the command).
|
|
59
|
+
def spawn_detached(args)
|
|
60
|
+
args = win32_prepare_args(args) if OS.windows?
|
|
61
|
+
|
|
62
|
+
process = ChildProcess.build(*args)
|
|
63
|
+
process.detach = true
|
|
64
|
+
|
|
65
|
+
assign_output_streams(process)
|
|
66
|
+
|
|
67
|
+
process.start
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Necessary to run commands in the cmd.exe context.
|
|
73
|
+
# Args are joined to properly handle quotes and special characters.
|
|
74
|
+
def win32_prepare_args(args)
|
|
75
|
+
args = args.map do |arg|
|
|
76
|
+
# Quote args that contain whitespace
|
|
77
|
+
arg = "\"#{arg}\"" if arg =~ /\s/
|
|
78
|
+
|
|
79
|
+
# Escape cmd.exe metacharacters
|
|
80
|
+
arg.gsub(/[()%!^"<>&|]/, '^\0')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
%w[cmd.exe /c] + [args.join(' ')]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @param process [ChildProcess]
|
|
87
|
+
# @return [Array<IO>]
|
|
88
|
+
def assign_output_streams(process)
|
|
89
|
+
%w[out err].map do |stream_name|
|
|
90
|
+
::Tempfile.new(stream_name).tap do |stream|
|
|
91
|
+
stream.sync = true
|
|
92
|
+
process.io.send("std#{stream_name}=", stream)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'overcommit/os'
|
|
5
|
+
require 'overcommit/subprocess'
|
|
6
|
+
require 'overcommit/command_splitter'
|
|
7
|
+
require 'tempfile'
|
|
8
|
+
|
|
9
|
+
module Overcommit
|
|
10
|
+
# Utility functions for general use.
|
|
11
|
+
module Utils
|
|
12
|
+
# Helper class for doing quick constraint validations on version numbers.
|
|
13
|
+
#
|
|
14
|
+
# This allows us to execute code based on the git version.
|
|
15
|
+
class Version < Gem::Version
|
|
16
|
+
# Overload comparison operators so we can conveniently compare this
|
|
17
|
+
# version directly to a string in code.
|
|
18
|
+
%w[< <= > >= == !=].each do |operator|
|
|
19
|
+
define_method operator do |version|
|
|
20
|
+
case version
|
|
21
|
+
when String
|
|
22
|
+
super(Gem::Version.new(version))
|
|
23
|
+
else
|
|
24
|
+
super(version)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# @return [Overcommit::Logger] logger with which to send debug output
|
|
32
|
+
attr_accessor :log
|
|
33
|
+
|
|
34
|
+
def script_path(script)
|
|
35
|
+
File.join(Overcommit::HOME, 'libexec', script)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns an absolute path to the root of the repository.
|
|
39
|
+
#
|
|
40
|
+
# We do this ourselves rather than call `git rev-parse --show-toplevel` to
|
|
41
|
+
# solve an issue where the .git directory might not actually be valid in
|
|
42
|
+
# tests.
|
|
43
|
+
#
|
|
44
|
+
# @return [String]
|
|
45
|
+
def repo_root
|
|
46
|
+
@repo_root ||=
|
|
47
|
+
begin
|
|
48
|
+
result = execute(%w[git rev-parse --show-toplevel])
|
|
49
|
+
unless result.success?
|
|
50
|
+
raise Overcommit::Exceptions::InvalidGitRepo,
|
|
51
|
+
'Unable to determine location of GIT_DIR. ' \
|
|
52
|
+
'Not a recognizable Git repository!'
|
|
53
|
+
end
|
|
54
|
+
result.stdout.chomp("\n")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns an absolute path to the .git directory for a repo.
|
|
59
|
+
#
|
|
60
|
+
# @return [String]
|
|
61
|
+
def git_dir
|
|
62
|
+
@git_dir ||=
|
|
63
|
+
begin
|
|
64
|
+
cmd = %w[git rev-parse]
|
|
65
|
+
cmd << (GIT_VERSION < '2.5' ? '--git-dir' : '--git-common-dir')
|
|
66
|
+
result = execute(cmd)
|
|
67
|
+
unless result.success?
|
|
68
|
+
raise Overcommit::Exceptions::InvalidGitRepo,
|
|
69
|
+
'Unable to determine location of GIT_DIR. ' \
|
|
70
|
+
'Not a recognizable Git repository!'
|
|
71
|
+
end
|
|
72
|
+
File.expand_path(result.stdout.chomp("\n"), Dir.pwd)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Remove ANSI escape sequences from a string.
|
|
77
|
+
#
|
|
78
|
+
# This is useful for stripping colorized output from external tools.
|
|
79
|
+
#
|
|
80
|
+
# @param text [String]
|
|
81
|
+
# @return [String]
|
|
82
|
+
def strip_color_codes(text)
|
|
83
|
+
text.gsub(/\e\[(\d+)(;\d+)*m/, '')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Shamelessly stolen from:
|
|
87
|
+
# stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby
|
|
88
|
+
def snake_case(str)
|
|
89
|
+
str.gsub(/::/, '/').
|
|
90
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
|
91
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
|
92
|
+
tr('-', '_').
|
|
93
|
+
downcase
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Converts a string containing underscores/hyphens/spaces into CamelCase.
|
|
97
|
+
def camel_case(str)
|
|
98
|
+
str.split(/_|-| /).map { |part| part.sub(/^\w/, &:upcase) }.join
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns a list of supported hook types (pre-commit, commit-msg, etc.)
|
|
102
|
+
def supported_hook_types
|
|
103
|
+
Dir[File.join(HOOK_DIRECTORY, '*')].
|
|
104
|
+
select { |file| File.directory?(file) }.
|
|
105
|
+
reject { |file| File.basename(file) == 'shared' }.
|
|
106
|
+
map { |file| File.basename(file).tr('_', '-') }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns a list of supported hook classes (PreCommit, CommitMsg, etc.)
|
|
110
|
+
def supported_hook_type_classes
|
|
111
|
+
supported_hook_types.map do |file|
|
|
112
|
+
file.split('-').map(&:capitalize).join
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @param cmd [String]
|
|
117
|
+
# @return [true,false] whether a command can be found given the current
|
|
118
|
+
# environment path.
|
|
119
|
+
def in_path?(cmd)
|
|
120
|
+
# ENV['PATH'] doesn't include the repo root, but that is a valid
|
|
121
|
+
# location for executables, so we want to add it to the list of places
|
|
122
|
+
# we are checking for the executable.
|
|
123
|
+
paths = [repo_root] + ENV['PATH'].split(File::PATH_SEPARATOR)
|
|
124
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
|
125
|
+
paths.each do |path|
|
|
126
|
+
exts.each do |ext|
|
|
127
|
+
cmd_with_ext = cmd.upcase.end_with?(ext.upcase) ? cmd : "#{cmd}#{ext}"
|
|
128
|
+
full_path = File.join(path, cmd_with_ext)
|
|
129
|
+
return true if File.executable?(full_path)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Return the parent command that triggered this hook run
|
|
136
|
+
#
|
|
137
|
+
# @return [String,nil] the command as a string, if a parent exists.
|
|
138
|
+
def parent_command
|
|
139
|
+
# When run in Docker containers, there may be no parent process.
|
|
140
|
+
return if Process.ppid.zero?
|
|
141
|
+
|
|
142
|
+
if OS.windows?
|
|
143
|
+
`wmic process where ProcessId=#{Process.ppid} get CommandLine /FORMAT:VALUE`.
|
|
144
|
+
strip.
|
|
145
|
+
slice(/(?<=CommandLine=).+/)
|
|
146
|
+
elsif OS.cygwin?
|
|
147
|
+
# Cygwin's `ps` command behaves differently than the traditional
|
|
148
|
+
# Linux version, but a comparable `procps` is provided to compensate.
|
|
149
|
+
`procps -ocommand= -p #{Process.ppid}`.chomp
|
|
150
|
+
else
|
|
151
|
+
`ps -ocommand= -p #{Process.ppid}`.chomp
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Execute a command in a subprocess, capturing exit status and output from
|
|
156
|
+
# both standard and error streams.
|
|
157
|
+
#
|
|
158
|
+
# This is intended to provide a centralized place to perform any checks or
|
|
159
|
+
# filtering of the command before executing it.
|
|
160
|
+
#
|
|
161
|
+
# The `args` option provides a convenient way of splitting up long
|
|
162
|
+
# argument lists which would otherwise exceed the maximum command line
|
|
163
|
+
# length of the OS. It will break up the list into chunks and run the
|
|
164
|
+
# command with the same prefix `initial_args`, finally combining the
|
|
165
|
+
# output together at the end.
|
|
166
|
+
#
|
|
167
|
+
# This requires that the external command you are running can have its
|
|
168
|
+
# work split up in this way and still produce the same resultant output
|
|
169
|
+
# when outputs of the individual commands are concatenated back together.
|
|
170
|
+
#
|
|
171
|
+
# @param initial_args [Array<String>]
|
|
172
|
+
# @param options [Hash]
|
|
173
|
+
# @option options [Array<String>] :args long list of arguments to split up
|
|
174
|
+
# @return [Overcommit::Subprocess::Result] status, stdout, and stderr
|
|
175
|
+
def execute(initial_args, options = {})
|
|
176
|
+
if initial_args.include?('|')
|
|
177
|
+
raise Overcommit::Exceptions::InvalidCommandArgs,
|
|
178
|
+
'Cannot pipe commands with the `execute` helper'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
result =
|
|
182
|
+
if (splittable_args = options.fetch(:args) { [] }).any?
|
|
183
|
+
debug(initial_args.join(' ') + " ... (#{splittable_args.length} splittable args)")
|
|
184
|
+
Overcommit::CommandSplitter.execute(initial_args, options)
|
|
185
|
+
else
|
|
186
|
+
debug(initial_args.join(' '))
|
|
187
|
+
Overcommit::Subprocess.spawn(initial_args, options)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
debug("EXIT STATUS: #{result.status}")
|
|
191
|
+
debug("STDOUT: #{result.stdout.inspect}")
|
|
192
|
+
debug("STDERR: #{result.stderr.inspect}")
|
|
193
|
+
|
|
194
|
+
result
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Execute a command in a subprocess, returning immediately.
|
|
198
|
+
#
|
|
199
|
+
# This provides a convenient way to execute long-running processes for
|
|
200
|
+
# which we do not need to know the result.
|
|
201
|
+
#
|
|
202
|
+
# @param args [Array<String>]
|
|
203
|
+
# @return [ChildProcess] detached process spawned in the background
|
|
204
|
+
def execute_in_background(args)
|
|
205
|
+
if args.include?('|')
|
|
206
|
+
raise Overcommit::Exceptions::InvalidCommandArgs,
|
|
207
|
+
'Cannot pipe commands with the `execute_in_background` helper'
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
debug("Spawning background task: #{args.join(' ')}")
|
|
211
|
+
Subprocess.spawn_detached(args)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Return the number of processors used by the OS for process scheduling.
|
|
215
|
+
#
|
|
216
|
+
# @see https://github.com/grosser/parallel/blob/v1.6.1/lib/parallel/processor_count.rb#L17-L51
|
|
217
|
+
def processor_count # rubocop:disable all
|
|
218
|
+
@processor_count ||=
|
|
219
|
+
begin
|
|
220
|
+
if Overcommit::OS.windows?
|
|
221
|
+
require 'win32ole'
|
|
222
|
+
result = WIN32OLE.connect('winmgmts://').ExecQuery(
|
|
223
|
+
'select NumberOfLogicalProcessors from Win32_Processor'
|
|
224
|
+
)
|
|
225
|
+
result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
|
|
226
|
+
elsif File.readable?('/proc/cpuinfo')
|
|
227
|
+
IO.read('/proc/cpuinfo').scan(/^processor/).size
|
|
228
|
+
elsif File.executable?('/usr/bin/hwprefs')
|
|
229
|
+
IO.popen('/usr/bin/hwprefs thread_count').read.to_i
|
|
230
|
+
elsif File.executable?('/usr/sbin/psrinfo')
|
|
231
|
+
IO.popen('/usr/sbin/psrinfo').read.scan(/^.*on-*line/).size
|
|
232
|
+
elsif File.executable?('/usr/sbin/ioscan')
|
|
233
|
+
IO.popen('/usr/sbin/ioscan -kC processor') do |out|
|
|
234
|
+
out.read.scan(/^.*processor/).size
|
|
235
|
+
end
|
|
236
|
+
elsif File.executable?('/usr/sbin/pmcycles')
|
|
237
|
+
IO.popen('/usr/sbin/pmcycles -m').read.count("\n")
|
|
238
|
+
elsif File.executable?('/usr/sbin/lsdev')
|
|
239
|
+
IO.popen('/usr/sbin/lsdev -Cc processor -S 1').read.count("\n")
|
|
240
|
+
elsif File.executable?('/usr/sbin/sysctl')
|
|
241
|
+
IO.popen('/usr/sbin/sysctl -n hw.ncpu').read.to_i
|
|
242
|
+
elsif File.executable?('/sbin/sysctl')
|
|
243
|
+
IO.popen('/sbin/sysctl -n hw.ncpu').read.to_i
|
|
244
|
+
else
|
|
245
|
+
# Unknown platform; assume 1 processor
|
|
246
|
+
1
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Calls a block of code with a modified set of environment variables,
|
|
252
|
+
# restoring them once the code has executed.
|
|
253
|
+
def with_environment(env)
|
|
254
|
+
old_env = {}
|
|
255
|
+
env.each do |var, value|
|
|
256
|
+
old_env[var] = ENV[var.to_s]
|
|
257
|
+
ENV[var.to_s] = value
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
yield
|
|
261
|
+
ensure
|
|
262
|
+
old_env.each { |var, value| ENV[var.to_s] = value }
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Returns whether a file is a broken symlink.
|
|
266
|
+
#
|
|
267
|
+
# @return [true,false]
|
|
268
|
+
def broken_symlink?(file)
|
|
269
|
+
# JRuby's implementation of File.exist? returns true for broken
|
|
270
|
+
# symlinks, so we need use File.size?
|
|
271
|
+
Overcommit::Utils::FileUtils.symlink?(file) && File.size?(file).nil?
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Convert a glob pattern to an absolute path glob pattern rooted from the
|
|
275
|
+
# repository root directory.
|
|
276
|
+
#
|
|
277
|
+
# @param glob [String]
|
|
278
|
+
# @return [String]
|
|
279
|
+
def convert_glob_to_absolute(glob)
|
|
280
|
+
File.join(repo_root, glob)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Return whether a pattern matches the given path.
|
|
284
|
+
#
|
|
285
|
+
# @param pattern [String]
|
|
286
|
+
# @param path [String]
|
|
287
|
+
def matches_path?(pattern, path)
|
|
288
|
+
File.fnmatch?(
|
|
289
|
+
pattern, path,
|
|
290
|
+
File::FNM_PATHNAME | # Wildcard doesn't match separator
|
|
291
|
+
File::FNM_DOTMATCH # Wildcards match dotfiles
|
|
292
|
+
)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
private
|
|
296
|
+
|
|
297
|
+
# Log debug output.
|
|
298
|
+
#
|
|
299
|
+
# This is necessary since some specs indirectly call utility functions but
|
|
300
|
+
# don't explicitly set the logger for the Utils class, so we do a quick
|
|
301
|
+
# check here to see if it's set before we attempt to log.
|
|
302
|
+
#
|
|
303
|
+
# @param args [Array<String>]
|
|
304
|
+
def debug(*args)
|
|
305
|
+
log&.debug(*args)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|