213 lines
5.7 KiB
Ruby
213 lines
5.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#
|
|
# Copyright (C) 2016 - present Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
$LOAD_PATH.push File.expand_path("../gems/dr_diff/lib", __dir__)
|
|
require "dr_diff"
|
|
|
|
class Linter
|
|
DEFAULT_OPTIONS = {
|
|
append_files_to_command: false,
|
|
auto_correct: false,
|
|
boyscout_mode: true,
|
|
campsite_mode: true,
|
|
command: nil,
|
|
comment_post_processing: proc { |comments| comments },
|
|
custom_comment_generation: false,
|
|
env_sha: ENV["SHA"] || ENV["GERRIT_PATCHSET_REVISION"],
|
|
format: nil,
|
|
file_regex: /./,
|
|
generate_comment_proc: proc {},
|
|
gerrit_patchset: !!ENV["GERRIT_PATCHSET_REVISION"],
|
|
heavy_mode: false,
|
|
include_git_dir_in_output: !!!ENV["GERRIT_PATCHSET_REVISION"],
|
|
linter_name: nil,
|
|
plugin: ENV["GERRIT_PROJECT"],
|
|
skip_file_size_check: false,
|
|
skip_wips: false,
|
|
base_dir: nil,
|
|
severe_anywhere: true
|
|
}.freeze
|
|
|
|
def initialize(options = {})
|
|
options = DEFAULT_OPTIONS.merge(options)
|
|
|
|
if options[:plugin] == "canvas-lms"
|
|
options[:plugin] = nil
|
|
end
|
|
|
|
if options[:plugin].nil?
|
|
canvas_dir = File.expand_path("..", __dir__)
|
|
plugins_dir = File.join(canvas_dir, "gems/plugins")
|
|
if Dir.pwd.start_with?(plugins_dir)
|
|
options[:plugin] = Dir.pwd[(plugins_dir.length + 1)..]
|
|
Dir.chdir(canvas_dir)
|
|
end
|
|
end
|
|
|
|
options.each do |key, value|
|
|
instance_variable_set("@#{key}", value)
|
|
end
|
|
end
|
|
|
|
def run
|
|
if git_dir && !Dir.exist?(git_dir)
|
|
puts "No plugin #{plugin} found"
|
|
return false
|
|
end
|
|
|
|
if skip_wips && wip?
|
|
puts "WIP detected, #{linter_name} will not run."
|
|
return true
|
|
end
|
|
|
|
if !skip_file_size_check && files.empty?
|
|
puts "No #{file_regex} file changes found, skipping #{linter_name} check!"
|
|
return true
|
|
end
|
|
|
|
publish_comments
|
|
end
|
|
|
|
def severe_levels
|
|
return @severe_levels if @severe_levels
|
|
|
|
boyscout_mode ? %w[info warn error fatal] : %w[warn error fatal]
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader(*DEFAULT_OPTIONS.keys)
|
|
|
|
def git_dir
|
|
@git_dir ||= plugin && "gems/plugins/#{plugin}/"
|
|
end
|
|
|
|
def dr_diff
|
|
@dr_diff ||= ::DrDiff::Manager.new(git_dir:, sha: env_sha, campsite: campsite_mode, heavy: heavy_mode, base_dir:, severe_anywhere:)
|
|
end
|
|
|
|
def wip?
|
|
dr_diff.wip?
|
|
end
|
|
|
|
def changes
|
|
dr_diff.changes
|
|
end
|
|
|
|
def files
|
|
@files ||= dr_diff.files(file_regex)
|
|
end
|
|
|
|
def full_command
|
|
if append_files_to_command
|
|
"#{command} #{files.join(" ")}"
|
|
else
|
|
command
|
|
end
|
|
end
|
|
|
|
def comments
|
|
@comments ||= dr_diff.comments(format:,
|
|
command: full_command,
|
|
include_git_dir_in_output:,
|
|
severe_levels:)
|
|
end
|
|
|
|
def generate_comments
|
|
if custom_comment_generation
|
|
generate_comment_proc.call(changes:, auto_correct:)
|
|
else
|
|
comments
|
|
end
|
|
end
|
|
|
|
def publish_comments
|
|
processed_comments = comment_post_processing.call(generate_comments)
|
|
|
|
if processed_comments.empty?
|
|
puts "-- -- -- -- -- -- -- -- -- -- --"
|
|
puts "No relevant #{linter_name} errors found!"
|
|
puts "-- -- -- -- -- -- -- -- -- -- --"
|
|
return true
|
|
end
|
|
|
|
if gerrit_patchset
|
|
if boyscout_mode
|
|
processed_comments.each do |comment|
|
|
comment[:severity] = "error"
|
|
end
|
|
end
|
|
publish_gergich_comments(processed_comments)
|
|
else
|
|
publish_local_comments(processed_comments)
|
|
if auto_correct
|
|
puts "Errors detected and possibly auto corrected."
|
|
puts "Fix and/or git add the corrections and try to commit again."
|
|
end
|
|
end
|
|
boyscout_mode ? false : processed_comments.any? { |c| severe_levels.include?(c[:severity]) }
|
|
end
|
|
|
|
def publish_gergich_comments(comments)
|
|
require "gergich"
|
|
draft = Gergich::Draft.new
|
|
|
|
cover_comments, comments = comments.partition do |comment|
|
|
comment[:cover_message]
|
|
end
|
|
|
|
comments.each do |comment|
|
|
message = +"[#{comment[:source]}] "
|
|
message << "#{comment[:rule]}: " if comment[:rule]
|
|
message << comment[:message]
|
|
|
|
draft.add_comment comment[:path],
|
|
comment[:position],
|
|
message,
|
|
comment[:severity]
|
|
end
|
|
|
|
cover_comments.each do |cover_comment|
|
|
draft.add_message(cover_comment[:message])
|
|
end
|
|
end
|
|
|
|
def publish_local_comments(comments)
|
|
require "colorize"
|
|
comments.each { |comment| pretty_comment(comment) }
|
|
end
|
|
|
|
def pretty_comment(comment)
|
|
message = +""
|
|
severity_color = severe_levels.include?(comment[:severity]) ? :red : :yellow
|
|
severity_color = :green if comment[:corrected]
|
|
message << "[#{comment[:severity]}]".colorize(severity_color)
|
|
unless comment[:cover_message]
|
|
message << " #{comment[:path].colorize(:light_blue)}:#{comment[:position]}"
|
|
end
|
|
message << " => "
|
|
message << "[Correctable] ".colorize(:yellow) if comment[:correctable]
|
|
message << "[Corrected] ".colorize(:green) if comment[:corrected]
|
|
message << "#{comment[:rule]}: " if comment[:rule]
|
|
message << comment[:message].colorize(:green)
|
|
puts message
|
|
end
|
|
end
|