#!/usr/bin/env ruby # # A hook script to verify that only syntactically valid ruby code is commited. # Called by git-commit with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # Put this code into a file called "pre-commit" inside your .git/hooks # directory, and make sure it is executable ("chmod +x .git/hooks/pre-commit") require 'open3' require "pry" include Open3 # Set this to true if you want warnings to stop your commit stop_on_warnings = (`git config --bool hooks.stop-on-warnings` != "true\n") # Set this to true if you want to skip erb files skip_erb_files = (`git config --bool hooks.skip-erb-files` == "false\n") ematch = /error/i excluded_errors = [] warnings = [] compiler_ruby = `which rubocop`.strip if compiler_ruby.length > 0 excluded_errors << %r{warning: parser/current is loading parser/ruby21, which recognizes} excluded_errors << /warning: 2\.1\.2-compliant syntax, but you are running 2\.1\.3\./ ematch = /Offenses:/ compiler_ruby << " -l" end if compiler_ruby.length == 0 warnings << /[0-9]+:\s+warning:/ compiler_ruby = `which rbx`.strip compiler_ruby = `which ruby`.strip if compiler_ruby.length == 0 compiler_ruby << " -wc" end changed_ruby_files = `git diff-index --name-only --cached HEAD`.split("\n").inject([]) do |files, line| files << line.chomp if line =~ /(.+\.(e?rb|task|rake|thor)|[Rr]akefile|[Tt]horfile)/ files end problem_files = changed_ruby_files.inject([]) do |problematic_files, file| if File.readable? file cmd = if file =~ /\.erb\z/ # Set trim mode to "-", just as Rails does "erb -xT - #{file} | #{compiler_ruby} -wc" unless skip_erb_files else "#{compiler_ruby} #{file}" end unless cmd.nil? then errors = [] output = [] popen3(cmd) do |stdin, stdout, stderr| _out = Thread.new do while line = stdout.gets output << line.chomp if line =~ ematch errors << line.chomp ematch = // if ematch == /Offenses:/ end end end _err = Thread.new do while line = stderr.gets errors << line.chomp end end _out.join _err.join end warnings.each do |matcher| errors.reject!{ |line| line =~ matcher } unless stop_on_warnings end excluded_errors.each do |matcher| errors.reject!{ |line| line =~ matcher } end unless errors.empty? errors.map!{ |line| line.sub(/#{file}:/, '') } problematic_files << "#{file}:\n#{errors.join("\n")}" end end end problematic_files end if problem_files.size > 0 $stderr.puts problem_files.join("\n") $stderr.flush puts "Would you like to commit anyway?" $stdout.flush STDIN.reopen('/dev/tty') yesno = STDIN.gets STDIN.close if yesno.to_s.match(/^y/i) puts "Ok, your call, buddy, proceeding with commit" $stdout.flush sleep 2 exit 0 else exit 1 end else # All is well exit 0 end