overcommit 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/bin/overcommit +3 -4
  3. data/config/default.yml +139 -0
  4. data/lib/overcommit.rb +7 -5
  5. data/lib/overcommit/cli.rb +59 -64
  6. data/lib/overcommit/configuration.rb +108 -34
  7. data/lib/overcommit/configuration_loader.rb +47 -0
  8. data/lib/overcommit/constants.rb +7 -0
  9. data/lib/overcommit/exceptions.rb +16 -0
  10. data/lib/overcommit/hook/base.rb +91 -0
  11. data/lib/overcommit/hook/commit_msg/base.rb +11 -0
  12. data/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +18 -0
  13. data/lib/overcommit/{plugins → hook}/commit_msg/hard_tabs.rb +5 -6
  14. data/lib/overcommit/hook/commit_msg/russian_novel.rb +14 -0
  15. data/lib/overcommit/hook/commit_msg/single_line_subject.rb +12 -0
  16. data/lib/overcommit/hook/commit_msg/text_width.rb +20 -0
  17. data/lib/overcommit/hook/commit_msg/trailing_period.rb +12 -0
  18. data/lib/overcommit/hook/post_checkout/base.rb +11 -0
  19. data/lib/overcommit/hook/post_checkout/bundle_check.rb +29 -0
  20. data/lib/overcommit/hook/post_checkout/index_tags.rb +24 -0
  21. data/lib/overcommit/hook/pre_commit/author_email.rb +17 -0
  22. data/lib/overcommit/hook/pre_commit/author_name.rb +17 -0
  23. data/lib/overcommit/hook/pre_commit/base.rb +10 -0
  24. data/lib/overcommit/hook/pre_commit/bundle_check.rb +30 -0
  25. data/lib/overcommit/hook/pre_commit/coffee_lint.rb +14 -0
  26. data/lib/overcommit/hook/pre_commit/css_lint.rb +16 -0
  27. data/lib/overcommit/hook/pre_commit/haml_lint.rb +26 -0
  28. data/lib/overcommit/hook/pre_commit/hard_tabs.rb +16 -0
  29. data/lib/overcommit/hook/pre_commit/image_optim.rb +41 -0
  30. data/lib/overcommit/hook/pre_commit/js_hint.rb +15 -0
  31. data/lib/overcommit/hook/pre_commit/jscs.rb +31 -0
  32. data/lib/overcommit/hook/pre_commit/python_flake8.rb +14 -0
  33. data/lib/overcommit/hook/pre_commit/rubocop.rb +26 -0
  34. data/lib/overcommit/hook/pre_commit/scss_lint.rb +26 -0
  35. data/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +15 -0
  36. data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +22 -0
  37. data/lib/overcommit/hook_context.rb +16 -0
  38. data/lib/overcommit/hook_context/base.rb +68 -0
  39. data/lib/overcommit/hook_context/commit_msg.rb +32 -0
  40. data/lib/overcommit/hook_context/post_checkout.rb +24 -0
  41. data/lib/overcommit/hook_context/pre_commit.rb +96 -0
  42. data/lib/overcommit/hook_runner.rb +150 -0
  43. data/lib/overcommit/installer.rb +61 -68
  44. data/lib/overcommit/logger.rb +16 -13
  45. data/lib/overcommit/utils.rb +63 -38
  46. data/lib/overcommit/version.rb +1 -1
  47. data/{bin/scripts → libexec}/gerrit-change-id +0 -0
  48. data/{bin/scripts → libexec}/index-tags +1 -3
  49. data/template-dir/hooks/commit-msg +83 -0
  50. data/template-dir/hooks/overcommit-hook +83 -0
  51. data/template-dir/hooks/post-checkout +83 -0
  52. data/template-dir/hooks/pre-commit +83 -0
  53. metadata +76 -57
  54. data/bin/hooks/commit-msg +0 -8
  55. data/bin/hooks/post-checkout +0 -9
  56. data/bin/hooks/post-merge +0 -23
  57. data/bin/hooks/pre-commit +0 -8
  58. data/bin/hooks/prepare-commit-msg +0 -159
  59. data/bin/run-hook +0 -8
  60. data/bin/scripts/check-gemfile +0 -9
  61. data/bin/scripts/csslint-rhino.js +0 -9080
  62. data/bin/scripts/jshint.js +0 -5921
  63. data/bin/scripts/jshint_runner.js +0 -42
  64. data/lib/overcommit/errors.rb +0 -3
  65. data/lib/overcommit/git_hook.rb +0 -89
  66. data/lib/overcommit/hook_specific_check.rb +0 -110
  67. data/lib/overcommit/hooks/commit_msg.rb +0 -7
  68. data/lib/overcommit/hooks/pre_commit.rb +0 -9
  69. data/lib/overcommit/plugins/commit_msg/change_id.rb +0 -15
  70. data/lib/overcommit/plugins/commit_msg/release_note.rb +0 -25
  71. data/lib/overcommit/plugins/commit_msg/russian_novel.rb +0 -16
  72. data/lib/overcommit/plugins/commit_msg/single_line_subject.rb +0 -13
  73. data/lib/overcommit/plugins/commit_msg/text_width.rb +0 -20
  74. data/lib/overcommit/plugins/commit_msg/trailing_period.rb +0 -13
  75. data/lib/overcommit/plugins/pre_commit/author_name.rb +0 -16
  76. data/lib/overcommit/plugins/pre_commit/causes_email.rb +0 -15
  77. data/lib/overcommit/plugins/pre_commit/coffee_lint.rb +0 -16
  78. data/lib/overcommit/plugins/pre_commit/css_linter.rb +0 -17
  79. data/lib/overcommit/plugins/pre_commit/haml_style.rb +0 -34
  80. data/lib/overcommit/plugins/pre_commit/haml_syntax.rb +0 -24
  81. data/lib/overcommit/plugins/pre_commit/image_optimization.rb +0 -50
  82. data/lib/overcommit/plugins/pre_commit/js_console_log.rb +0 -16
  83. data/lib/overcommit/plugins/pre_commit/js_syntax.rb +0 -30
  84. data/lib/overcommit/plugins/pre_commit/python_flake8.rb +0 -15
  85. data/lib/overcommit/plugins/pre_commit/ruby_style.rb +0 -67
  86. data/lib/overcommit/plugins/pre_commit/ruby_syntax.rb +0 -19
  87. data/lib/overcommit/plugins/pre_commit/scss_lint.rb +0 -66
  88. data/lib/overcommit/plugins/pre_commit/test_history.rb +0 -58
  89. data/lib/overcommit/plugins/pre_commit/whitespace.rb +0 -21
  90. data/lib/overcommit/plugins/pre_commit/yaml_syntax.rb +0 -22
  91. data/lib/overcommit/reporter.rb +0 -90
  92. data/lib/overcommit/staged_file.rb +0 -86
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd49b47c55670147b58f3b1f9d24c563af35b589
4
- data.tar.gz: 986b58c2716ebd051826a84f420e1c568ba98e7e
3
+ metadata.gz: a23b361b3bac1357e190f81ea1912710b9bd62bf
4
+ data.tar.gz: d0d707d8f82ed50aff865fd170b769baf8d78869
5
5
  SHA512:
6
- metadata.gz: 555c4a7777514b88d459eff53b03e5be698499273c95d24b0fa39b779f5ab0d2d59334ec497eaca1061852e06ca14b4d69af0fdd8119dbdc40509c069d29a3b5
7
- data.tar.gz: d4e0f5ce2ca1c0a5af8f430c65e7f485c3936837e09659f460e489d8f11cfcf30731ac4fd331984816ecb501cffeaff0e87624911c8cabb93e16be8d21580f5b
6
+ metadata.gz: a7a1110c6239f00662de316e677dab141db03ae87f9c13eca2c2a0dc2b78ed5b3bb41cbbf0d4c11a79436423b0a3cda2215de6a4d926c7b3a48e0b4ab586e1ca
7
+ data.tar.gz: 408ac82b4911e94f48670c674870eb79acfd93109f2edd1ca19993c49f6a40f193161a8cd1f26ec7b4121ee66e0391f3ce12715a332152bca128636ec218476d
data/bin/overcommit CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- $: << File.expand_path('../../lib', __FILE__)
4
-
5
3
  require 'overcommit'
6
4
  require 'overcommit/cli'
7
5
 
8
- Overcommit::CLI.new(ARGV).tap do |cli|
9
- cli.parse_arguments
6
+ logger = Overcommit::Logger.new(STDOUT)
7
+
8
+ Overcommit::CLI.new(ARGV, logger).tap do |cli|
10
9
  cli.run
11
10
  end
@@ -0,0 +1,139 @@
1
+ # Default configuration that all Overcommit configurations inherit from.
2
+ #
3
+ # This is an opinionated list of which hooks are valuable to run and what their
4
+ # out-of-the-box settings should be.
5
+
6
+ # Where to store hook plugins specific to a repository. These are loaded in
7
+ # addition to the default hooks Overcommit comes with. The location is relative
8
+ # to the root of the repository.
9
+ plugin_directory: '.git-hooks'
10
+
11
+ # Hooks that run after HEAD changes or a file is explicitly checked out. Useful
12
+ # for updating source tags (e.g. via ctags) or warning about new migrations,
13
+ # etc.
14
+ PostCheckout:
15
+ ALL:
16
+ required: false
17
+ quiet: false
18
+ BundleCheck:
19
+ description: 'Checking Gemfile dependencies'
20
+ include:
21
+ - 'Gemfile'
22
+ - 'Gemfile.lock'
23
+ - '*.gemspec'
24
+ IndexTags:
25
+ description: 'Indexing source code tags'
26
+
27
+ # Hooks that are run after `git commit` is executed, before the commit message
28
+ # editor is displayed. These hooks are ideal for syntax checkers, linters, and
29
+ # other checks that you want to run before you allow a commit object to be
30
+ # created.
31
+ PreCommit:
32
+ ALL:
33
+ requires_files: true
34
+ required: false
35
+ quiet: false
36
+
37
+ AuthorEmail:
38
+ description: 'Checking author email'
39
+ requires_files: false
40
+ required: true
41
+ quiet: true
42
+ pattern: '^[^@]+@.*$'
43
+
44
+ AuthorName:
45
+ description: 'Checking for author name'
46
+ requires_files: false
47
+ required: true
48
+ quiet: true
49
+
50
+ BundleCheck:
51
+ description: 'Checking Gemfile dependencies'
52
+ include:
53
+ - 'Gemfile'
54
+ - 'Gemfile.lock'
55
+ - '*.gemspec'
56
+
57
+ CoffeeLint:
58
+ description: 'Analyzing with coffeelint'
59
+ include: '**/*.coffee'
60
+
61
+ CssLint:
62
+ description: 'Analyzing with csslint'
63
+ include: '**/*.css'
64
+
65
+ HamlLint:
66
+ description: 'Analyzing with haml-lint'
67
+ include: '**/*.haml'
68
+
69
+ HardTabs:
70
+ description: 'Checking for hard tabs'
71
+
72
+ ImageOptim:
73
+ description: 'Checking for optimizable images'
74
+ include:
75
+ - '**/*.gif'
76
+ - '**/*.jpg'
77
+ - '**/*.jpeg'
78
+ - '**/*.png'
79
+
80
+ Jscs:
81
+ description: 'Analyzing with JSCS'
82
+ include: '**/*.js'
83
+
84
+ JsHint:
85
+ description: 'Analyzing with JSHint'
86
+ include: '**/*.js'
87
+
88
+ PythonFlake8:
89
+ description: 'Analyzing with flake8'
90
+ include: '**/*.py'
91
+
92
+ Rubocop:
93
+ description: 'Analyzing with Rubocop'
94
+ include:
95
+ - '**/*.gemspec'
96
+ - '**/*.rake'
97
+ - '**/*.rb'
98
+ - '**/Gemfile'
99
+ - '**/Rakefile'
100
+
101
+ ScssLint:
102
+ description: 'Analyzing with scss-lint'
103
+ include: '**/*.scss'
104
+
105
+ TrailingWhitespace:
106
+ description: 'Checking for trailing whitespace'
107
+
108
+ YamlSyntax:
109
+ description: 'Checking YAML syntax'
110
+ include: '**/*.yml'
111
+
112
+ # Hooks that are run against every commit message after a user has written it.
113
+ # These hooks are useful for enforcing policies on commit messages written for a
114
+ # project.
115
+ CommitMsg:
116
+ ALL:
117
+ requires_files: false
118
+ quiet: false
119
+
120
+ GerritChangeId:
121
+ enabled: false
122
+ description: 'Ensuring Gerrit Change-Id is present'
123
+ required: true
124
+
125
+ HardTabs:
126
+ description: 'Checking for hard tabs'
127
+
128
+ RussianNovel:
129
+ description: 'Checking length of commit message'
130
+ quiet: true
131
+
132
+ SingleLineSubject:
133
+ description: 'Checking subject line'
134
+
135
+ TextWidth:
136
+ description: 'Checking text width'
137
+
138
+ TrailingPeriod:
139
+ description: 'Checking for trailing periods in subject'
data/lib/overcommit.rb CHANGED
@@ -1,10 +1,12 @@
1
+ require 'overcommit/constants'
2
+ require 'overcommit/exceptions'
1
3
  require 'overcommit/configuration'
2
- require 'overcommit/errors'
3
- require 'overcommit/git_hook'
4
- require 'overcommit/hook_specific_check'
4
+ require 'overcommit/configuration_loader'
5
+ require 'overcommit/hook/base'
6
+ require 'overcommit/hook_context/base'
7
+ require 'overcommit/hook_context'
8
+ require 'overcommit/hook_runner'
5
9
  require 'overcommit/installer'
6
10
  require 'overcommit/logger'
7
- require 'overcommit/reporter'
8
- require 'overcommit/staged_file'
9
11
  require 'overcommit/utils'
10
12
  require 'overcommit/version'
@@ -1,75 +1,61 @@
1
1
  require 'optparse'
2
2
 
3
3
  module Overcommit
4
+ # Responsible for parsing command-line options and executing appropriate
5
+ # application logic based on those options.
4
6
  class CLI
5
- attr_reader :options
6
-
7
- def initialize(arguments = [])
7
+ def initialize(arguments, logger)
8
8
  @arguments = arguments
9
+ @log = logger
9
10
  @options = {}
10
11
  end
11
12
 
13
+ def run
14
+ parse_arguments
15
+
16
+ case @options[:action]
17
+ when :install, :uninstall
18
+ install_or_uninstall
19
+ when :template_dir
20
+ print_template_directory_path
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :log
27
+
12
28
  def parse_arguments
13
29
  @parser = OptionParser.new do |opts|
14
- opts.banner = "Usage: #{opts.program_name} [options] target"
30
+ opts.banner = "Usage: #{opts.program_name} [options] [target-repo]"
15
31
 
16
32
  opts.on_tail('-h', '--help', 'Show this message') do
17
33
  print_help opts.help
18
34
  end
19
35
 
20
36
  opts.on_tail('-v', '--version', 'Show version') do
21
- log.log VERSION
22
- exit 0
37
+ print_version(opts.program_name)
23
38
  end
24
39
 
25
- opts.on('-l', '--list-templates', 'List built-in templates') do
26
- Overcommit.config.templates.each_pair do |name, configuration|
27
- log.bold name
28
- log.log YAML.dump(configuration), ''
29
- end
30
- exit 0
40
+ opts.on('-u', '--uninstall', 'Remove Overcommit hooks from a repository') do
41
+ @options[:action] = :uninstall
31
42
  end
32
43
 
33
- opts.on('-a', '--all', 'Include all git hooks') do
34
- @options[:template] = 'all'
44
+ opts.on('-i', '--install', 'Install Overcommit hooks in a repository') do
45
+ @options[:action] = :install
35
46
  end
36
47
 
37
- opts.on('-t', '--template template',
38
- 'Specify a template of hooks') do |template|
39
- @options[:template] = template
40
- end
41
-
42
- opts.on('--uninstall', 'Remove overcommit from target') do
43
- @options[:uninstall] = true
44
- end
45
-
46
- opts.on('-e', '--exclude hook_name,...', Array,
47
- 'Exclude hooks from installation') do |excludes|
48
- # Transform from:
49
- #
50
- # pre_commit/test_history,commit_msg/change_id
51
- #
52
- # Into:
53
- #
54
- # {
55
- # 'commit_msg' => ['change_id'],
56
- # 'pre_commit' => ['test_history']
57
- # }
58
- @options[:excludes] = excludes.inject({}) do |memo, exclude|
59
- parts = exclude.split(%r{[:/.]})
60
- next memo unless parts.size == 2
61
-
62
- memo[parts.first] ||= []
63
- memo[parts.first] << parts.last
64
-
65
- memo
66
- end
48
+ opts.on('-t', '--template-dir', 'Print location of template directory') do
49
+ @options[:action] = :template_dir
67
50
  end
68
51
  end
69
52
 
70
53
  begin
71
54
  @parser.parse!(@arguments)
72
55
 
56
+ # Default action is to install
57
+ @options[:action] ||= :install
58
+
73
59
  # Unconsumed arguments are our targets
74
60
  @options[:targets] = @arguments
75
61
  rescue OptionParser::InvalidOption => ex
@@ -77,38 +63,47 @@ module Overcommit
77
63
  end
78
64
  end
79
65
 
80
- def run
81
- if @options[:targets].nil? || @options[:targets].empty?
82
- log.warning 'You must supply at least one directory'
83
- log.log @parser.help
84
- exit 2
66
+ def install_or_uninstall
67
+ if Array(@options[:targets]).empty?
68
+ @options[:targets] = [Overcommit::Utils.repo_root].compact
69
+ end
70
+
71
+ if @options[:targets].empty?
72
+ log.warning 'You are not in a git repository.'
73
+ log.log 'You must either specify the path to a repository or ' <<
74
+ 'change your current directory to a repository.'
75
+ halt 64 # EX_USAGE
85
76
  end
86
77
 
87
78
  @options[:targets].each do |target|
88
79
  begin
89
- Installer.new(@options, target).run
90
- rescue NotAGitRepoError => e
91
- log.warning "Skipping #{target}: #{e}"
80
+ Installer.new(log).run(target, @options)
81
+ rescue Overcommit::Exceptions::InvalidGitRepo => error
82
+ log.warning "Invalid repo #{target}: #{error}"
83
+ halt 69 # EX_UNAVAILABLE
92
84
  end
93
85
  end
86
+ end
94
87
 
95
- log.success "#{@options[:uninstall] ? 'Removal' : 'Installation'} complete"
96
-
97
- rescue ArgumentError => ex
98
- error "Installation failed: #{ex}"
99
- exit 3
88
+ def print_template_directory_path
89
+ puts File.join(OVERCOMMIT_HOME, 'template-dir')
90
+ halt
100
91
  end
101
92
 
102
- private
93
+ def print_help(message, error = nil)
94
+ log.error "#{error}\n" if error
95
+ log.log message
96
+ halt(error ? 64 : 0) # 64 = EX_USAGE
97
+ end
103
98
 
104
- def log
105
- Logger.instance
99
+ def print_version(program_name)
100
+ log.log "#{program_name} #{Overcommit::VERSION}"
101
+ halt
106
102
  end
107
103
 
108
- def print_help(message, ex = nil)
109
- log.error ex.to_s + "\n" if ex
110
- log.log message
111
- exit 0
104
+ # Used for ease of stubbing in tests
105
+ def halt(status = 0)
106
+ exit status
112
107
  end
113
108
  end
114
109
  end
@@ -1,54 +1,128 @@
1
- require 'singleton'
2
- require 'yaml'
3
-
4
1
  module Overcommit
2
+ # Stores configuration for Overcommit and the hooks it runs.
5
3
  class Configuration
6
- include Singleton
4
+ # Creates a configuration from the given hash.
5
+ def initialize(hash)
6
+ @hash = hash
7
+ validate
8
+ end
9
+
10
+ def ==(other)
11
+ super || @hash == other.hash
12
+ end
13
+ alias_method :eql?, :==
7
14
 
8
- attr_reader :templates
15
+ # Returns absolute path to the directory that external hook plugins should
16
+ # be loaded from.
17
+ def plugin_directory
18
+ File.join(Overcommit::Utils.repo_root, @hash['plugin_directory'] || '.githooks')
19
+ end
9
20
 
10
- def initialize
11
- @templates = YAML.load_file(Utils.absolute_path('config/templates.yml'))
21
+ # Returns the built-in hooks that have been enabled for a hook type.
22
+ def enabled_builtin_hooks(hook_type)
23
+ @hash[hook_type].keys.
24
+ select { |hook_name| hook_name != 'ALL' }.
25
+ select { |hook_name| hook_enabled?(hook_type, hook_name) }
12
26
  end
13
27
 
14
- # Read the repo-specific 'overcommit.yml' file to determine what behavior
15
- # the user wants.
16
- def repo_settings
17
- config_file = Utils.repo_path('.git/hooks/overcommit.yml')
28
+ # Returns a non-modifiable configuration for a hook.
29
+ def for_hook(hook, hook_type = nil)
30
+ unless hook_type
31
+ components = hook.class.name.split('::')
32
+ hook = components.last
33
+ hook_type = components[-2]
34
+ end
18
35
 
19
- File.exist?(config_file) ? YAML.load_file(config_file) : {}
36
+ # Merge hook configuration with special 'ALL' config
37
+ smart_merge(@hash[hook_type]['ALL'], @hash[hook_type][hook] || {}).freeze
20
38
  end
21
39
 
22
- # Given the current configuration, return a set of paths which should be
23
- # loaded as plugins (`require`d)
24
- def desired_plugins
25
- excludes = repo_settings['excludes'] || {}
40
+ # Merges the given configuration with this one, returning a new
41
+ # {Configuration}. The provided configuration will either add to or replace
42
+ # any options defined in this configuration.
43
+ def merge(config)
44
+ self.class.new(smart_merge(@hash, config.hash))
45
+ end
26
46
 
27
- plugin_directories.map do |dir|
28
- Dir[File.join(dir, Utils.hook_name, '*.rb')].map do |plugin|
29
- basename = File.basename(plugin, '.rb')
30
- if !(excludes[Utils.hook_name] || []).include?(basename)
31
- plugin
32
- end
33
- end.compact
34
- end.flatten
47
+ # Applies additional configuration settings based on the provided
48
+ # environment variables.
49
+ def apply_environment!(hook_type, env)
50
+ skipped_hooks = "#{env['SKIP']} #{env['SKIP_CHECKS']} #{env['SKIP_HOOKS']}".split(/[:, ]/)
51
+
52
+ if skipped_hooks.include?('all') || skipped_hooks.include?('ALL')
53
+ @hash[hook_type]['ALL']['skip'] = true
54
+ else
55
+ skipped_hooks.select { |hook_name| hook_exists?(hook_type, hook_name) }.
56
+ map { |hook_name| Overcommit::Utils.camel_case(hook_name) }.
57
+ each do |hook_name|
58
+ @hash[hook_type][hook_name] ||= {}
59
+ @hash[hook_type][hook_name]['skip'] = true
60
+ end
61
+ end
35
62
  end
36
63
 
64
+ protected
65
+
66
+ attr_reader :hash
67
+
37
68
  private
38
69
 
39
- def plugin_directories
40
- # Start with the base plugins provided by the gem
41
- plugin_dirs = [File.expand_path('../plugins', __FILE__)]
42
- repo_specific = Utils.repo_path('.githooks')
70
+ def hook_exists?(hook_type, hook_name)
71
+ hook_name = Overcommit::Utils.snake_case(hook_name)
72
+ underscored_hook_type = Overcommit::Utils.snake_case(hook_type)
73
+
74
+ File.exist?(File.join(OVERCOMMIT_HOME, 'lib', 'overcommit', 'hook',
75
+ underscored_hook_type, "#{hook_name}.rb"))
76
+ end
77
+
78
+ def hook_enabled?(hook_type, hook_name)
79
+ individual_enabled = @hash[hook_type][hook_name]['enabled']
80
+ return individual_enabled unless individual_enabled.nil?
43
81
 
44
- # Add on any repo-specific checks
45
- plugin_dirs << repo_specific if File.directory?(repo_specific)
82
+ all_enabled = @hash[hook_type]['ALL']['enabled']
83
+ return all_enabled unless all_enabled.nil?
46
84
 
47
- plugin_dirs
85
+ true
48
86
  end
49
- end
50
87
 
51
- def self.config
52
- Configuration.instance
88
+ # Validates the configuration for any invalid options, normalizing it where
89
+ # possible.
90
+ def validate
91
+ @hash = convert_nils_to_empty_hashes(@hash)
92
+ ensure_hook_type_sections_exist(@hash)
93
+ end
94
+
95
+ def smart_merge(parent, child)
96
+ parent.merge(child) do |key, old, new|
97
+ case old
98
+ when Array
99
+ old + Array(new)
100
+ when Hash
101
+ smart_merge(old, new)
102
+ else
103
+ new
104
+ end
105
+ end
106
+ end
107
+
108
+ def ensure_hook_type_sections_exist(hash)
109
+ Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
110
+ hash[hook_type] ||= {}
111
+ hash[hook_type]['ALL'] ||= {}
112
+ end
113
+ end
114
+
115
+ def convert_nils_to_empty_hashes(hash)
116
+ hash.inject({}) do |h, (key, value)|
117
+ h[key] =
118
+ case value
119
+ when nil then {}
120
+ when Hash then convert_nils_to_empty_hashes(value)
121
+ else
122
+ value
123
+ end
124
+ h
125
+ end
126
+ end
53
127
  end
54
128
  end