2023-03-13 18:31:26 -07:00
|
|
|
# typed: true
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-05-24 16:14:44 +01:00
|
|
|
require "formula"
|
2016-01-14 13:33:56 +08:00
|
|
|
require "formula_versions"
|
2017-12-03 14:02:55 +01:00
|
|
|
require "utils/curl"
|
2020-09-10 22:00:18 +02:00
|
|
|
require "utils/github/actions"
|
2020-08-26 09:42:39 +02:00
|
|
|
require "utils/shared_audits"
|
2020-08-04 10:07:57 -07:00
|
|
|
require "utils/spdx"
|
2015-05-24 16:14:44 +01:00
|
|
|
require "extend/ENV"
|
|
|
|
require "formula_cellar_checks"
|
2015-05-31 18:40:28 +08:00
|
|
|
require "cmd/search"
|
2018-06-05 23:19:18 -04:00
|
|
|
require "style"
|
2015-07-09 15:28:27 +01:00
|
|
|
require "date"
|
2017-03-18 17:02:08 +02:00
|
|
|
require "missing_formula"
|
2017-02-02 21:25:29 +00:00
|
|
|
require "digest"
|
2019-04-17 18:25:08 +09:00
|
|
|
require "cli/parser"
|
2020-06-16 01:00:36 +08:00
|
|
|
require "json"
|
2020-11-18 10:25:12 +01:00
|
|
|
require "formula_auditor"
|
2020-11-18 10:05:23 +01:00
|
|
|
require "tap_auditor"
|
2012-03-17 19:49:49 -07:00
|
|
|
|
2014-06-18 22:41:47 -05:00
|
|
|
module Homebrew
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(CLI::Parser) }
|
2023-03-13 18:31:26 -07:00
|
|
|
def self.audit_args
|
2018-07-30 18:25:38 +05:30
|
|
|
Homebrew::CLI::Parser.new do
|
2021-01-15 15:04:02 -05:00
|
|
|
description <<~EOS
|
2019-01-30 21:33:03 +00:00
|
|
|
Check <formula> for Homebrew coding style violations. This should be run before
|
2020-11-18 12:41:18 +01:00
|
|
|
submitting a new formula or cask. If no <formula>|<cask> are provided, check all
|
|
|
|
locally available formulae and casks and skip style checks. Will exit with a
|
|
|
|
non-zero status if any errors are found.
|
2018-06-28 09:28:19 +05:30
|
|
|
EOS
|
2023-04-14 15:33:40 +02:00
|
|
|
flag "--os=",
|
|
|
|
description: "Audit the given operating system. (Pass `all` to audit all operating systems.)"
|
|
|
|
flag "--arch=",
|
|
|
|
description: "Audit the given CPU architecture. (Pass `all` to audit all architectures.)"
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "--strict",
|
2020-04-16 08:24:38 +01:00
|
|
|
description: "Run additional, stricter style checks."
|
2020-06-08 15:00:09 +01:00
|
|
|
switch "--git",
|
|
|
|
description: "Run additional, slower style checks that navigate the Git repository."
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "--online",
|
2020-04-16 08:24:38 +01:00
|
|
|
description: "Run additional, slower style checks that require a network connection."
|
2022-03-08 19:24:55 +00:00
|
|
|
switch "--installed",
|
|
|
|
description: "Only check formulae and casks that are currently installed."
|
2022-09-05 13:57:22 +01:00
|
|
|
switch "--eval-all",
|
|
|
|
description: "Evaluate all available formulae and casks, whether installed or not, to audit them. " \
|
2023-02-10 23:15:40 -05:00
|
|
|
"Implied if `HOMEBREW_EVAL_ALL` is set."
|
2022-03-08 19:24:55 +00:00
|
|
|
switch "--all",
|
2022-09-05 13:57:22 +01:00
|
|
|
hidden: true
|
2020-11-18 12:41:18 +01:00
|
|
|
switch "--new", "--new-formula", "--new-cask",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Run various additional style checks to determine if a new formula or cask is eligible " \
|
|
|
|
"for Homebrew. This should be used when creating new formula and implies " \
|
2019-04-30 08:44:35 +01:00
|
|
|
"`--strict` and `--online`."
|
2020-11-27 11:41:08 -05:00
|
|
|
switch "--[no-]appcast",
|
2023-03-29 20:49:29 +02:00
|
|
|
description: "Audit the appcast.",
|
|
|
|
replacement: false
|
2022-12-06 11:00:59 +11:00
|
|
|
switch "--[no-]signing",
|
2022-12-06 20:50:00 +11:00
|
|
|
description: "Audit for signed apps, which are required on ARM"
|
2020-11-27 11:41:08 -05:00
|
|
|
switch "--token-conflicts",
|
|
|
|
description: "Audit for token conflicts."
|
2020-08-06 09:43:02 +01:00
|
|
|
flag "--tap=",
|
|
|
|
description: "Check the formulae within the given tap, specified as <user>`/`<repo>."
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "--fix",
|
2019-04-30 08:44:35 +01:00
|
|
|
description: "Fix style violations automatically using RuboCop's auto-correct feature."
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "--display-cop-names",
|
2023-04-11 00:19:09 +01:00
|
|
|
description: "Include the RuboCop cop name for each violation in the output. This is the default.",
|
|
|
|
hidden: true
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "--display-filename",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Prefix every line of output with the file or formula name being audited, to " \
|
2019-04-30 08:44:35 +01:00
|
|
|
"make output easy to grep."
|
2021-03-21 13:59:43 -04:00
|
|
|
switch "--display-failures-only",
|
2023-04-03 17:23:02 +01:00
|
|
|
description: "Only display casks that fail the audit. This is the default for formulae and casks.",
|
|
|
|
hidden: true
|
2020-04-16 08:24:38 +01:00
|
|
|
switch "--skip-style",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Skip running non-RuboCop style checks. Useful if you plan on running " \
|
2020-11-12 10:40:48 -05:00
|
|
|
"`brew style` separately. Enabled by default unless a formula is specified by name."
|
2018-09-22 09:31:30 +05:30
|
|
|
switch "-D", "--audit-debug",
|
2019-04-30 08:44:35 +01:00
|
|
|
description: "Enable debugging and profiling of audit methods."
|
2018-09-22 09:31:30 +05:30
|
|
|
comma_array "--only",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Specify a comma-separated <method> list to only run the methods named " \
|
2019-04-30 08:44:35 +01:00
|
|
|
"`audit_`<method>."
|
2018-09-22 09:31:30 +05:30
|
|
|
comma_array "--except",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Specify a comma-separated <method> list to skip running the methods named " \
|
2019-04-30 08:44:35 +01:00
|
|
|
"`audit_`<method>."
|
2018-09-22 09:31:30 +05:30
|
|
|
comma_array "--only-cops",
|
2022-06-28 10:09:59 +01:00
|
|
|
description: "Specify a comma-separated <cops> list to check for violations of only the listed " \
|
2019-04-30 08:44:35 +01:00
|
|
|
"RuboCop cops."
|
2018-09-22 09:31:30 +05:30
|
|
|
comma_array "--except-cops",
|
2022-06-28 10:17:14 +01:00
|
|
|
description: "Specify a comma-separated <cops> list to skip checking for violations of the " \
|
|
|
|
"listed RuboCop cops."
|
2020-11-18 12:41:18 +01:00
|
|
|
switch "--formula", "--formulae",
|
|
|
|
description: "Treat all named arguments as formulae."
|
|
|
|
switch "--cask", "--casks",
|
|
|
|
description: "Treat all named arguments as casks."
|
|
|
|
|
2018-10-08 22:49:03 -04:00
|
|
|
conflicts "--only", "--except"
|
2019-12-11 14:59:47 -05:00
|
|
|
conflicts "--only-cops", "--except-cops", "--strict"
|
|
|
|
conflicts "--only-cops", "--except-cops", "--only"
|
2020-11-27 11:41:08 -05:00
|
|
|
conflicts "--formula", "--cask"
|
2022-03-08 19:24:55 +00:00
|
|
|
conflicts "--installed", "--all"
|
2021-01-10 14:26:40 -05:00
|
|
|
|
|
|
|
named_args [:formula, :cask]
|
2018-03-25 17:48:22 +05:30
|
|
|
end
|
2018-07-30 18:25:38 +05:30
|
|
|
end
|
|
|
|
|
2020-11-18 12:41:18 +01:00
|
|
|
sig { void }
|
2023-03-13 18:31:26 -07:00
|
|
|
def self.audit
|
2020-07-23 06:25:35 +02:00
|
|
|
args = audit_args.parse
|
2018-02-04 22:09:35 +05:30
|
|
|
|
2023-04-14 15:33:40 +02:00
|
|
|
Formulary.enable_factory_cache!
|
|
|
|
|
|
|
|
os_arch_combinations = args.os_arch_combinations
|
2023-03-27 00:02:21 +02:00
|
|
|
|
2017-09-02 12:38:18 +05:30
|
|
|
Homebrew.auditing = true
|
2018-02-04 22:09:35 +05:30
|
|
|
inject_dump_stats!(FormulaAuditor, /^audit_/) if args.audit_debug?
|
2016-04-18 17:39:21 -04:00
|
|
|
|
2018-02-04 22:09:35 +05:30
|
|
|
new_formula = args.new_formula?
|
|
|
|
strict = new_formula || args.strict?
|
|
|
|
online = new_formula || args.online?
|
2023-06-20 22:36:15 +08:00
|
|
|
tap_audit = args.tap.present?
|
|
|
|
skip_style = args.skip_style? || args.no_named? || tap_audit
|
2023-03-13 18:31:26 -07:00
|
|
|
no_named_args = T.let(false, T::Boolean)
|
2015-07-09 12:31:17 +01:00
|
|
|
|
2020-07-28 02:04:50 +02:00
|
|
|
ENV.activate_extensions!
|
|
|
|
ENV.setup_build_environment
|
2013-05-07 18:39:45 -05:00
|
|
|
|
2023-06-19 03:57:52 +01:00
|
|
|
audit_formulae, audit_casks = with_no_api_env do # audit requires full Ruby source
|
2023-02-24 12:29:36 +00:00
|
|
|
if args.tap
|
|
|
|
Tap.fetch(args.tap).then do |tap|
|
|
|
|
[
|
|
|
|
tap.formula_names.map { |name| Formula[name] },
|
|
|
|
tap.cask_files.map { |path| Cask::CaskLoader.load(path) },
|
|
|
|
]
|
|
|
|
end
|
|
|
|
elsif args.installed?
|
|
|
|
no_named_args = true
|
|
|
|
[Formula.installed, Cask::Caskroom.casks]
|
|
|
|
elsif args.no_named?
|
|
|
|
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
|
|
|
odisabled "brew audit",
|
|
|
|
"brew audit --eval-all or HOMEBREW_EVAL_ALL"
|
|
|
|
end
|
|
|
|
no_named_args = true
|
|
|
|
[Formula.all, Cask::Cask.all]
|
|
|
|
else
|
|
|
|
if args.named.any? { |named_arg| named_arg.end_with?(".rb") }
|
|
|
|
odeprecated "brew audit [path ...]",
|
|
|
|
"brew audit [name ...]"
|
|
|
|
end
|
2022-12-21 21:40:58 -08:00
|
|
|
|
2023-02-24 12:29:36 +00:00
|
|
|
args.named.to_formulae_and_casks
|
|
|
|
.partition { |formula_or_cask| formula_or_cask.is_a?(Formula) }
|
|
|
|
end
|
2020-08-06 09:43:02 +01:00
|
|
|
end
|
2022-09-26 10:49:12 +02:00
|
|
|
|
2023-05-11 21:52:31 +02:00
|
|
|
if audit_formulae.empty? && audit_casks.empty? && !args.tap
|
2022-09-26 15:37:12 +02:00
|
|
|
ofail "No matching formulae or casks to audit!"
|
2022-09-26 10:49:12 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2020-11-18 12:41:18 +01:00
|
|
|
style_files = args.named.to_paths unless skip_style
|
2016-08-16 17:00:31 +01:00
|
|
|
|
2018-02-04 22:09:35 +05:30
|
|
|
only_cops = args.only_cops
|
|
|
|
except_cops = args.except_cops
|
2020-11-18 12:41:18 +01:00
|
|
|
style_options = { fix: args.fix?, debug: args.debug?, verbose: args.verbose? }
|
2017-05-03 11:33:00 +05:30
|
|
|
|
2018-02-04 22:09:35 +05:30
|
|
|
if only_cops
|
2020-11-18 12:41:18 +01:00
|
|
|
style_options[:only_cops] = only_cops
|
2018-02-04 22:09:35 +05:30
|
|
|
elsif args.new_formula?
|
2017-07-28 05:01:01 +05:30
|
|
|
nil
|
2018-02-04 22:09:35 +05:30
|
|
|
elsif except_cops
|
2020-11-18 12:41:18 +01:00
|
|
|
style_options[:except_cops] = except_cops
|
2017-05-03 11:33:00 +05:30
|
|
|
elsif !strict
|
2020-11-18 12:41:18 +01:00
|
|
|
style_options[:except_cops] = [:FormulaAuditStrict]
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2014-12-27 12:38:04 +00:00
|
|
|
|
2020-11-05 01:25:13 -05:00
|
|
|
# Run tap audits first
|
2023-05-19 16:59:14 +02:00
|
|
|
tap_problems = Tap.each_with_object({}) do |tap, problems|
|
2020-11-06 10:12:09 -05:00
|
|
|
next if args.tap && tap != args.tap
|
|
|
|
|
2020-11-18 12:41:18 +01:00
|
|
|
ta = TapAuditor.new(tap, strict: args.strict?)
|
2020-11-05 01:25:13 -05:00
|
|
|
ta.audit
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
problems[[tap.name, tap.path]] = ta.problems if ta.problems.any?
|
2020-11-05 01:25:13 -05:00
|
|
|
end
|
|
|
|
|
2017-04-23 04:09:13 +05:30
|
|
|
# Check style in a single batch run up front for performance
|
2022-10-18 01:32:55 +01:00
|
|
|
style_offenses = Style.check_style_json(style_files, **style_options) if style_files
|
2020-06-16 00:19:32 +08:00
|
|
|
# load licenses
|
2020-08-18 11:00:17 -04:00
|
|
|
spdx_license_data = SPDX.license_data
|
|
|
|
spdx_exception_data = SPDX.exception_data
|
2023-04-14 15:33:40 +02:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
formula_problems = audit_formulae.sort.each_with_object({}) do |f, problems|
|
2023-04-14 15:33:40 +02:00
|
|
|
path = f.path
|
|
|
|
|
2018-10-30 23:44:14 +05:30
|
|
|
only = only_cops ? ["style"] : args.only
|
2020-06-08 15:00:09 +01:00
|
|
|
options = {
|
2021-10-04 21:45:20 -04:00
|
|
|
new_formula: new_formula,
|
|
|
|
strict: strict,
|
|
|
|
online: online,
|
|
|
|
git: args.git?,
|
|
|
|
only: only,
|
|
|
|
except: args.except,
|
|
|
|
spdx_license_data: spdx_license_data,
|
|
|
|
spdx_exception_data: spdx_exception_data,
|
2022-12-13 10:54:22 +00:00
|
|
|
style_offenses: style_offenses&.for_path(f.path),
|
2023-06-20 22:36:15 +08:00
|
|
|
tap_audit: tap_audit,
|
2020-11-18 12:41:18 +01:00
|
|
|
}.compact
|
2019-09-02 10:48:19 +01:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
errors = os_arch_combinations.flat_map do |os, arch|
|
2023-04-14 15:33:40 +02:00
|
|
|
SimulateSystem.with os: os, arch: arch do
|
|
|
|
odebug "Auditing Formula #{f} on os #{os} and arch #{arch}"
|
|
|
|
|
|
|
|
Formulary.clear_cache
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
audit_proc = proc { FormulaAuditor.new(Formulary.factory(path), **options).tap(&:audit) }
|
2023-04-14 15:33:40 +02:00
|
|
|
|
|
|
|
# Audit requires full Ruby source so disable API.
|
|
|
|
# We shouldn't do this for taps however so that we don't unnecessarily require a full Homebrew/core clone.
|
|
|
|
fa = if f.core_formula?
|
2023-06-19 03:57:52 +01:00
|
|
|
with_no_api_env(&audit_proc)
|
2023-04-14 15:33:40 +02:00
|
|
|
else
|
|
|
|
audit_proc.call
|
|
|
|
end
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
fa.problems + fa.new_formula_problems
|
2021-04-03 03:49:41 +02:00
|
|
|
end
|
2023-05-19 16:59:14 +02:00
|
|
|
end.uniq
|
2021-04-03 03:49:41 +02:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
problems[[f.full_name, path]] = errors if errors.any?
|
|
|
|
end
|
2023-04-14 15:33:40 +02:00
|
|
|
|
|
|
|
if audit_casks.any?
|
|
|
|
require "cask/auditor"
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2023-03-29 00:40:46 +01:00
|
|
|
if args.display_failures_only?
|
|
|
|
odeprecated "`brew audit <cask> --display-failures-only`", "`brew audit <cask>` without the argument"
|
|
|
|
end
|
2023-04-14 15:33:40 +02:00
|
|
|
end
|
2023-03-29 00:40:46 +01:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
cask_problems = audit_casks.each_with_object({}) do |cask, problems|
|
2023-04-14 15:33:40 +02:00
|
|
|
path = cask.sourcefile_path
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
errors = os_arch_combinations.flat_map do |os, arch|
|
|
|
|
next [] if os == :linux
|
2023-04-14 15:33:40 +02:00
|
|
|
|
|
|
|
SimulateSystem.with os: os, arch: arch do
|
|
|
|
odebug "Auditing Cask #{cask} on os #{os} and arch #{arch}"
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
Cask::Auditor.audit(
|
|
|
|
Cask::CaskLoader.load(path),
|
2023-04-14 15:33:40 +02:00
|
|
|
# For switches, we add `|| nil` so that `nil` will be passed
|
|
|
|
# instead of `false` if they aren't set.
|
|
|
|
# This way, we can distinguish between "not set" and "set to false".
|
|
|
|
audit_online: (args.online? || nil),
|
|
|
|
audit_strict: (args.strict? || nil),
|
|
|
|
|
|
|
|
# No need for `|| nil` for `--[no-]signing`
|
|
|
|
# because boolean switches are already `nil` if not passed
|
|
|
|
audit_signing: args.signing?,
|
|
|
|
audit_new_cask: (args.new_cask? || nil),
|
|
|
|
audit_token_conflicts: (args.token_conflicts? || nil),
|
|
|
|
quarantine: true,
|
|
|
|
any_named_args: !no_named_args,
|
|
|
|
only: args.only,
|
|
|
|
except: args.except,
|
2023-05-19 16:59:14 +02:00
|
|
|
).to_a
|
2023-04-14 15:33:40 +02:00
|
|
|
end
|
2023-05-19 16:59:14 +02:00
|
|
|
end.uniq
|
|
|
|
|
|
|
|
problems[[cask.full_name, path]] = errors if errors.any?
|
2020-11-18 12:41:18 +01:00
|
|
|
end
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
print_problems(tap_problems, display_filename: args.display_filename?)
|
|
|
|
print_problems(formula_problems, display_filename: args.display_filename?)
|
|
|
|
print_problems(cask_problems, display_filename: args.display_filename?)
|
2018-04-30 01:22:04 +05:30
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
tap_count = tap_problems.keys.count
|
|
|
|
formula_count = formula_problems.keys.count
|
|
|
|
cask_count = cask_problems.keys.count
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
corrected_problem_count = (formula_problems.values + cask_problems.values)
|
|
|
|
.sum { |problems| problems.count { |problem| problem.fetch(:corrected) } }
|
2020-11-06 10:12:09 -05:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
tap_problem_count = tap_problems.sum { |_, problems| problems.count }
|
|
|
|
formula_problem_count = formula_problems.sum { |_, problems| problems.count }
|
|
|
|
cask_problem_count = cask_problems.sum { |_, problems| problems.count }
|
|
|
|
total_problems_count = formula_problem_count + cask_problem_count + tap_problem_count
|
2021-04-03 03:49:41 +02:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
if total_problems_count.positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true)
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
error_sources = []
|
2023-02-24 08:53:04 -08:00
|
|
|
if formula_count.positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
error_sources << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
|
2023-02-24 08:53:04 -08:00
|
|
|
end
|
2023-03-20 07:23:17 -04:00
|
|
|
error_sources << Utils.pluralize("cask", cask_count, include_count: true) if cask_count.positive?
|
|
|
|
error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_count.positive?
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
errors_summary += " in #{error_sources.to_sentence}" if error_sources.any?
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
errors_summary += " detected"
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
if corrected_problem_count.positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
errors_summary += ", #{Utils.pluralize("problem", corrected_problem_count, include_count: true)} corrected"
|
2021-04-03 03:49:41 +02:00
|
|
|
end
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
ofail "#{errors_summary}."
|
2021-04-03 03:49:41 +02:00
|
|
|
end
|
2020-11-18 12:41:18 +01:00
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
return unless ENV["GITHUB_ACTIONS"]
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
annotations = formula_problems.merge(cask_problems).flat_map do |(_, path), problems|
|
2023-04-22 15:13:35 +08:00
|
|
|
problems.map do |problem|
|
|
|
|
GitHub::Actions::Annotation.new(
|
|
|
|
:error,
|
|
|
|
problem[:message],
|
|
|
|
file: path,
|
|
|
|
line: problem[:location]&.line,
|
|
|
|
column: problem[:location]&.column,
|
|
|
|
)
|
|
|
|
end
|
2023-04-19 16:09:45 +01:00
|
|
|
end.compact
|
2018-05-22 18:16:46 +05:30
|
|
|
|
2021-04-03 03:49:41 +02:00
|
|
|
annotations.each do |annotation|
|
|
|
|
puts annotation if annotation.relevant?
|
|
|
|
end
|
2018-04-30 01:22:04 +05:30
|
|
|
end
|
2016-09-22 20:12:28 +02:00
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
def self.print_problems(results, display_filename:)
|
|
|
|
results.each do |(name, path), problems|
|
|
|
|
problem_lines = format_problem_lines(problems)
|
|
|
|
|
|
|
|
if display_filename
|
|
|
|
problem_lines.each do |l|
|
|
|
|
puts "#{path}: #{l}"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
puts name, problem_lines.map { |l| l.dup.prepend(" ") }
|
|
|
|
end
|
|
|
|
end
|
2020-09-10 22:00:18 +02:00
|
|
|
end
|
|
|
|
|
2023-05-19 16:59:14 +02:00
|
|
|
def self.format_problem_lines(problems)
|
|
|
|
problems.map do |message:, location:, corrected:|
|
|
|
|
status = " #{Formatter.success("[corrected]")}" if corrected
|
|
|
|
location = "#{location.line&.to_s&.prepend("line ")}#{location.column&.to_s&.prepend(", col ")}: " if location
|
|
|
|
"* #{location}#{message.chomp.gsub("\n", "\n ")}#{status}"
|
|
|
|
end
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2013-09-18 18:08:50 -05:00
|
|
|
end
|