Skip to content

Instantly share code, notes, and snippets.

@sonota88
Last active August 22, 2018 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sonota88/980f3225ba5c95f81217a1d8c518c300 to your computer and use it in GitHub Desktop.
Save sonota88/980f3225ba5c95f81217a1d8c518c300 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# coding: utf-8
require "pp"
CS_JAR_PATH = "checkstyle-8.12-all.jar"
CS_CONFIG_PATH = "google_checks.xml"
class DiffRange
def initialize(from, len)
@from = from
@len = len
end
def include?(n)
@from + 3 <= n && n < @from + @len - 3
end
def inspect
"(#{@from},#{@len})"
end
end
class CheckstyleLine
attr_reader :path, :ln
def initialize(line)
if /^(.+?) (.+?):(.+?):(.+?): (.+)/ =~ line
@line = line
@level = $1
@path = $2
@ln = $3.to_i
@col = $4.to_i
@rest = $5
@valid = true
else
@valid = false
end
end
def rel_path(pwd_len)
@path[pwd_len + 1 .. -1]
end
def to_short
"#{@level} #{@ln}:#{@col}: #{@rest}"
end
def valid?
@valid
end
end
# diff --git a/path/to/Foo.java b/path/to/Foo.java
# new file mode 100755
# index 0000000..12eb909
# --- /dev/null
# +++ b/path/to/Foo.java
# @@ -73,10 +73,10 @@
# ...
def split_diff_output(git_diff_output)
blocks = []
buf = []
git_diff_output.each_line{|line|
line.chomp!
if /^diff \-\-git / =~ line
blocks << buf unless buf.empty?
buf = [line]
else
buf << line
end
}
blocks << buf unless buf.empty?
blocks
end
def extract_path(lines)
re = %r{^\+\+\+ b/(.+)}
path_line = lines.find{|line|
re =~ line
}
return nil unless path_line
re =~ path_line
$1
end
def extract_diff_ranges(lines)
re = /^@@ \-(.+?) \+(\d+),(\d+) @@/
lines.select{|line|
re =~ line
}.map{|line|
re =~ line
from = $2.to_i
len = $3.to_i
DiffRange.new(from, len)
}
end
def split_cs_output(cs_output)
blocks = []
buf = []
path_prev = nil
cs_output.each_line{|line|
line.chomp!
cs_line = CheckstyleLine.new(line)
next unless cs_line.valid?
if cs_line.path != path_prev
blocks << buf unless buf.empty?
buf = [line]
else
buf << line
end
path_prev = cs_line.path
}
blocks << buf unless buf.empty?
blocks
end
def select_changed(lines, diff_map)
cs_lines = lines.map{|line|
CheckstyleLine.new(line)
}
rel_path = cs_lines[0].rel_path(Dir.pwd.size)
cs_lines_filtered = cs_lines.select{|cs_line|
if diff_map.has_key?(rel_path)
diff_map[rel_path].any?{|dr| dr.include?(cs_line.ln) }
else
false
end
}
{
rel_path: rel_path,
cs_lines: cs_lines_filtered
}
end
def print_result(base_commit, blocks)
puts "# Diff from #{base_commit}"
print "\n"
blocks.each{|block|
puts "## %s (%s)" % [
File.basename(block[:rel_path]),
block[:rel_path]
]
print "\n"
puts "```"
block[:cs_lines].each{|line|
puts "- " + line.to_short()
}
puts "```"
print "\n"
}
end
def main(args)
base_commit = args[0]
git_diff_out = `git diff #{base_commit}`
# ファイルごとに分割
diff_blocks = split_diff_output(git_diff_out)
diff_map = {}
diff_blocks.each{|lines|
path = extract_path(lines)
if /\.java$/ =~ path
diff_map[path] = extract_diff_ranges(lines)
end
}
file_list = diff_map.keys.join(" ")
cs_out = `java -jar "#{CS_JAR_PATH}" -c "#{CS_CONFIG_PATH}" #{file_list}`
# ファイルごとに分割
cs_blocks = split_cs_output(cs_out)
# ファイル内での絞り込み
filtered_blocks = cs_blocks.map{|lines|
select_changed(lines, diff_map)
}.select{|item|
! item[:cs_lines].empty?
}
unless filtered_blocks.empty?
print_result(base_commit, filtered_blocks)
exit 1
end
end
main(ARGV)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment