Skip to content

Instantly share code, notes, and snippets.

@ianchesal
Last active August 29, 2015 14:01
Show Gist options
  • Save ianchesal/14c0c51c5710ec40e2ca to your computer and use it in GitHub Desktop.
Save ianchesal/14c0c51c5710ec40e2ca to your computer and use it in GitHub Desktop.
breathalyzer -- Run Rubocop on Changed Files in a Git-Managed Directory
#!/usr/bin/env ruby
##
# Runs rubocop on just the modified and new files in a repoistory. Handy for
# checking your compliance before you commit. It only runs against files that
# end in .rb. Maybe it should do more than that?
#
# Easy to run. In a dirty working directory:
#
# breathalyzer
#
# It runs rubocop on each dirty file separately.
require 'logger'
require 'optparse'
require 'shellwords'
def git_status(args = nil)
##
# Returns the git status output as an array of strings.
# Optional args appended to the git status call.
# Raises an error if git doesn't return a 0 error code.
git_command = Shellwords.join(%w(git status) + args.split)
git_output = `#{git_command}`
fail "git error: #{git_command}" unless $CHILD_STATUS.to_i
git_output.split("\n")
end
def get_ruby_files(options = {})
##
# Returns a list of ruby files that need to be rubocop'ed
rubyfiles = []
git_status('--short').each do |line|
(status, file) = line.split(' ', 2)
case status
when 'M', 'A', 'R', 'C', 'U'
rubyfiles.push(file) if ruby_file?(file)
when '??'
unless options[:exclude_unmanaged]
rubyfiles.push(file) if ruby_file?(file)
end
else
# Unrecognized status, do nothing
end
end
rubyfiles
end
def ruby_file?(name)
##
# Returns true if this is a file that rubocop can check.
# Otherwise false.
File.extname(name).casecmp('.rb') == 0
end
def breathalyzer(options = {})
##
# Run rubocop on some files
files = get_ruby_files(options)
if files.length == 0
puts 'No changes detected, no reason to run rubocop'
else
if options[:batch]
system(Shellwords.join(['rubocop'] + files))
else
files.each do |file|
puts "------ #{file} ------"
system(Shellwords.join(['rubocop', file]))
break if options[:stop] && $CHILD_STATUS != 0
end
end
end
end
options = { batch: true, stop: false, exclude_unmanaged: false }
opt_parser = OptionParser.new do |opt|
script_name = File.basename($PROGRAM_NAME)
opt.banner = 'Run Rubocop on Changed Files in a Git-Managed Directory'
opt.separator ''
opt.define_head(
'',
"Usage: #{script_name} [OPTIONS]",
'',
'Examples:',
'',
' * Check everything that\'s changed in batch mode',
" #{script_name}",
'',
' * Check one file at a time, stop on first violating file',
" #{script_name} -os")
opt.separator ''
opt.separator 'Options:'
opt.on(
'-x',
'--exclude-unmanaged',
'exclude unmanaged files from checks?') do
options[:exclude_unmanaged] = true
end
opt.on(
'-o',
'--one-at-a-time',
'run rubocop on file at a time?') do
options[:batch] = false
end
opt.on(
'-s',
'--stop-on-error',
'stop after the first rubocop error?') do
options[:stop] = true
end
opt.on(
'-h',
'--help',
'help') do
puts opt_parser
exit 0
end
opt.separator ''
opt.separator 'See Also:'
opt.separator ''
opt.separator ' * Rubocop: https://github.com/bbatsov/rubocop'
opt.separator ' * My Rubocop Settings: https://gist.github.com/ianchesal/352a7963b97a92562a45'
end
opt_parser.parse!
breathalyzer(options)
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment