class Overcommit::Hook::Base

Functionality common to all hooks.

Attributes

config[R]

Public Class Methods

new(config, context) click to toggle source

@param config [Overcommit::Configuration] @param context [Overcommit::HookContext]

# File lib/overcommit/hook/base.rb, line 27
def initialize(config, context)
  @config = config.for_hook(self)
  @context = context
end

Public Instance Methods

applicable_files() click to toggle source

Gets a list of staged files that apply to this hook based on its configured ‘include` and `exclude` lists.

# File lib/overcommit/hook/base.rb, line 165
def applicable_files
  @applicable_files ||= select_applicable(modified_files)
end
command() click to toggle source

Return command to execute for this hook.

This is intended to be configurable so hooks can prefix their commands with ‘bundle exec` or similar. It will appends the command line flags specified by the `flags` option after.

Note that any files intended to be passed must be handled by the hook itself.

@return [Array<String>]

# File lib/overcommit/hook/base.rb, line 141
def command
  Array(@config['command'] || required_executable) + flags
end
description() click to toggle source
# File lib/overcommit/hook/base.rb, line 58
def description
  @config['description'] || "Run #{name}"
end
enabled?() click to toggle source
# File lib/overcommit/hook/base.rb, line 78
def enabled?
  @config['enabled'] != false
end
excluded?() click to toggle source
# File lib/overcommit/hook/base.rb, line 82
def excluded?
  exclude_branches.any? { |p| File.fnmatch(p, current_branch) }
end
execute(cmd, options = {}) click to toggle source

Execute a command in a separate process.

If ‘splittable_args` is specified, ensures that those arguments are concatenated onto the end of the `cmd` arguments, but split up so that the operating system’s maximum command length is not exceeded. This is useful for splitting up long file lists.

@param cmd [Array<String>] command arguments @param options [Hash] @option options [Array<String>] :args arguments that can be split up over

multiple invocations (usually a list of files)

@option options [String] :input string to pass to process’ standard input

stream

@return [#status,#stdout,#stderr] struct containing result of invocation

# File lib/overcommit/hook/base.rb, line 115
def execute(cmd, options = {})
  Overcommit::Utils.execute(cmd, options)
end
execute_in_background(cmd) click to toggle source
# File lib/overcommit/hook/base.rb, line 119
def execute_in_background(cmd)
  Overcommit::Utils.execute_in_background(cmd)
end
flags() click to toggle source

Return command line flags to be passed to the command.

This excludes the list of files, as that must be handled by the hook itself.

The intention here is to provide flexibility for when a tool removes/renames its flags. Rather than wait for Overcommit to update the flags it uses, you can update your configuration to use the new flags right away without being blocked.

Also note that any flags containing dynamic content must be passed in the hook’s {#run} method.

@return [Array<String>]

# File lib/overcommit/hook/base.rb, line 159
def flags
  Array(@config['flags'])
end
in_path?(cmd) click to toggle source
# File lib/overcommit/hook/base.rb, line 97
def in_path?(cmd)
  Overcommit::Utils.in_path?(cmd)
end
included_files() click to toggle source

Gets a list of all files that apply to this hook based on its configured ‘include` and `exclude` lists.

# File lib/overcommit/hook/base.rb, line 171
def included_files
  @included_files ||= select_applicable(all_files)
end
name() click to toggle source
# File lib/overcommit/hook/base.rb, line 54
def name
  self.class.name.split('::').last
end
parallelize?() click to toggle source
# File lib/overcommit/hook/base.rb, line 66
def parallelize?
  @config['parallelize'] != false
end
processors() click to toggle source
# File lib/overcommit/hook/base.rb, line 70
def processors
  @config.fetch('processors') { 1 }
end
quiet?() click to toggle source
# File lib/overcommit/hook/base.rb, line 74
def quiet?
  @config['quiet']
end
required?() click to toggle source
# File lib/overcommit/hook/base.rb, line 62
def required?
  @config['required']
end
required_executable() click to toggle source
# File lib/overcommit/hook/base.rb, line 123
def required_executable
  @config['required_executable']
end
required_libraries() click to toggle source
# File lib/overcommit/hook/base.rb, line 127
def required_libraries
  Array(@config['required_library'] || @config['required_libraries'])
end
run() click to toggle source

Runs the hook.

# File lib/overcommit/hook/base.rb, line 33
def run
  raise NotImplementedError, 'Hook must define `run`'
end
run?() click to toggle source
# File lib/overcommit/hook/base.rb, line 91
def run?
  enabled? &&
  !excluded? &&
    !(@config['requires_files'] && applicable_files.empty?)
end
run_and_transform() click to toggle source

Runs the hook and transforms the status returned based on the hook’s configuration.

Poorly named because we already have a bunch of hooks in the wild that implement ‘#run`, and we needed a wrapper step to transform the status based on any custom configuration.

# File lib/overcommit/hook/base.rb, line 43
def run_and_transform
  if output = check_for_requirements
    status = :fail
  else
    result = Overcommit::Utils.with_environment(@config.fetch('env') { {} }) { run }
    status, output = process_hook_return_value(result)
  end

  [transform_status(status), output]
end
skip?() click to toggle source
# File lib/overcommit/hook/base.rb, line 86
def skip?
  @config['skip'] ||
    (@config['skip_if'] ? execute(@config['skip_if']).success? : false)
end

Private Instance Methods

applicable_file?(file) click to toggle source
# File lib/overcommit/hook/base.rb, line 181
def applicable_file?(file)
  includes = Array(@config['include']).flatten.map do |glob|
    Overcommit::Utils.convert_glob_to_absolute(glob)
  end

  included = includes.empty? || includes.any? do |glob|
    Overcommit::Utils.matches_path?(glob, file)
  end

  excludes = Array(@config['exclude']).flatten.map do |glob|
    Overcommit::Utils.convert_glob_to_absolute(glob)
  end

  excluded = excludes.any? do |glob|
    Overcommit::Utils.matches_path?(glob, file)
  end

  included && !excluded
end
check_for_executable() click to toggle source

If the hook defines a required executable, check if it’s in the path and display the install command if one exists.

# File lib/overcommit/hook/base.rb, line 210
def check_for_executable
  return unless required_executable && !in_path?(required_executable)

  "'#{required_executable}' is not installed, not in your PATH, " \
  "or does not have execute permissions#{install_command_prompt}"
end
check_for_libraries() click to toggle source

If the hook defines required library paths that it wants to load, attempt to load them.

# File lib/overcommit/hook/base.rb, line 227
def check_for_libraries
  output = []

  required_libraries.each do |library|
    require library
  rescue LoadError
    install_command = @config['install_command']
    install_command = " -- install via #{install_command}" if install_command

    output << "Unable to load '#{library}'#{install_command}"
  end

  return if output.empty?

  output.join("\n")
end
check_for_requirements() click to toggle source

Check for any required executables or libraries.

Returns output if any requirements are not met.

# File lib/overcommit/hook/base.rb, line 204
def check_for_requirements
  check_for_executable || check_for_libraries
end
current_branch() click to toggle source
# File lib/overcommit/hook/base.rb, line 288
def current_branch
  @current_branch ||= Overcommit::GitRepo.current_branch
end
exclude_branches() click to toggle source
# File lib/overcommit/hook/base.rb, line 284
def exclude_branches
  @config['exclude_branches'] || []
end
install_command_prompt() click to toggle source
# File lib/overcommit/hook/base.rb, line 217
def install_command_prompt
  if install_command = @config['install_command']
    "\nInstall it by running: #{install_command}"
  else
    ''
  end
end
process_hook_return_value(hook_return_value) click to toggle source

Converts the hook’s return value into a canonical form of a tuple containing status (pass/warn/fail) and output.

This is intended to support various shortcuts for writing hooks so that hook authors don’t need to work with {Overcommit::Hook::Message} objects for simple pass/fail hooks. It also saves you from needing to manually encode logic like “if there are errors, fail; if there are warnings, warn, otherwise pass.” by simply returning an array of {Overcommit::Hook::Message} objects.

@param hook_return_value [Symbol, Array<Symbol,String>, Array<Message>] @return [Array<Symbol,String>] tuple of status and output

# File lib/overcommit/hook/base.rb, line 256
def process_hook_return_value(hook_return_value)
  if hook_return_value.is_a?(Array) &&
     (hook_return_value.first.is_a?(Message) || hook_return_value.empty?)
    # Process messages into a status and output
    Overcommit::MessageProcessor.new(
      self,
      @config['problem_on_unmodified_line'],
    ).hook_result(hook_return_value)
  else
    # Otherwise return as-is
    hook_return_value
  end
end
select_applicable(list) click to toggle source
# File lib/overcommit/hook/base.rb, line 177
def select_applicable(list)
  list.select { |file| applicable_file?(file) }.sort
end
transform_status(status) click to toggle source

Transforms the hook’s status based on custom configuration.

This allows users to change failures into warnings, or vice versa.

# File lib/overcommit/hook/base.rb, line 273
def transform_status(status)
  case status
  when :fail
    @config.fetch('on_fail') { :fail }.to_sym
  when :warn
    @config.fetch('on_warn') { :warn }.to_sym
  else
    status
  end
end