Skip to content

Instantly share code, notes, and snippets.

@jumanjiman
Created August 6, 2012 15:00
Show Gist options
  • Save jumanjiman/3275053 to your computer and use it in GitHub Desktop.
Save jumanjiman/3275053 to your computer and use it in GitHub Desktop.
cross-platform git pre-commit hook for puppet repos
#!/usr/bin/env ruby
# An example cross-platform (Linux, Windows, Mac, etc.)
# pre-commit hook script to verify what is about to be committed.
# 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.
# To enable this hook:
# - save this file as <path-to-git-clone>/.git/hooks/pre-commit
# - mark file as executable
# Current checks:
# - ruby syntax
# - erb (template) syntax
# - puppet syntax
# - puppet style
# - unresolved merge conflicts
# - yaml (just check if well-formed)
# assume everything is good; override as things go bad
rc = 0
dependencies = [
'puppet',
'puppet-lint',
'open3',
]
dependencies.each do |d|
begin
require d
rescue
rc = 1
puts "Missing dependency: #{d}"
end
end
# create a reusable puppet parser object for checking syntax
parser = Puppet::Parser::Parser.new('')
# create a reusable puppet-lint object for checking style
linter = PuppetLint.new
begin
linter.quiet = true # suppress normal output
rescue
rc = 1
puts 'Oops. Need puppet-lint version from https://github.com/rodjek/puppet-lint/pull/124'
end
if rc != 0
puts 'Bailing before I hurt myself'
exit(rc)
end
if `git rev-parse --quiet --verify HEAD`.empty?
# Initial commit: diff against an empty tree object
treeish = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
else
treeish = 'HEAD'
end
# get list of staged files
filenames = `git diff-index --diff-filter=AM --name-only --cached #{treeish}`.split(/\n/)
filenames.each do |f|
# get file content from index, not from workdir
# (but skip to next filename if this commit removes the file)
(content = `git cat-file -p :0:#{f}`).empty? && next
# look for unresolved merge conflicts
# content.split(/\n/).each do |line|
# if /^<<<<<<<|^>>>>>>>|^=======$/.match(line)
# rc = 1
# puts "ERROR: #{f} appears to have unresolved merge conflicts"
# end
# end
# check for well-formed yaml
if /\.yaml$/.match(f)
begin
YAML.load(content)
rescue ArgumentError, Psych::SyntaxError
rc = 1
puts "ERROR: #{f} is not well-formed YAML"
end
end
# Ruby (.rb) and template (.erb) files
if /\.e{0,1}rb$/.match(f)
if /\.erb$/.match(f)
# convert content from erb to ruby
content = ERB.new(content, nil, '-').src
end
begin
# check ruby syntax
Open3.popen3('ruby -Ku -c') do |stdin, stdout, stderr|
stdin.puts(content)
stdin.close
output = (stdout.readline rescue '')
unless /^Syntax OK$/.match(output)
rc = 1
error = (stderr.readline rescue '')
puts "ERROR: #{f} #{error}"
end
end
rescue => except
rc = 1
puts "ERROR: #{except.message}"
end
end
# puppet (.pp) files
if /\.pp$/.match(f)
begin
# check puppet syntax
parser.parse(content)
# check puppet style, but only if parser didn't raise exception
linter.run_virtual(f, content)
if linter.errors?
rc = 1
puts "#{f}: #{linter.statistics[:error]} puppet-lint errors (fatal)"
end
rescue => except
# if we're here, puppet parser found bad syntax
rc = 1
puts "ERROR: #{f} #{except.message}"
end
end
end
# if we exit non-zero, git will abort the commit
exit(rc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment