class GitHooks::Runner
Attributes
hook_path[R]
options[R]
repo_path[R]
repository[R]
script[R]
Public Class Methods
new(options = {})
click to toggle source
# File lib/githooks/runner.rb, line 34 def initialize(options = {}) # rubocop:disable Metrics/AbcSize @repo_path = Pathname.new(options.delete('repo') || Dir.getwd) @repository = Repository.new(@repo_path) @hook_path = acquire_hooks_path(options.delete('hooks-path') || @repository.config.hooks_path || @repository.path) @script = options.delete('script') || @repository.hooks_script @options = IndifferentAccessOpenStruct.new(options) GitHooks.verbose = !!ENV['GITHOOKS_VERBOSE'] GitHooks.debug = !!ENV['GITHOOKS_DEBUG'] end
Public Instance Methods
attach()
click to toggle source
# File lib/githooks/runner.rb, line 78 def attach entry_path = Pathname.new(script || hook_path).realdirpath hook_phases = options.hooks || Hook::VALID_PHASES bootstrapper = Pathname.new(options.bootstrap).realpath if options.bootstrap if entry_path.directory? if repository.hooks_path fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to hook path #{repository.hooks_path} - Detach to continue." end repository.config.set('hooks-path', entry_path) elsif entry_path.executable? if repository.hooks_script fail Error::AlreadyAttached, "Repository [#{repo_path}] already attached to script #{repository.hooks_script}. Detach to continue." end repository.config.set('script', entry_path) else fail ArgumentError, "Provided path '#{entry_path}' is neither a directory nor an executable file." end gitrunner = bootstrapper gitrunner ||= SystemUtils.which('githooks-runner') gitrunner ||= (GitHooks::BIN_PATH + 'githooks-runner').realpath # When repos are cloned, sometimes the hooks directory isn't created repository.hooks.mkdir unless repository.hooks.directory? hook_phases.each do |hook| hook = (repository.hooks + hook).to_s puts "Linking #{gitrunner} -> #{hook}" if GitHooks.verbose FileUtils.ln_sf gitrunner.to_s, hook end end
detach(hook_phases = nil)
click to toggle source
# File lib/githooks/runner.rb, line 111 def detach(hook_phases = nil) (hook_phases || Hook::VALID_PHASES).each do |hook| next unless (repo_hook = (@repository.hooks + hook)).symlink? puts "Removing hook '#{hook}' from repository at: #{repository.path}" if GitHooks.verbose FileUtils.rm_f repo_hook end active_hooks = Hook::VALID_PHASES.select { |hook| (@repository.hooks + hook).exist? } if active_hooks.empty? puts 'All hooks detached. Removing configuration section.' repository.config.remove_section(repo_path: repository.path) else puts "Keeping configuration for active hooks: #{active_hooks.join(', ')}" end end
list()
click to toggle source
# File lib/githooks/runner.rb, line 128 def list unless script || hook_path fail Error::NotAttached, 'Repository currently not configured. Usage attach to setup for use with githooks.' end if (executables = repository.config.pre_run_execute).size > 0 puts 'PreRun Executables (in execution order):' puts executables.collect { |exe| " #{exe}" }.join("\n") puts end if script puts 'Main Test Script:' puts " #{script}" puts end if hook_path puts 'Main Testing Library with Tests (in execution order):' puts ' Tests loaded from:' puts " #{hook_path}" puts GitHooks.quieted { load_tests(true) } Hook::VALID_PHASES.each do |phase| next unless Hook.phases[phase] puts " Phase #{phase.camelize}:" Hook.phases[phase].limiters.each_with_index do |(type, limiter), limiter_index| selector = limiter.only.size > 1 ? limiter.only : limiter.only.first printf " Hook Limiter %d: %s -> %s\n", limiter_index + 1, type, selector.inspect end Hook.phases[phase].sections.each_with_index do |section, section_index| printf " %d: %s\n", section_index + 1, section.title section.actions.each_with_index do |action, action_index| section.limiters.each_with_index do |(type, limiter), limiter_index| selector = limiter.only.size > 1 ? limiter.only : limiter.only.first printf " Section Limiter %d: %s -> %s\n", limiter_index + 1, type, selector.inspect end printf " %d: %s\n", action_index + 1, action.title action.limiters.each_with_index do |(type, limiter), limiter_index| selector = limiter.only.size > 1 ? limiter.only : limiter.only.first printf " Action Limiter %d: %s -> %s\n", limiter_index + 1, type, selector.inspect end end end end puts end if (executables = repository.config.post_run_execute).size > 0 puts 'PostRun Executables (in execution order):' executables.each do |exe| puts " #{exe}" end puts end rescue Error::NotAGitRepo puts "Unable to find a valid git repo in #{repository}." puts 'Please specify path to repo via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks' raise end
run()
click to toggle source
rubocop:disable CyclomaticComplexity, MethodLength, AbcSize, PerceivedComplexity
# File lib/githooks/runner.rb, line 46 def run options.staged = options.staged.nil? ? true : options.staged if options.skip_pre puts 'Skipping PreRun Executables' else run_externals('pre-run-execute') end if script && !(options.ignore_script || GitHooks.ignore_script) command = "#{script} #{Pathname.new($0)} #{Shellwords.join(ARGV)};" puts "Kernel#exec(#{command.inspect})" if GitHooks.verbose exec(command) elsif hook_path load_tests && start else puts %q"I can't figure out what to run! Specify either path or script to give me a hint..." end if options.skip_post puts 'Skipping PostRun Executables' else run_externals('post-run-execute') end rescue SystemStackError => e puts "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}" rescue GitHooks::Error::NotAGitRepo => e puts "Unable to find a valid git repo in #{repository}." puts 'Please specify path to repository via --repo <path>' if GitHooks::SCRIPT_NAME == 'githooks' raise e end
Private Instance Methods
acquire_hooks_path(path)
click to toggle source
# File lib/githooks/runner.rb, line 197 def acquire_hooks_path(path) path = Pathname.new(path) unless path.is_a? Pathname path end
load_tests(skip_bundler = nil)
click to toggle source
# File lib/githooks/runner.rb, line 278 def load_tests(skip_bundler = nil) skip_bundler = skip_bundler.nil? ? options.skip_bundler : skip_bundler hooks_path = @hook_path.dup hooks_libs = hooks_path.join('lib') hooks_init = (p = hooks_path.join('hooks_init.rb')).exist? ? p : hooks_path.join('githooks_init.rb') gemfile = hooks_path.join('Gemfile') GitHooks.hooks_root = hooks_path if gemfile.exist? && !(skip_bundler.nil? ? ENV.include?('GITHOOKS_SKIP_BUNDLER') : skip_bundler) puts "loading Gemfile from: #{gemfile}" if GitHooks.verbose begin ENV['BUNDLE_GEMFILE'] = gemfile.to_s # stupid RVM polluting my environment without asking via it's # executable-hooks gem preloading bundler. hence the following ... if defined? Bundler [:@bundle_path, :@configured, :@definition, :@load].each do |var| ::Bundler.instance_variable_set(var, nil) end # bundler tests for @settings using defined? - which means we need # to forcibly remove it. if Bundler.instance_variables.include? :@settings Bundler.send(:remove_instance_variable, :@settings) end else require 'bundler' end ::Bundler.require(:default) rescue LoadError puts %q"Unable to load bundler - please make sure it's installed." raise rescue ::Bundler::GemNotFound => e puts "Error: #{e.message}" puts 'Did you bundle install your Gemfile?' raise end end $LOAD_PATH.unshift hooks_libs.to_s Dir.chdir repo_path if hooks_init.exist? puts "Loading hooks from #{hooks_init} ..." if GitHooks.verbose? require hooks_init.sub_ext('').to_s else puts 'Loading hooks brute-force style ...' if GitHooks.verbose? Dir["#{hooks_path}/**/*.rb"].each do |lib| lib.gsub!('.rb', '') puts " -> #{lib}" if GitHooks.verbose require lib end end true end
run_externals(which)
click to toggle source
# File lib/githooks/runner.rb, line 202 def run_externals(which) args = options.args || [] repository.config[which].all? { |executable| command = SystemUtils::Command.new(File.basename(executable), bin_path: executable) puts "#{which.camelize}: #{command.build_command(args)}" if GitHooks.verbose unless (r = command.execute(*args)).status.success? print "#{which.camelize} Executable [#{executable}] failed with error code #{r.status.exitstatus} and " if r.error.empty? puts 'no output' else puts "error message:\n\t#{r.error}" end end r.status.success? } || fail(TestsFailed, "Failed #{which.camelize} executables - giving up") end
start()
click to toggle source
# File lib/githooks/runner.rb, line 220 def start # rubocop:disable CyclomaticComplexity, MethodLength phase = options.hook || GitHooks.hook_name || 'pre-commit' puts "PHASE: #{phase}" if GitHooks.debug if (active_hook = Hook.phases[phase]) active_hook.args = options.args active_hook.staged = options.staged active_hook.untracked = options.untracked active_hook.tracked = options.tracked active_hook.repository_path = repository.path else $stderr.puts "Hook '#{phase}' not defined - skipping..." if GitHooks.verbose? || GitHooks.debug? exit!(0) # exit quickly - no need to hold things up end success = active_hook.run section_length = active_hook.sections.map { |s| s.title.length }.max sections = active_hook.sections.select { |section| !section.actions.empty? } # TODO: refactor to show this in realtime instead of after the hooks have run sections.each do |section| hash_tail_length = (section_length - section.title.length) printf "===== %s %s===== (%ds)\n", section.colored_name(phase), ('=' * hash_tail_length), section.benchmark section.actions.each_with_index do |action, index| printf " %d. [ %s ] %s (%ds)\n", (index + 1), action.status_symbol, action.colored_title, action.benchmark action.errors.each do |error| if action.success? printf " %s %s\n", GitHooks::WARNING_SYMBOL, error.color_warning! else printf " %s %s\n", GitHooks::FAILURE_SYMBOL, error end end state_string = action.success? ? GitHooks::SUCCESS_SYMBOL : GitHooks::WARNING_SYMBOL action.warnings.each do |warning| printf " %s %s\n", state_string, warning end end puts end success = false if ENV['GITHOOKS_FORCE_FAIL'] unless success command = case phase when /commit/i then 'commit' when /push/i then 'push' else phase end $stderr.puts "#{command.capitalize} failed due to errors listed above." $stderr.puts "Please fix and attempt your #{command} again." end exit(success ? 0 : 1) end