require 'set' module Danger # Shows the warnings and suggestions for shell scripts generated from ShellCheck. # You need [ShellCheck](https://github.com/koalaman/shellcheck) installed and generating a json file # to use this plugin. # # @example Showing summary # # shellcheck -f json myscript > shellcheck.json # shellcheck.report 'shellcheck.json' # # @see IntrepidPursuits/danger-shellcheck # @tags bash, sh, lint, syntax, format, analysis # class DangerShellcheck < Plugin # The project root, which will be used to make the paths relative. # Defaults to 'pwd'. # @return [String] project_root value attr_accessor :project_root # Defines if the test summary will be sticky or not # Defaults to 'false' # @return [Boolean] sticky attr_accessor :sticky_summary def project_root root = @project_root || Dir.pwd root += '/' unless root.end_with? '/' root end def sticky_summary @sticky_summary || false end # Reads a file with JSON ShellCheck summary and reports it. # # @param [String] file_path Path for ShellCheck summary in JSON format. # @return [void] def report(file_path) raise 'ShellCheck summary file not found' unless File.file?(file_path) shellcheck_summary = JSON.parse(File.read(file_path), symbolize_names: true) run_summary(shellcheck_summary) end private def run_summary(shellcheck_summary) @files = Set.new @error_count = 0 @warning_count = 0 @info_count = 0 @style_count = 0 # Parse the file violations parse_files(shellcheck_summary) # Output the ShellCheck summary message(summary_message, sticky: sticky_summary) end def summary_message "ShellCheck Summary: Analyzed #{@files.size} files. Found #{@error_count} errors. #{@warning_count} Warnings, #{@info_count} Info and #{@style_count} Style Violations." end # A method that takes the ShellCheck summary and parses any violations found def parse_files(shellcheck_summary) shellcheck_summary.each do |element| file = element[:file] @files.add(file) level = element[:level] message = format_violation(file, element) if level == 'error' @error_count += 1 fail(message, sticky: false) else if level == 'warning' @warning_count += 1 elsif level == 'info' @info_count += 1 else @style_count += 1 end warn(message, sticky: false) end end end # A method that returns a formatted string for a violation # @return String # def format_violation(file, violation) "#{file}: #L#{violation[:line]} -> #{violation[:code]} - #{violation[:message]}" end end end