Skip to content

Instantly share code, notes, and snippets.

@spalenza
Last active May 30, 2017 18:24
Show Gist options
  • Save spalenza/259fbdaddf27c303fd2f to your computer and use it in GitHub Desktop.
Save spalenza/259fbdaddf27c303fd2f to your computer and use it in GitHub Desktop.
Git hook for commit. Add in .git/hooks/pre-commit
#!/usr/bin/env ruby
# .git/hooks/pre-commit
# Git pre-commit hook that catches errors that I commonly make.
#
# To intentionally ignore the hook (i.e., when adding an alert call), commit
# from the command line with "--no-verify"
#
# Loosely based on Henrik Nyh's <http://henrik.nyh.se> work (2011-10-08)
# under the MIT License.
#
# Bob Gilmore ([email protected])
############# CONFIGURATION
# The two sections of regular expressions below ("forbidden" and "warning")
# will trigger a commit failure, *if* they are found on an added or edited line.
# "Forbidden" regular expressions
FORBIDDEN = [
/TMP_DEBUG/, # My TextExpander macros for embedding debug code always include this for easy scanning.
/>>>>>>/, # Git conflict markers
/<<<<<</, # ''
/======/, # ''
/console\.debug/, # JavaScript debug code that would break IE.
/console\.log/, # ''
/binding\.pry/, # pry debugging code
/binding\.remote_pry/, # ''
/save_and_open_page/, # Launchy debugging code
/debugger/, # Ruby debugging code
/focus: true/ # Focus to rspec
]
USER = ENV["USER"]
USER_HOME = [
Regexp.new("/home/#{USER}"), # Hard-coded path to user's HOME
Regexp.new("/Users/#{USER}"), # ''
Regexp.new("/export/home/#{USER}") # '' (for a current work config)
]
# Warning signs that someone is committing a private key
PRIVATE_KEY = [
/PRIVATE KEY/,
/ssh-rsa/
]
# Things that I probably don't want to commit.
# Will force a rejection, but will "label" them in the error message
# as "merely" warnings.
WARNING = [
/alert/, # I *almost* never want to check in a call to commit.
/logger\.debug/ # Similarly, I almost never want to commit a (Ruby) call to logger.debug. error, message, etc., but not debug.
]
# For particular projects - configurable on a per-project git config basis
# Check to ensure that if you add JavaScript or CSS, you change production.rb. This is required at my current employer.
NEW_ASSETS_REQUIRE_PRODUCTION_CHANGE = ( `git config hooks.newassetsrequireproductionchange`.strip || 'false') == 'true'
# Prevent inadvertent .ruby-version and .rbenv-version commits. I rarely edit these files, so any attempt to commit is probably an accident.
ALLOW_RUBY_VERSION_CHANGES = ( `git config hooks.allowrubyversionchange`.strip || 'false') == 'true'
############# END OF CONFIGURATION
# Check for "forbidden" and "warning" strings
# Loop over ALL errors and warnings and return ALL problems.
# I want to report on *all* problems that exist in the commit before aborting,
# so that anyone calling --no-verify has been informed of all problems first.
error_found = false
full_diff = `git diff --cached --`
full_diff.scan(%r{^\+\+\+ b/(.+)\n@@.*\n([\s\S]*?)(?:^diff|\z)}).each do |file, diff|
added = diff.split("\n").select { |x| x.start_with?("+") }.join("\n")
# Scan for "forbidden" calls
FORBIDDEN.each do |re|
if added.match(re)
puts %{Error: git pre-commit hook forbids committing lines with "#{$1 || $&}" to #{file}\n--------------}
error_found = true
end
end
# Scan for probable HARD_CODED references to user's home directory
USER_HOME.each do |re|
if added.match(re)
puts %{Error: git pre-commit hook forbids committing lines that look like a hard-coded home directory: "#{$1 || $&}" to #{file}\n--------------}
error_found = true
end
end
# Scan for private key indicators
PRIVATE_KEY.each do |re|
if added.match(re)
puts %{Error: git pre-commit hook detected a probable private key commit: "#{$1 || $&}" to #{file}\n--------------}
error_found = true
end
end
# Scan for "suspect" calls
WARNING.each do |re|
if added.match(re)
puts %{Warning: git pre-commit hook is suspicious of committing lines with "#{$1 || $&}" to #{file}\n--------------}
error_found = true
end
end
end
=begin
# Syntax check .rb files.
toplevel = `git rev-parse --show-toplevel`
name_only = `git diff --cached --name-only`
name_only.split(/\n/).each do |fil|
if File.extname(fil) == '.rb'
fullfile = File.join(toplevel.strip, fil)
if File.exist?(fullfile)
output = `ruby -c #{fullfile}`
status = $?.success?
unless status
puts %{Error: git pre-commit hook found a syntax error in #{file}}
error_found = true
end
end
end
end
=end
name_only = `git diff --cached --name-only`
# Ensure that production.rb is changed when assets are added
if NEW_ASSETS_REQUIRE_PRODUCTION_CHANGE
name_only_added = `git diff --cached --name-only --diff-filter=A`
has_production = name_only.include?('production.rb')
name_only_added.split(/\n/).each do |fil|
ext = File.extname(fil)
if (ext == '.css' || ext == '.scss' || ext == '.js' || ext == '.coffee' || ext == '.less') && !has_production
puts %{Error: git pre-commit hook found attempt to add #{fil} without editing production.rb\nThis is probably an error.\nTo allow this for this project, run\ngit config hooks.newassetsrequireproductionchange true\nand try again.\n-------------- }
error_found = true
end
end
end
# Prevent changes to .ruby-version (and .rbenv-version)
unless ALLOW_RUBY_VERSION_CHANGES
name_only.split(/\n/).each do |fil|
base = File.basename(fil)
if base == '.ruby-version' || base == '.rbenv-version'
puts %{Warning: git pre-commit hook found attempt to edit #{fil}.\nThis is probably an error.\nTo allow this for this project, run\ngit config hooks.allowrubyversionchange true\nand try again.\n-------------- }
error_found = true
end
end
end
# Finally, report errors
if error_found
puts "To commit anyway, use --no-verify"
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment