Inspired and adapted from https://gist.github.com/skanev/9d4bec97d5a6825eaaf6 https://gist.github.com/MaxLap/ea4b6d1df81de3024562798b5501b9c8pull/10894/head
parent
e25d2f14ee
commit
d1ce52f9f0
@ -0,0 +1,202 @@ |
|||||||
|
#!/usr/bin/env ruby |
||||||
|
|
||||||
|
# A sneaky wrapper around Rubocop that allows you to run it only against |
||||||
|
# the recent changes, as opposed to the whole project. It lets you |
||||||
|
# enforce the style guide for new/modified code only, as opposed to |
||||||
|
# having to restyle everything or adding cops incrementally. It relies |
||||||
|
# on git to figure out which files to check. |
||||||
|
# |
||||||
|
# Here are some options you can pass in addition to the ones in rubocop: |
||||||
|
# |
||||||
|
# --local Check only the changes you are about to push |
||||||
|
# to the remote repository. |
||||||
|
# |
||||||
|
# --staged Check only changes that are currently staged. |
||||||
|
# |
||||||
|
# --uncommitted Check only changes in files that have not been |
||||||
|
# --index committed (i.e. either in working directory or |
||||||
|
# staged). |
||||||
|
# |
||||||
|
# --against REFSPEC Check changes since REFSPEC. This can be |
||||||
|
# anything that git will recognize. |
||||||
|
# |
||||||
|
# --branch Check only changes in the current branch. |
||||||
|
# |
||||||
|
# Caveat emptor: |
||||||
|
# |
||||||
|
# * Monkey patching ahead. This script relies on Rubocop internals and |
||||||
|
# has been tested against 1.30.1. Newer (or older) versions might |
||||||
|
# break it. |
||||||
|
# |
||||||
|
# * While it does try to check modified lines only, there might be some |
||||||
|
# quirks. It might not show offenses in modified code if they are |
||||||
|
# reported at unmodified lines. It might also show offenses in |
||||||
|
# unmodified code if they are reported in modified lines. |
||||||
|
|
||||||
|
# Inspired and adapted from |
||||||
|
# https://gist.github.com/skanev/9d4bec97d5a6825eaaf6 |
||||||
|
# https://gist.github.com/MaxLap/ea4b6d1df81de3024562798b5501b9c8 |
||||||
|
|
||||||
|
require 'rubocop' |
||||||
|
|
||||||
|
module DirtyCop |
||||||
|
extend self # In your face, style guide! |
||||||
|
|
||||||
|
def bury_evidence?(file, line) |
||||||
|
!report_offense_at?(file, line) |
||||||
|
end |
||||||
|
|
||||||
|
def bury_evidences(file, offenses) |
||||||
|
offenses.reject { |o| bury_evidence?(file, o.line) } |
||||||
|
end |
||||||
|
|
||||||
|
def staged_changes_only? |
||||||
|
!!@staged_changes_only |
||||||
|
end |
||||||
|
|
||||||
|
def uncovered_targets |
||||||
|
@files |
||||||
|
end |
||||||
|
|
||||||
|
def cover_up_unmodified(ref) |
||||||
|
@files = files_modified_since(ref) |
||||||
|
@line_filter = build_line_filter(@files, ref) |
||||||
|
end |
||||||
|
|
||||||
|
def process_bribe |
||||||
|
eat_a_donut if ARGV.empty? |
||||||
|
|
||||||
|
ref = nil |
||||||
|
|
||||||
|
loop do |
||||||
|
arg = ARGV.shift |
||||||
|
case arg |
||||||
|
when '--local' |
||||||
|
ref = `git rev-parse --abbrev-ref --symbolic-full-name @{u}` |
||||||
|
exit 1 unless $?.success? |
||||||
|
when '--staged' |
||||||
|
ref = '--cached' |
||||||
|
@staged_changes_only = true |
||||||
|
ARGV << "--cache=false" |
||||||
|
when '--against' |
||||||
|
ref = ARGV.shift |
||||||
|
when '--uncommitted', '--index' |
||||||
|
ref = 'HEAD' |
||||||
|
when '--branch' |
||||||
|
ref = `git merge-base HEAD dev` |
||||||
|
else |
||||||
|
ARGV.unshift arg |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return unless ref |
||||||
|
|
||||||
|
cover_up_unmodified ref.chomp |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def report_offense_at?(file, line) |
||||||
|
@line_filter && @line_filter.fetch(file)[line] |
||||||
|
end |
||||||
|
|
||||||
|
def files_modified_since(ref) |
||||||
|
`git diff --diff-filter=AM --name-only #{ref}` |
||||||
|
.lines |
||||||
|
.map(&:chomp) |
||||||
|
.map { |file| File.absolute_path(file) } |
||||||
|
end |
||||||
|
|
||||||
|
def build_line_filter(suspects, ref) |
||||||
|
result = {} |
||||||
|
|
||||||
|
suspects.each do |file| |
||||||
|
result[file] = lines_modified_since(file, ref) |
||||||
|
end |
||||||
|
|
||||||
|
result |
||||||
|
end |
||||||
|
|
||||||
|
def lines_modified_since(file, ref) |
||||||
|
ranges = |
||||||
|
`git diff -p -U0 #{ref} #{file}` |
||||||
|
.lines |
||||||
|
.grep(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/) { $1.to_i...($1.to_i + ($2 || 1).to_i) } |
||||||
|
.reverse |
||||||
|
|
||||||
|
return [] if ranges.empty? |
||||||
|
|
||||||
|
mask = Array.new(ranges.first.end) |
||||||
|
|
||||||
|
ranges.each do |range| |
||||||
|
range.each do |line| |
||||||
|
mask[line] = true |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
mask |
||||||
|
end |
||||||
|
|
||||||
|
def eat_a_donut |
||||||
|
puts "#$PROGRAM_NAME: The dirty cop Alex Murphy could have been" |
||||||
|
puts |
||||||
|
puts File.read(__FILE__)[/(?:^#(?:[^!].*)?\n)+/s].gsub(/^#/, ' ') |
||||||
|
exit |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
module RuboCop |
||||||
|
class TargetFinder |
||||||
|
alias find_unpatched find |
||||||
|
|
||||||
|
def find(...) |
||||||
|
every_files = find_unpatched(...) |
||||||
|
modified_files = DirtyCop.uncovered_targets |
||||||
|
|
||||||
|
if modified_files |
||||||
|
every_files & modified_files |
||||||
|
else |
||||||
|
every_files |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
class Runner |
||||||
|
alias inspect_file_unpatched inspect_file |
||||||
|
|
||||||
|
def inspect_file(file, *args) |
||||||
|
offenses, updated = inspect_file_unpatched(file, *args) |
||||||
|
offenses = offenses.reject { |o| DirtyCop.bury_evidence?(file.path, o.line) } |
||||||
|
[offenses, updated] |
||||||
|
end |
||||||
|
|
||||||
|
alias add_redundant_disables_unpatched add_redundant_disables |
||||||
|
def add_redundant_disables(file, offenses, source) |
||||||
|
offenses = add_redundant_disables_unpatched(file, offenses, source) |
||||||
|
DirtyCop.bury_evidences(file, offenses) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
class ProcessedSource |
||||||
|
class << self |
||||||
|
alias from_file_unpatched from_file |
||||||
|
end |
||||||
|
|
||||||
|
def self.from_file(path, ruby_version) |
||||||
|
if DirtyCop.staged_changes_only? |
||||||
|
pathname = Pathname.new(path) |
||||||
|
git_root = Pathname.new(`git rev-parse --show-toplevel`.strip) |
||||||
|
git_relative_path = pathname.relative_path_from(git_root).to_s |
||||||
|
source = `git show :#{git_relative_path}` |
||||||
|
new(source, ruby_version, path) |
||||||
|
else |
||||||
|
from_file_unpatched(path, ruby_version) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
DirtyCop.process_bribe |
||||||
|
|
||||||
|
exit RuboCop::CLI.new.run |
Loading…
Reference in new issue