class Overcommit::Configuration

Stores configuration for Overcommit and the hooks it runs.

Attributes

hash[R]

Public Class Methods

new(hash, options = {}) click to toggle source

Creates a configuration from the given hash.

@param hash [Hash] loaded YAML config file as a hash @param options [Hash] @option default [Boolean] whether this is the default built-in configuration @option logger [Overcommit::Logger]

# File lib/overcommit/configuration.rb, line 15
def initialize(hash, options = {})
  @options = options.dup
  @options[:logger] ||= Overcommit::Logger.silent
  @hash = hash # Assign so validator can read original values
  unless options[:validate] == false
    @hash = Overcommit::ConfigurationValidator.new.validate(self, hash, options)
  end
end

Public Instance Methods

==(other) click to toggle source
Calls superclass method
# File lib/overcommit/configuration.rb, line 24
def ==(other)
  super || @hash == other.hash
end
[](key) click to toggle source

Access the configuration as if it were a hash.

@param key [String] @return [Array,Hash,Number,String]

# File lib/overcommit/configuration.rb, line 32
def [](key)
  @hash[key]
end
all_builtin_hook_configs() click to toggle source

Returns configuration for all built-in hooks in each hook type.

@return [Hash]

# File lib/overcommit/configuration.rb, line 72
def all_builtin_hook_configs
  hook_configs = {}

  Overcommit::Utils.supported_hook_type_classes.each do |hook_type|
    hook_names = @hash[hook_type].keys.reject { |name| name == 'ALL' }

    hook_configs[hook_type] = Hash[
      hook_names.map do |hook_name|
        [hook_name, for_hook(hook_name, hook_type)]
      end
    ]
  end

  hook_configs
end
all_hook_configs() click to toggle source

Returns configuration for all hooks in each hook type.

@return [Hash]

# File lib/overcommit/configuration.rb, line 65
def all_hook_configs
  smart_merge(all_builtin_hook_configs, all_plugin_hook_configs)
end
all_plugin_hook_configs() click to toggle source

Returns configuration for all plugin hooks in each hook type.

@return [Hash]

# File lib/overcommit/configuration.rb, line 91
def all_plugin_hook_configs
  hook_configs = {}

  Overcommit::Utils.supported_hook_types.each do |hook_type|
    hook_type_class_name = Overcommit::Utils.camel_case(hook_type)

    directory = File.join(plugin_directory, hook_type.tr('-', '_'))
    plugin_paths = Dir[File.join(directory, '*.rb')].sort

    hook_names = plugin_paths.map do |path|
      Overcommit::Utils.camel_case(File.basename(path, '.rb'))
    end

    hook_configs[hook_type_class_name] = Hash[
      hook_names.map do |hook_name|
        [hook_name, for_hook(hook_name, Overcommit::Utils.camel_case(hook_type))]
      end
    ]
  end

  hook_configs
end
apply_environment!(hook_context, env) click to toggle source

Applies additional configuration settings based on the provided environment variables.

# File lib/overcommit/configuration.rb, line 157
def apply_environment!(hook_context, env)
  skipped_hooks = "#{env['SKIP']} #{env['SKIP_CHECKS']} #{env['SKIP_HOOKS']}".split(/[:, ]/)
  only_hooks = env.fetch('ONLY') { '' }.split(/[:, ]/)
  hook_type = hook_context.hook_class_name

  if only_hooks.any? || skipped_hooks.include?('all') || skipped_hooks.include?('ALL')
    @hash[hook_type]['ALL']['skip'] = true
  end

  only_hooks.select { |hook_name| hook_exists?(hook_context, hook_name) }.
             map { |hook_name| Overcommit::Utils.camel_case(hook_name) }.
             each do |hook_name|
    @hash[hook_type][hook_name] ||= {}
    @hash[hook_type][hook_name]['skip'] = false
  end

  skipped_hooks.select { |hook_name| hook_exists?(hook_context, hook_name) }.
                map { |hook_name| Overcommit::Utils.camel_case(hook_name) }.
                each do |hook_name|
    @hash[hook_type][hook_name] ||= {}
    @hash[hook_type][hook_name]['skip'] = true
  end
end
concurrency() click to toggle source
# File lib/overcommit/configuration.rb, line 42
def concurrency
  @concurrency ||=
    begin
      cores = Overcommit::Utils.processor_count
      content = @hash.fetch('concurrency') { '%<processors>d' }
      if content.is_a?(String)
        concurrency_expr = content % { processors: cores }

        a, op, b = concurrency_expr.scan(%r{(\d+)\s*([+\-*\/])\s*(\d+)})[0]
        if a
          a.to_i.send(op, b.to_i)
        else
          concurrency_expr.to_i
        end
      else
        content.to_i
      end
    end
end
enabled_ad_hoc_hooks(hook_context) click to toggle source

Returns the ad hoc hooks that have been enabled for a hook type.

# File lib/overcommit/configuration.rb, line 123
def enabled_ad_hoc_hooks(hook_context)
  @hash[hook_context.hook_class_name].keys.
    reject { |hook_name| hook_name == 'ALL' }.
    select { |hook_name| ad_hoc_hook?(hook_context, hook_name) }.
    select { |hook_name| hook_enabled?(hook_context, hook_name) }
end
enabled_builtin_hooks(hook_context) click to toggle source

Returns the built-in hooks that have been enabled for a hook type.

# File lib/overcommit/configuration.rb, line 115
def enabled_builtin_hooks(hook_context)
  @hash[hook_context.hook_class_name].keys.
    reject { |hook_name| hook_name == 'ALL' }.
    select { |hook_name| built_in_hook?(hook_context, hook_name) }.
    select { |hook_name| hook_enabled?(hook_context, hook_name) }
end
for_hook(hook, hook_type = nil) click to toggle source

Returns a non-modifiable configuration for a hook.

# File lib/overcommit/configuration.rb, line 131
def for_hook(hook, hook_type = nil)
  unless hook_type
    components = hook.class.name.split('::')
    hook = components.last
    hook_type = components[-2]
  end

  # Merge hook configuration with special 'ALL' config
  hook_config = smart_merge(@hash[hook_type]['ALL'], @hash[hook_type][hook] || {})

  # Need to specially handle `enabled` option since not setting it does not
  # necessarily mean the hook is disabled
  hook_config['enabled'] = hook_enabled?(hook_type, hook)

  hook_config.freeze
end
merge(config) click to toggle source

Merges the given configuration with this one, returning a new {Configuration}. The provided configuration will either add to or replace any options defined in this configuration.

# File lib/overcommit/configuration.rb, line 151
def merge(config)
  self.class.new(smart_merge(@hash, config.hash))
end
plugin_directory() click to toggle source

Returns absolute path to the directory that external hook plugins should be loaded from.

# File lib/overcommit/configuration.rb, line 38
def plugin_directory
  File.join(Overcommit::Utils.repo_root, @hash['plugin_directory'] || '.git-hooks')
end
plugin_hook?(hook_context_or_type, hook_name) click to toggle source
# File lib/overcommit/configuration.rb, line 181
def plugin_hook?(hook_context_or_type, hook_name)
  hook_type_name =
    if hook_context_or_type.is_a?(String)
      Overcommit::Utils.snake_case(hook_context_or_type)
    else
      hook_context_or_type.hook_type_name
    end
  hook_name = Overcommit::Utils.snake_case(hook_name)

  File.exist?(File.join(plugin_directory, hook_type_name, "#{hook_name}.rb"))
end
previous_signature?() click to toggle source

Return whether a previous signature has been recorded for this configuration.

@return [true,false]

# File lib/overcommit/configuration.rb, line 205
def previous_signature?
  !stored_signature.empty?
end
signature_changed?() click to toggle source

Return whether the signature for this configuration has changed since it was last calculated.

@return [true,false]

# File lib/overcommit/configuration.rb, line 197
def signature_changed?
  signature != stored_signature
end
update_signature!() click to toggle source

Update the currently stored signature for this hook.

# File lib/overcommit/configuration.rb, line 234
def update_signature!
  result = Overcommit::Utils.execute(
    %w[git config --local] + [signature_config_key, signature]
  )

  verify_signature_value = @hash['verify_signatures'] ? 1 : 0
  result &&= Overcommit::Utils.execute(
    %W[git config --local #{verify_signature_config_key} #{verify_signature_value}]
  )

  unless result.success?
    raise Overcommit::Exceptions::GitConfigError,
          "Unable to write to local repo git config: #{result.stderr}"
  end
end
verify_signatures?() click to toggle source

Returns whether this configuration should verify itself by checking the stored configuration for the repo.

@return [true,false]

# File lib/overcommit/configuration.rb, line 213
def verify_signatures?
  return false if ENV['OVERCOMMIT_NO_VERIFY']
  return true if @hash['verify_signatures'] != false

  result = Overcommit::Utils.execute(
    %W[git config --local --get #{verify_signature_config_key}]
  )

  if result.status == 1 # Key doesn't exist
    return true
  elsif result.status != 0
    raise Overcommit::Exceptions::GitConfigError,
          "Unable to read from local repo git config: #{result.stderr}"
  end

  # We don't cast since we want to allow anything to count as "true" except
  # a literal zero
  result.stdout.strip != '0'
end

Private Instance Methods

ad_hoc_hook?(hook_context, hook_name) click to toggle source
# File lib/overcommit/configuration.rb, line 256
def ad_hoc_hook?(hook_context, hook_name)
  ad_hoc_conf = @hash.fetch(hook_context.hook_class_name) { {} }.fetch(hook_name) { {} }

  # Ad hoc hooks are neither built-in nor have a plugin file written but
  # still have a `command` specified to be run
  !built_in_hook?(hook_context, hook_name) &&
    !plugin_hook?(hook_context, hook_name) &&
    (ad_hoc_conf['command'] || ad_hoc_conf['required_executable'])
end
built_in_hook?(hook_context, hook_name) click to toggle source
# File lib/overcommit/configuration.rb, line 266
def built_in_hook?(hook_context, hook_name)
  hook_name = Overcommit::Utils.snake_case(hook_name)

  File.exist?(File.join(Overcommit::HOME, 'lib', 'overcommit', 'hook',
                        hook_context.hook_type_name, "#{hook_name}.rb"))
end
hook_enabled?(hook_context_or_type, hook_name) click to toggle source
# File lib/overcommit/configuration.rb, line 279
def hook_enabled?(hook_context_or_type, hook_name)
  hook_type =
    if hook_context_or_type.is_a?(String)
      hook_context_or_type
    else
      hook_context_or_type.hook_class_name
    end

  individual_enabled = @hash[hook_type].fetch(hook_name) { {} }['enabled']
  return individual_enabled unless individual_enabled.nil?

  all_enabled = @hash[hook_type]['ALL']['enabled']
  return all_enabled unless all_enabled.nil?

  false
end
hook_exists?(hook_context, hook_name) click to toggle source
# File lib/overcommit/configuration.rb, line 273
def hook_exists?(hook_context, hook_name)
  built_in_hook?(hook_context, hook_name) ||
    plugin_hook?(hook_context, hook_name) ||
    ad_hoc_hook?(hook_context, hook_name)
end
signature() click to toggle source

Returns the unique signature of this configuration.

@return [String]

# File lib/overcommit/configuration.rb, line 317
def signature
  Digest::SHA256.hexdigest(@hash.to_json)
end
signature_config_key() click to toggle source
# File lib/overcommit/configuration.rb, line 342
def signature_config_key
  'overcommit.configuration.signature'
end
smart_merge(parent, child) click to toggle source
# File lib/overcommit/configuration.rb, line 296
def smart_merge(parent, child)
  # Treat the ALL hook specially so that it overrides any configuration
  # specified by the default configuration.
  child_all = child['ALL']
  unless child_all.nil?
    parent = Hash[parent.collect { |k, v| [k, smart_merge(v, child_all)] }]
  end

  parent.merge(child) do |_key, old, new|
    case old
    when Hash
      smart_merge(old, new)
    else
      new
    end
  end
end
stored_signature() click to toggle source

Returns the stored signature of this repo’s Overcommit configuration.

This is intended to be compared against the current signature of this configuration object.

@return [String]

# File lib/overcommit/configuration.rb, line 327
def stored_signature
  result = Overcommit::Utils.execute(
    %w[git config --local --get] + [signature_config_key]
  )

  if result.status == 1 # Key doesn't exist
    return ''
  elsif result.status != 0
    raise Overcommit::Exceptions::GitConfigError,
          "Unable to read from local repo git config: #{result.stderr}"
  end

  result.stdout.chomp
end
verify_signature_config_key() click to toggle source
# File lib/overcommit/configuration.rb, line 346
def verify_signature_config_key
  'overcommit.configuration.verifysignatures'
end