Skip to content

Instantly share code, notes, and snippets.

@mattbee
Created January 29, 2019 17:26
Show Gist options
  • Save mattbee/8db2f0da68821314d1e11332bce1e2c5 to your computer and use it in GitHub Desktop.
Save mattbee/8db2f0da68821314d1e11332bce1e2c5 to your computer and use it in GitHub Desktop.
pre-commit hook
#!/usr/bin/env ruby
REQUIRED_GEMS = %w(rubocop)
REQUIRED_GEMS.each do |gem_name|
begin
require gem_name
rescue LoadError => e
puts "=" * 50
puts "Please ensure that '#{gem_name}' is installed on your system e.g. `gem install #{gem_name}`"
puts "=" * 50
raise
end
end
class GitStatus
ADDED_OR_MODIFIED = /A|AM|^M/.freeze
def changed_files(file_extension_filters: [])
if file_extension_filters.any?
new_or_modified_filenames.select do |filename|
file_extension_filters.include?(File.extname(filename).delete('.'))
end
else
new_or_modified_filenames
end
end
private
def git_status
@git_status ||= `git status --porcelain`.split(/\n/)
end
def new_or_modified_filenames
git_status.select do |filename_with_status|
filename_with_status =~ ADDED_OR_MODIFIED
end.map do |filename_with_status|
filename_with_status.split(' ')[1]
end
end
end
class Linter
def initialize(filename_list)
@filename_list = filename_list
end
def lint!
puts "======== Linting with #{linter_name} ========"
exit_status = system("#{lint_executable} #{filenames_as_single_line}")
puts "\n"
exit_status
end
private
attr_reader :filename_list
def filenames_as_single_line
filename_list.join(' ')
end
end
class Linter::Rubocop < Linter
def self.file_extensions
%w(rb)
end
def lint_executable
'bundle exec rubocop --force-exclusion'
end
def linter_name
'rubocop'
end
end
class Linter::SASSLint < Linter
def self.file_extensions
%w(scss)
end
def lint_executable
'yarn run sass-lint'
end
def linter_name
'sass-lint'
end
end
class Linter::JSHint < Linter
def self.file_extensions
%w(js js.erb)
end
def lint_executable
'yarn run jshint-single'
end
def linter_name
'jshint'
end
private
def filename_list
@filename_list.select { |filename| filename =~ %r{app/assets/javascripts/} }
end
end
class Linter::JSCS < Linter
def self.file_extensions
%w(js js.erb)
end
def lint_executable
'yarn run jscs-single'
end
def linter_name
'jscs'
end
end
class Linter::HAMLLint < Linter
def self.file_extensions
%w(haml)
end
def lint_executable
'bundle exec haml-lint'
end
def linter_name
'haml-lint'
end
end
class Linter::ESLint < Linter
def self.file_extensions
%w(js jsx)
end
def lint_executable
'yarn run eslint-single -- --quiet'
end
def linter_name
'eslint'
end
private
def filename_list
files_that_use_es6
end
def files_that_use_es6
@filename_list.select { |filename| filename =~ %r{app/javascript/} }
end
end
class Linter::Locale < Linter
def self.file_extensions
%w(yml)
end
def lint_executable
'bundle exec rake lint:locale'
end
def linter_name
'locale'
end
private
def filename_list
@filename_list.select { |filename| filename =~ %r{config/locales/en.yml} }
end
end
linters = [
Linter::Rubocop,
Linter::JSHint,
Linter::JSCS,
Linter::ESLint,
Linter::SASSLint,
Linter::HAMLLint,
Linter::Locale
]
git_status = GitStatus.new
linter_exit_statuses = []
linters.each do |linter_class|
changed_filename_list = git_status.changed_files(file_extension_filters: linter_class.file_extensions)
if changed_filename_list.any?
linter_exit_statuses << linter_class.new(changed_filename_list).lint!
end
end
if linter_exit_statuses.all? { |status| status == true }
exit 0
else
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment