Support casks in brew audit.

This commit is contained in:
Markus Reiter 2020-11-18 12:41:18 +01:00
parent 97a7b02f8e
commit 24cef5c2ce
4 changed files with 133 additions and 62 deletions

View File

@ -51,7 +51,7 @@ module Cask
casks = casks.map { |c| CaskLoader.load(c, config: Config.from_args(args)) } casks = casks.map { |c| CaskLoader.load(c, config: Config.from_args(args)) }
casks = Cask.to_a if casks.empty? casks = Cask.to_a if casks.empty?
self.class.audit_casks( results = self.class.audit_casks(
*casks, *casks,
download: args.download?, download: args.download?,
appcast: args.appcast?, appcast: args.appcast?,
@ -62,6 +62,13 @@ module Cask
quarantine: args.quarantine?, quarantine: args.quarantine?,
language: args.language, language: args.language,
) )
self.class.print_annotations(results)
failed_casks = results.reject { |_, result| result[:errors].empty? }.map(&:first)
return if failed_casks.empty?
raise CaskError, "audit failed for casks: #{failed_casks.join(" ")}"
end end
def self.audit_casks( def self.audit_casks(
@ -92,11 +99,16 @@ module Cask
require "cask/auditor" require "cask/auditor"
failed_casks = casks.reject do |cask| casks.map do |cask|
odebug "Auditing Cask #{cask}" odebug "Auditing Cask #{cask}"
result = Auditor.audit(cask, **options) [cask, Auditor.audit(cask, **options)]
end.to_h
end
if ENV["GITHUB_ACTIONS"] def self.print_annotations(results)
return unless ENV["GITHUB_ACTIONS"]
results.each do |cask, result|
cask_path = cask.sourcefile_path cask_path = cask.sourcefile_path
annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] }) annotations = (result[:warnings].map { |w| [:warning, w] } + result[:errors].map { |e| [:error, e] })
.map { |type, message| GitHub::Actions::Annotation.new(type, message, file: cask_path) } .map { |type, message| GitHub::Actions::Annotation.new(type, message, file: cask_path) }
@ -105,13 +117,6 @@ module Cask
puts annotation if annotation.relevant? puts annotation if annotation.relevant?
end end
end end
result[:errors].empty?
end
return if failed_casks.empty?
raise CaskError, "audit failed for casks: #{failed_casks.join(" ")}"
end end
end end
end end

View File

@ -28,12 +28,12 @@ module Homebrew
def audit_args def audit_args
Homebrew::CLI::Parser.new do Homebrew::CLI::Parser.new do
usage_banner <<~EOS usage_banner <<~EOS
`audit` [<options>] [<formula>] `audit` [<options>] [<formula>|<cask>]
Check <formula> for Homebrew coding style violations. This should be run before Check <formula> for Homebrew coding style violations. This should be run before
submitting a new formula. If no <formula> are provided, check all locally submitting a new formula or cask. If no <formula>|<cask> are provided, check all
available formulae and skip style checks. Will exit with a non-zero status if any locally available formulae and casks and skip style checks. Will exit with a
errors are found. non-zero status if any errors are found.
EOS EOS
switch "--strict", switch "--strict",
description: "Run additional, stricter style checks." description: "Run additional, stricter style checks."
@ -41,8 +41,8 @@ module Homebrew
description: "Run additional, slower style checks that navigate the Git repository." description: "Run additional, slower style checks that navigate the Git repository."
switch "--online", switch "--online",
description: "Run additional, slower style checks that require a network connection." description: "Run additional, slower style checks that require a network connection."
switch "--new-formula", switch "--new", "--new-formula", "--new-cask",
description: "Run various additional style checks to determine if a new formula is eligible "\ 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 "\ "for Homebrew. This should be used when creating new formula and implies "\
"`--strict` and `--online`." "`--strict` and `--online`."
flag "--tap=", flag "--tap=",
@ -72,6 +72,18 @@ module Homebrew
description: "Specify a comma-separated <cops> list to skip checking for violations of the listed "\ description: "Specify a comma-separated <cops> list to skip checking for violations of the listed "\
"RuboCop cops." "RuboCop cops."
switch "--formula", "--formulae",
description: "Treat all named arguments as formulae."
switch "--cask", "--casks",
description: "Treat all named arguments as casks."
switch "--[no-]appcast",
description: "Audit the appcast"
switch "--token-conflicts",
description: "Audit for token conflicts"
conflicts "--formula", "--cask"
conflicts "--only", "--except" conflicts "--only", "--except"
conflicts "--only-cops", "--except-cops", "--strict" conflicts "--only-cops", "--except-cops", "--strict"
conflicts "--only-cops", "--except-cops", "--only" conflicts "--only-cops", "--except-cops", "--only"
@ -81,6 +93,7 @@ module Homebrew
end end
end end
sig { void }
def audit def audit
args = audit_args.parse args = audit_args.parse
@ -97,30 +110,34 @@ module Homebrew
git = args.git? git = args.git?
skip_style = args.skip_style? || args.no_named? || args.tap skip_style = args.skip_style? || args.no_named? || args.tap
only = :formula if args.formula? && !args.cask?
only = :cask if args.cask? && !args.formula?
ENV.activate_extensions! ENV.activate_extensions!
ENV.setup_build_environment ENV.setup_build_environment
audit_formulae = if args.tap audit_formulae, audit_casks = if args.tap
Tap.fetch(args.tap).formula_names.map { |name| Formula[name] } Tap.fetch(args.tap).formula_names.map { |name| Formula[name] }
elsif args.no_named? elsif args.no_named?
Formula [Formula, Cask::Cask.to_a]
else else
args.named.to_resolved_formulae args.named.to_formulae_and_casks(only: only)
.partition { |formula_or_cask| formula_or_cask.is_a?(Formula) }
end end
style_files = args.named.to_formulae_paths unless skip_style style_files = args.named.to_paths unless skip_style
only_cops = args.only_cops only_cops = args.only_cops
except_cops = args.except_cops except_cops = args.except_cops
options = { fix: args.fix?, debug: args.debug?, verbose: args.verbose? } style_options = { fix: args.fix?, debug: args.debug?, verbose: args.verbose? }
if only_cops if only_cops
options[:only_cops] = only_cops style_options[:only_cops] = only_cops
elsif args.new_formula? elsif args.new_formula?
nil nil
elsif except_cops elsif except_cops
options[:except_cops] = except_cops style_options[:except_cops] = except_cops
elsif !strict elsif !strict
options[:except_cops] = [:FormulaAuditStrict] style_options[:except_cops] = [:FormulaAuditStrict]
end end
# Run tap audits first # Run tap audits first
@ -129,7 +146,7 @@ module Homebrew
Tap.each do |tap| Tap.each do |tap|
next if args.tap && tap != args.tap next if args.tap && tap != args.tap
ta = TapAuditor.new tap, strict: args.strict? ta = TapAuditor.new(tap, strict: args.strict?)
ta.audit ta.audit
next if ta.problems.blank? next if ta.problems.blank?
@ -142,7 +159,7 @@ module Homebrew
end end
# Check style in a single batch run up front for performance # Check style in a single batch run up front for performance
style_offenses = Style.check_style_json(style_files, options) if style_files style_offenses = Style.check_style_json(style_files, style_options) if style_files
# load licenses # load licenses
spdx_license_data = SPDX.license_data spdx_license_data = SPDX.license_data
spdx_exception_data = SPDX.exception_data spdx_exception_data = SPDX.exception_data
@ -159,19 +176,19 @@ module Homebrew
spdx_license_data: spdx_license_data, spdx_license_data: spdx_license_data,
spdx_exception_data: spdx_exception_data, spdx_exception_data: spdx_exception_data,
tap_audit_exceptions: f.tap.audit_exceptions, tap_audit_exceptions: f.tap.audit_exceptions,
} style_offenses: style_offenses ? style_offenses.for_path(f.path) : nil,
options[:style_offenses] = style_offenses.for_path(f.path) if style_offenses display_cop_names: args.display_cop_names?,
options[:display_cop_names] = args.display_cop_names? build_stable: args.build_stable?,
options[:build_stable] = args.build_stable? }.compact
fa = FormulaAuditor.new(f, options) fa = FormulaAuditor.new(f, **options)
fa.audit fa.audit
next if fa.problems.empty? && fa.new_formula_problems.empty? next if fa.problems.empty? && fa.new_formula_problems.empty?
formula_count += 1 formula_count += 1
problem_count += fa.problems.size problem_count += fa.problems.size
problem_lines = format_problem_lines(fa.problems) problem_lines = format_problem_lines(fa.problems)
corrected_problem_count = options[:style_offenses].count(&:corrected?) if options[:style_offenses] corrected_problem_count = options[:style_offenses]&.count(&:corrected?)
new_formula_problem_lines = format_problem_lines(fa.new_formula_problems) new_formula_problem_lines = format_problem_lines(fa.new_formula_problems)
if args.display_filename? if args.display_filename?
puts problem_lines.map { |s| "#{f.path}: #{s}" } puts problem_lines.map { |s| "#{f.path}: #{s}" }
@ -189,24 +206,49 @@ module Homebrew
end end
end end
new_formula_problem_count += new_formula_problem_lines.size casks_results = if audit_casks.empty?
puts new_formula_problem_lines.map { |s| " #{s}" } []
else
require "cask/cmd/audit"
total_problems_count = problem_count + new_formula_problem_count + tap_problem_count Cask::Cmd::Audit.audit_casks(
*audit_casks,
download: nil,
appcast: args.appcast?,
online: args.online?,
strict: args.strict?,
new_cask: args.new_cask?,
token_conflicts: args.token_conflicts?,
quarantine: nil,
language: nil,
)
end
failed_casks = casks_results.reject { |_, result| result[:errors].empty? }
cask_count = failed_casks.count
cask_problem_count = failed_casks.sum { |_, result| result[:warnings].count + result[:errors].count }
new_formula_problem_count += new_formula_problem_lines.count
total_problems_count = problem_count + new_formula_problem_count + cask_problem_count + tap_problem_count
return unless total_problems_count.positive? return unless total_problems_count.positive?
problem_plural = "#{total_problems_count} #{"problem".pluralize(total_problems_count)}" puts new_formula_problem_lines.map { |s| " #{s}" }
formula_plural = "#{formula_count} #{"formula".pluralize(formula_count)}"
tap_plural = "#{tap_count} #{"tap".pluralize(tap_count)}" errors_summary = "#{total_problems_count} #{"problem".pluralize(total_problems_count)}"
corrected_problem_plural = "#{corrected_problem_count} #{"problem".pluralize(corrected_problem_count)}"
errors_summary = if tap_count.zero? error_sources = []
"#{problem_plural} in #{formula_plural} detected" error_sources << "#{formula_count} #{"formula".pluralize(formula_count)}" if formula_count.positive?
elsif formula_count.zero? error_sources << "#{cask_count} #{"cask".pluralize(cask_count)}" if cask_count.positive?
"#{problem_plural} in #{tap_plural} detected" error_sources << "#{tap_count} #{"tap".pluralize(tap_count)}" if tap_count.positive?
else
"#{problem_plural} in #{formula_plural} and #{tap_plural} detected" errors_summary += " in #{error_sources.to_sentence}" if error_sources.any?
errors_summary += " detected"
if corrected_problem_count.positive?
errors_summary += ", #{corrected_problem_count} #{"problem".pluralize(corrected_problem_count)} corrected"
end end
errors_summary += ", #{corrected_problem_plural} corrected" if corrected_problem_count.positive?
ofail errors_summary ofail errors_summary
end end

View File

@ -736,12 +736,12 @@ Print the version numbers of Homebrew, Homebrew/homebrew-core and Homebrew/homeb
## DEVELOPER COMMANDS ## DEVELOPER COMMANDS
### `audit` [*`options`*] [*`formula`*] ### `audit` [*`options`*] [*`formula`*|*`cask`*]
Check *`formula`* for Homebrew coding style violations. This should be run before Check *`formula`* for Homebrew coding style violations. This should be run before
submitting a new formula. If no *`formula`* are provided, check all locally submitting a new formula or cask. If no *`formula`*|*`cask`* are provided, check all
available formulae and skip style checks. Will exit with a non-zero status if any locally available formulae and casks and skip style checks. Will exit with a
errors are found. non-zero status if any errors are found.
* `--strict`: * `--strict`:
Run additional, stricter style checks. Run additional, stricter style checks.
@ -749,8 +749,8 @@ errors are found.
Run additional, slower style checks that navigate the Git repository. Run additional, slower style checks that navigate the Git repository.
* `--online`: * `--online`:
Run additional, slower style checks that require a network connection. Run additional, slower style checks that require a network connection.
* `--new-formula`: * `--new`:
Run various additional style checks to determine if a new formula is eligible for Homebrew. This should be used when creating new formula and implies `--strict` and `--online`. 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 `--strict` and `--online`.
* `--tap`: * `--tap`:
Check the formulae within the given tap, specified as *`user`*`/`*`repo`*. Check the formulae within the given tap, specified as *`user`*`/`*`repo`*.
* `--fix`: * `--fix`:
@ -771,6 +771,14 @@ errors are found.
Specify a comma-separated *`cops`* list to check for violations of only the listed RuboCop cops. Specify a comma-separated *`cops`* list to check for violations of only the listed RuboCop cops.
* `--except-cops`: * `--except-cops`:
Specify a comma-separated *`cops`* list to skip checking for violations of the listed RuboCop cops. Specify a comma-separated *`cops`* list to skip checking for violations of the listed RuboCop cops.
* `--formula`:
Treat all named arguments as formulae.
* `--cask`:
Treat all named arguments as casks.
* `--[no-]appcast`:
Audit the appcast
* `--token-conflicts`:
Audit for token conflicts
### `bottle` [*`options`*] *`formula`* ### `bottle` [*`options`*] *`formula`*

View File

@ -997,8 +997,8 @@ Print the version numbers of Homebrew, Homebrew/homebrew\-core and Homebrew/home
. .
.SH "DEVELOPER COMMANDS" .SH "DEVELOPER COMMANDS"
. .
.SS "\fBaudit\fR [\fIoptions\fR] [\fIformula\fR]" .SS "\fBaudit\fR [\fIoptions\fR] [\fIformula\fR|\fIcask\fR]"
Check \fIformula\fR for Homebrew coding style violations\. This should be run before submitting a new formula\. If no \fIformula\fR are provided, check all locally available formulae and skip style checks\. Will exit with a non\-zero status if any errors are found\. Check \fIformula\fR for Homebrew coding style violations\. This should be run before submitting a new formula or cask\. If no \fIformula\fR|\fIcask\fR 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\.
. .
.TP .TP
\fB\-\-strict\fR \fB\-\-strict\fR
@ -1013,8 +1013,8 @@ Run additional, slower style checks that navigate the Git repository\.
Run additional, slower style checks that require a network connection\. Run additional, slower style checks that require a network connection\.
. .
.TP .TP
\fB\-\-new\-formula\fR \fB\-\-new\fR
Run various additional style checks to determine if a new formula is eligible for Homebrew\. This should be used when creating new formula and implies \fB\-\-strict\fR and \fB\-\-online\fR\. 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 \fB\-\-strict\fR and \fB\-\-online\fR\.
. .
.TP .TP
\fB\-\-tap\fR \fB\-\-tap\fR
@ -1056,6 +1056,22 @@ Specify a comma\-separated \fIcops\fR list to check for violations of only the l
\fB\-\-except\-cops\fR \fB\-\-except\-cops\fR
Specify a comma\-separated \fIcops\fR list to skip checking for violations of the listed RuboCop cops\. Specify a comma\-separated \fIcops\fR list to skip checking for violations of the listed RuboCop cops\.
. .
.TP
\fB\-\-formula\fR
Treat all named arguments as formulae\.
.
.TP
\fB\-\-cask\fR
Treat all named arguments as casks\.
.
.TP
\fB\-\-[no\-]appcast\fR
Audit the appcast
.
.TP
\fB\-\-token\-conflicts\fR
Audit for token conflicts
.
.SS "\fBbottle\fR [\fIoptions\fR] \fIformula\fR" .SS "\fBbottle\fR [\fIoptions\fR] \fIformula\fR"
Generate a bottle (binary package) from a formula that was installed with \fB\-\-build\-bottle\fR\. If the formula specifies a rebuild version, it will be incremented in the generated DSL\. Passing \fB\-\-keep\-old\fR will attempt to keep it at its original value, while \fB\-\-no\-rebuild\fR will remove it\. Generate a bottle (binary package) from a formula that was installed with \fB\-\-build\-bottle\fR\. If the formula specifies a rebuild version, it will be incremented in the generated DSL\. Passing \fB\-\-keep\-old\fR will attempt to keep it at its original value, while \fB\-\-no\-rebuild\fR will remove it\.
. .