Created
June 23, 2012 12:51
-
-
Save mschuerig/2978195 to your computer and use it in GitHub Desktop.
Pre-commit hook for git
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/ruby -w | |
# Michael Schuerig, <[email protected]>, 2012. | |
# This file is in the public domain. | |
# Any trouble it may cause is yours, not mine. | |
# | |
# This is a pre-commit hook for git with a few checks I find useful | |
# useful in Rails projects. The checks are: | |
# | |
# * Bad tags (such as XXX, WTF, ...) as specified in | |
# git config hooks.pre-commit-bad-tags | |
# This options may contain a comma-separated list of strings | |
# or regular expressions. The later must be written as /expr/. | |
# | |
# * Paths in Gemfile. | |
# | |
# * Only allowed platforms in Gemfile.lock as specified in | |
# git config hooks.pre-commit-allowed-platforms | |
# | |
# | |
# Installation | |
# | |
# Put this file anywhere that's convenient. If it is on your PATH | |
# and executable, you can invoke it manually to run the checks. | |
# | |
# To use it as a pre-commit hook in a git repository, place a | |
# symbolic link to this file at <repository>/.git/hooks/pre-commit. | |
# | |
# To override checks when committing, invoke git commit with the | |
# --no-verify option. | |
require 'open3' | |
def main | |
ok = true | |
ok &&= check_staged_files | |
ok &&= check_gemfile | |
ok &&= check_gemfile_lock | |
exit ok ? 0 : 1 | |
end | |
def shell(*cmd) | |
out, err = Open3.popen3(cmd.join(' ')) do |stdin, stdout, stderr| | |
[stdout.read, stderr.read] | |
end | |
unless err.empty? | |
raise "Git exited with an error #{cmd}:\n#{err}" | |
end | |
out | |
end | |
def git_config(name) | |
shell("git config '#{name}'").strip | |
end | |
def git_configs(name) | |
git_config(name).split(/\s*,\s*/) | |
end | |
def bad_tags | |
tags = git_configs('hooks.pre-commit-bad-tags') | |
tags.map { |t| | |
if %r{^/(?<pat>.*)/$} =~ t | |
Regexp.new(pat) | |
else | |
Regexp.new("\\b#{t}\\b") | |
end | |
} | |
end | |
def allowed_platforms | |
git_configs('hooks.pre-commit-allowed-platforms') | |
end | |
def staged_text_files | |
files = shell(%q{git status --porcelain | sed -n 's/^M. \(.*\)$/\1/p' | file --mime-type -f - | sed -n 's/\(.*\): *text\/[^ /]*$/\1/p'}) | |
files.lines.map { |f| f.chomp } | |
end | |
def cat_staged_file(file) | |
shell("git show ':#{file}'") | |
end | |
def in_repo?(file) | |
!shell("git status --porcelain '#{file}'").strip.empty? | |
end | |
def gemfile_lock_platforms(gl = 'Gemfile.lock') | |
section = cat_staged_file(gl).lines.select do |l| | |
!!((/^PLATFORMS/ =~ l) .. (/^$/ =~ l)) | |
end | |
section[1..-2].map(&:strip) | |
end | |
def report_bad_lines(bad_lines, message) | |
ok = true | |
if !bad_lines.empty? | |
ok = false | |
puts message | |
bad_lines.each do |line, num| | |
printf "%5d: %s\n", num + 1, line | |
end | |
end | |
ok | |
end | |
def check_staged_files | |
ok = true | |
tags = bad_tags | |
staged_text_files.each do |file| | |
bad_lines = cat_staged_file(file).lines.with_index.select { |line, num| | |
tags.any? { |t| t =~ line } | |
} | |
ok2 = report_bad_lines(bad_lines, "File #{file} contains lines with prohibited tags:") | |
ok &&= ok2 | |
end | |
ok | |
end | |
def check_gemfile(g = 'Gemfile') | |
ok = true | |
if in_repo?(g) | |
bad_lines = cat_staged_file(g).lines.with_index.select { |line, num| | |
/^[^#]*\bpath\b/ =~ line | |
} | |
ok2 = report_bad_lines(bad_lines, "#{g} contains paths:") | |
ok &&= ok2 | |
end | |
ok | |
end | |
def check_gemfile_lock(gl = 'Gemfile.lock') | |
ok = true | |
allowed = allowed_platforms | |
if !allowed.empty? && in_repo?(gl) | |
bad_platforms = gemfile_lock_platforms(gl).reject { |p| | |
allowed.include?(p) | |
} | |
unless bad_platforms.empty? | |
ok = false | |
puts "#{gl} for prohibited platform(s): #{bad_platforms.join(', ')}" | |
end | |
end | |
ok | |
end | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment