Skip to content

Instantly share code, notes, and snippets.

@joes
Created August 17, 2011 08:46
Show Gist options
  • Save joes/1151114 to your computer and use it in GitHub Desktop.
Save joes/1151114 to your computer and use it in GitHub Desktop.
git pre-commit hook with php syntax checking and more
#!/usr/bin/env ruby
#
# Author: Joe Siponen <joe.siponen@gmail.com>
#
# A hook script to verify that only syntactically valid php code is commited.
#
# This hook also stops files containing the string '[[NOCOMMIT' from being committed.
# This provides an mechanism allowing you to protect yourself from committing
# code you have added for debugging purposes by adding the string '[[NOCOMMIT'
# near the relevant section of code.
#
# 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")
#
# Requires Ruby 1.8.6 or better
require 'pp'
require 'open3'
require 'strscan'
include Open3
workdir = Dir.getwd
head_verified = system('git rev-parse --verify HEAD >/dev/null')
if head_verified
against = 'HEAD'
else
# Initial commit: diff against an empty tree object
against = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
end
# pp ENV
def is_php_lint_error_result(result_string)
return result_string.chomp.size > 0
end
def read_file_from_git_index(file)
file_index_contents_cmd = "git show :#{file}"
file_index_contents = nil
popen3(file_index_contents_cmd) do |stdin, stdout, stderr|
file_index_contents = stdout.read
end
if file_index_contents.nil? || file_index_contents.size <= 0
$stdout.puts "Error reading #{file}: "+(file_index_contents.nil? ? 'Unreadable file' : 'Empty file')
exit 1
end
return file_index_contents
end
changed_files = `git diff-index --diff-filter=ACMRTU --name-only -z --cached #{against}`.split("\0").inject([]) do |files, line|
files << line.chomp
files
end
puts "Validating PHP syntax"
php_syntax_error_files = changed_files.inject([]) do |php_syntax_error_files, file|
if file =~ /\.php$/ #Validate php files
php_file_index_contents = read_file_from_git_index(file)
if php_file_index_contents.nil? || php_file_index_contents.size <= 0
$stdout.puts "Error reading #{file}: "+(php_file_index_contents.size <= 0 ? 'Empty file' : 'Unreadable file')
exit 1
end
puts "php -l #{file}"
php_lint_result = nil
popen3('php -l') do | stdin, stdout, stderr |
stdin.puts(php_file_index_contents)
stdin.close_write()
php_lint_result = stderr.read()
end
if is_php_lint_error_result(php_lint_result)
php_syntax_error_files << "file://#{File.join(workdir, file)}: #{php_lint_result.chomp}"
end
end
php_syntax_error_files
end
#Check for the token [[NOCOMMIT in changed files and
#stop the commit if it exists.
commit_warn_token_re = /\[\[NOCOMMIT/
commmit_warn_line_counter = 0
commit_warned_files = changed_files.inject([]) do |commit_warned_files, file|
file_contents = read_file_from_git_index(file)
commit_warn_scanner = StringScanner.new(file_contents)
while !(commit_warn_scanner.eos?)
matched = commit_warn_scanner.scan_until(commit_warn_token_re)
break if (matched.nil? or matched.size <= 0)
matched_on_line = matched.count("\n")
commmit_warn_line_counter += matched_on_line
commit_warned_files << "file://#{File.join(workdir, file)}: [[NOCOMMIT token at line #{commmit_warn_line_counter+1}"
end
commit_warned_files
end
is_valid_commit = true
if (php_syntax_error_files.size > 0)
$stderr.puts "\n== PHP Parse errors in: ==\n"+php_syntax_error_files.join("\n")
is_valid_commit = false
end
if (commit_warned_files.size > 0)
$stderr.puts "\n== Encountered [[NOCOMMIT tokens in: ==\n"+commit_warned_files.join("\n")
is_valid_commit = false
end
if is_valid_commit == true
$stdout.puts 'No errors/problems detected in pre-commit phase'
# All is well
exit 0
else
puts
$stdout.puts 'Aborting commit due to errors/problems detected in pre-commit phase'
exit 1
end
@joes
Copy link
Author

joes commented Aug 17, 2011

First version, beta quality: trying this one out for now to see what breaks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment