
This seems to be a bug with how we handle name shortening for the core cask tap. The core tap always returns short formula names and returning long names from the core cask tap when not using the API leads to unexpected behavior. Specifically this can trick the `brew untap` command into thinking that there aren't any installed casks in the core cask tap and that it can be removed even when that is not the case. One risk here is that the full names were used when caching descriptions so descriptions could be out of date for people in the short term though hopefully that's not the end of the world.
108 lines
3.4 KiB
Ruby
108 lines
3.4 KiB
Ruby
# typed: true
|
|
# frozen_string_literal: true
|
|
|
|
module Homebrew
|
|
# Auditor for checking common violations in {Tap}s.
|
|
#
|
|
# @api private
|
|
class TapAuditor
|
|
attr_reader :name, :path, :formula_names, :formula_aliases, :formula_renames, :cask_tokens,
|
|
:tap_audit_exceptions, :tap_style_exceptions, :tap_pypi_formula_mappings, :problems
|
|
|
|
sig { params(tap: Tap, strict: T.nilable(T::Boolean)).void }
|
|
def initialize(tap, strict:)
|
|
Homebrew.with_no_api_env do
|
|
@name = tap.name
|
|
@path = tap.path
|
|
@tap_audit_exceptions = tap.audit_exceptions
|
|
@tap_style_exceptions = tap.style_exceptions
|
|
@tap_pypi_formula_mappings = tap.pypi_formula_mappings
|
|
@problems = []
|
|
|
|
@cask_tokens = tap.cask_tokens.map do |cask_token|
|
|
cask_token.split("/").last
|
|
end
|
|
@formula_aliases = tap.aliases.map do |formula_alias|
|
|
formula_alias.split("/").last
|
|
end
|
|
@formula_renames = tap.formula_renames
|
|
@formula_names = tap.formula_names.map do |formula_name|
|
|
formula_name.split("/").last
|
|
end
|
|
end
|
|
end
|
|
|
|
sig { void }
|
|
def audit
|
|
audit_json_files
|
|
audit_tap_formula_lists
|
|
audit_aliases_renames_duplicates
|
|
end
|
|
|
|
sig { void }
|
|
def audit_json_files
|
|
json_patterns = Tap::HOMEBREW_TAP_JSON_FILES.map { |pattern| @path/pattern }
|
|
Pathname.glob(json_patterns).each do |file|
|
|
JSON.parse file.read
|
|
rescue JSON::ParserError
|
|
problem "#{file.to_s.delete_prefix("#{@path}/")} contains invalid JSON"
|
|
end
|
|
end
|
|
|
|
sig { void }
|
|
def audit_tap_formula_lists
|
|
check_formula_list_directory "audit_exceptions", @tap_audit_exceptions
|
|
check_formula_list_directory "style_exceptions", @tap_style_exceptions
|
|
check_formula_list "pypi_formula_mappings", @tap_pypi_formula_mappings
|
|
end
|
|
|
|
sig { void }
|
|
def audit_aliases_renames_duplicates
|
|
duplicates = formula_aliases & formula_renames.keys
|
|
return if duplicates.none?
|
|
|
|
problem "The following should either be an alias or a rename, not both: #{duplicates.to_sentence}"
|
|
end
|
|
|
|
sig { params(message: String).void }
|
|
def problem(message)
|
|
@problems << ({ message:, location: nil, corrected: false })
|
|
end
|
|
|
|
private
|
|
|
|
sig { params(list_file: String, list: T.untyped).void }
|
|
def check_formula_list(list_file, list)
|
|
unless [Hash, Array].include? list.class
|
|
problem <<~EOS
|
|
#{list_file}.json should contain a JSON array
|
|
of formula names or a JSON object mapping formula names to values
|
|
EOS
|
|
return
|
|
end
|
|
|
|
list = list.keys if list.is_a? Hash
|
|
invalid_formulae_casks = list.select do |formula_or_cask_name|
|
|
formula_names.exclude?(formula_or_cask_name) &&
|
|
formula_aliases.exclude?(formula_or_cask_name) &&
|
|
cask_tokens.exclude?(formula_or_cask_name)
|
|
end
|
|
|
|
return if invalid_formulae_casks.empty?
|
|
|
|
problem <<~EOS
|
|
#{list_file}.json references
|
|
formulae or casks that are not found in the #{@name} tap.
|
|
Invalid formulae or casks: #{invalid_formulae_casks.join(", ")}
|
|
EOS
|
|
end
|
|
|
|
sig { params(directory_name: String, lists: Hash).void }
|
|
def check_formula_list_directory(directory_name, lists)
|
|
lists.each do |list_name, list|
|
|
check_formula_list "#{directory_name}/#{list_name}", list
|
|
end
|
|
end
|
|
end
|
|
end
|