Merge pull request #13613 from mohammadzainabbas/mohammad
Augment `brew livecheck` with a `--resources` option to check resources
This commit is contained in:
commit
639e8eb237
@ -30,6 +30,9 @@ module Homebrew
|
||||
sig { returns(T::Boolean) }
|
||||
def newer_only?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def resources?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def full_name?; end
|
||||
|
||||
|
||||
@ -72,7 +72,8 @@ module Homebrew
|
||||
|
||||
ambiguous_casks = []
|
||||
if !args.formula? && !args.cask?
|
||||
ambiguous_casks = formulae_and_casks.group_by { |item| Livecheck.formula_or_cask_name(item, full_name: true) }
|
||||
ambiguous_casks = formulae_and_casks \
|
||||
.group_by { |item| Livecheck.package_or_resource_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -82,7 +83,7 @@ module Homebrew
|
||||
ambiguous_names = []
|
||||
unless args.full_name?
|
||||
ambiguous_names =
|
||||
(formulae_and_casks - ambiguous_casks).group_by { |item| Livecheck.formula_or_cask_name(item) }
|
||||
(formulae_and_casks - ambiguous_casks).group_by { |item| Livecheck.package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -92,7 +93,7 @@ module Homebrew
|
||||
puts if i.positive?
|
||||
|
||||
use_full_name = args.full_name? || ambiguous_names.include?(formula_or_cask)
|
||||
name = Livecheck.formula_or_cask_name(formula_or_cask, full_name: use_full_name)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
repository = if formula_or_cask.is_a?(Formula)
|
||||
if formula_or_cask.head_only?
|
||||
ohai name
|
||||
@ -157,7 +158,7 @@ module Homebrew
|
||||
rescue
|
||||
next
|
||||
end
|
||||
name = Livecheck.formula_or_cask_name(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
ambiguous_cask = begin
|
||||
formula_or_cask.is_a?(Cask::Cask) && !args.cask? && Formula[name]
|
||||
rescue FormulaUnavailableError
|
||||
@ -178,7 +179,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def livecheck_result(formula_or_cask)
|
||||
name = Livecheck.formula_or_cask_name(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
|
||||
referenced_formula_or_cask, =
|
||||
Livecheck.resolve_livecheck_reference(formula_or_cask, full_name: false, debug: false)
|
||||
|
||||
@ -37,6 +37,8 @@ module Homebrew
|
||||
description: "Show the latest version only if it's newer than the formula/cask."
|
||||
switch "--json",
|
||||
description: "Output information in JSON format."
|
||||
switch "-r", "--resources",
|
||||
description: "Also check resources for formulae."
|
||||
switch "-q", "--quiet",
|
||||
description: "Suppress warnings, don't print a progress bar for JSON output."
|
||||
switch "--formula", "--formulae",
|
||||
@ -101,6 +103,7 @@ module Homebrew
|
||||
else
|
||||
raise UsageError, "A watchlist file is required when no arguments are given."
|
||||
end
|
||||
|
||||
formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask|
|
||||
formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
|
||||
end
|
||||
@ -111,6 +114,7 @@ module Homebrew
|
||||
json: args.json?,
|
||||
full_name: args.full_name?,
|
||||
handle_name_conflict: !args.formula? && !args.cask?,
|
||||
check_resources: args.resources?,
|
||||
newer_only: args.newer_only?,
|
||||
quiet: args.quiet?,
|
||||
debug: args.debug?,
|
||||
|
||||
@ -126,11 +126,11 @@ module Homebrew
|
||||
if debug
|
||||
# Print the chain of references for debugging
|
||||
puts "Reference Chain:"
|
||||
puts formula_or_cask_name(first_formula_or_cask, full_name: full_name)
|
||||
puts package_or_resource_name(first_formula_or_cask, full_name: full_name)
|
||||
|
||||
references << referenced_formula_or_cask
|
||||
references.each do |ref_formula_or_cask|
|
||||
puts formula_or_cask_name(ref_formula_or_cask, full_name: full_name)
|
||||
puts package_or_resource_name(ref_formula_or_cask, full_name: full_name)
|
||||
end
|
||||
end
|
||||
|
||||
@ -157,11 +157,14 @@ module Homebrew
|
||||
|
||||
# Executes the livecheck logic for each formula/cask in the
|
||||
# `formulae_and_casks_to_check` array and prints the results.
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
sig {
|
||||
params(
|
||||
formulae_and_casks_to_check: T::Array[T.any(Formula, Cask::Cask)],
|
||||
full_name: T::Boolean,
|
||||
handle_name_conflict: T::Boolean,
|
||||
check_resources: T::Boolean,
|
||||
json: T::Boolean,
|
||||
newer_only: T::Boolean,
|
||||
debug: T::Boolean,
|
||||
@ -171,14 +174,15 @@ module Homebrew
|
||||
}
|
||||
def run_checks(
|
||||
formulae_and_casks_to_check,
|
||||
full_name: false, handle_name_conflict: false, json: false, newer_only: false,
|
||||
full_name: false, handle_name_conflict: false, check_resources: false, json: false, newer_only: false,
|
||||
debug: false, quiet: false, verbose: false
|
||||
)
|
||||
load_other_tap_strategies(formulae_and_casks_to_check)
|
||||
|
||||
ambiguous_casks = []
|
||||
if handle_name_conflict
|
||||
ambiguous_casks = formulae_and_casks_to_check.group_by { |item| formula_or_cask_name(item, full_name: true) }
|
||||
ambiguous_casks = formulae_and_casks_to_check \
|
||||
.group_by { |item| package_or_resource_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -188,7 +192,7 @@ module Homebrew
|
||||
ambiguous_names = []
|
||||
unless full_name
|
||||
ambiguous_names =
|
||||
(formulae_and_casks_to_check - ambiguous_casks).group_by { |item| formula_or_cask_name(item) }
|
||||
(formulae_and_casks_to_check - ambiguous_casks).group_by { |item| package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -218,7 +222,7 @@ module Homebrew
|
||||
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
|
||||
|
||||
use_full_name = full_name || ambiguous_names.include?(formula_or_cask)
|
||||
name = formula_or_cask_name(formula_or_cask, full_name: use_full_name)
|
||||
name = package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
|
||||
referenced_formula_or_cask, livecheck_references =
|
||||
resolve_livecheck_reference(formula_or_cask, full_name: use_full_name, debug: debug)
|
||||
@ -282,6 +286,30 @@ module Homebrew
|
||||
version_info[:latest] if version_info.present?
|
||||
end
|
||||
|
||||
check_for_resources = check_resources && formula_or_cask.is_a?(Formula) && formula_or_cask.resources.present?
|
||||
if check_for_resources
|
||||
resource_version_info = formula_or_cask.resources.map do |resource|
|
||||
res_skip_info ||= SkipConditions.skip_information(resource, verbose: verbose)
|
||||
if res_skip_info.present?
|
||||
res_skip_info
|
||||
else
|
||||
res_version_info = resource_version(
|
||||
resource,
|
||||
json: json,
|
||||
debug: debug,
|
||||
quiet: quiet,
|
||||
verbose: verbose,
|
||||
)
|
||||
if res_version_info.empty?
|
||||
status_hash(resource, "error", ["Unable to get versions"], verbose: verbose)
|
||||
else
|
||||
res_version_info
|
||||
end
|
||||
end
|
||||
end.compact_blank
|
||||
Homebrew.failed = true if resource_version_info.any? { |info| info[:status] == "error" }
|
||||
end
|
||||
|
||||
if latest.blank?
|
||||
no_versions_msg = "Unable to get versions"
|
||||
raise Livecheck::Error, no_versions_msg unless json
|
||||
@ -289,7 +317,14 @@ module Homebrew
|
||||
|
||||
next version_info if version_info.is_a?(Hash) && version_info[:status] && version_info[:messages]
|
||||
|
||||
next status_hash(formula_or_cask, "error", [no_versions_msg], full_name: use_full_name, verbose: verbose)
|
||||
latest_info = status_hash(formula_or_cask, "error", [no_versions_msg], full_name: use_full_name,
|
||||
verbose: verbose)
|
||||
if check_for_resources
|
||||
resource_version_info.map! { |r| r.except!(:meta) } unless verbose
|
||||
latest_info[:resources] = resource_version_info
|
||||
end
|
||||
|
||||
next latest_info
|
||||
end
|
||||
|
||||
if (m = latest.to_s.match(/(.*)-release$/)) && !current.to_s.match(/.*-release$/)
|
||||
@ -324,6 +359,8 @@ module Homebrew
|
||||
info[:meta][:head_only] = true if formula&.head_only?
|
||||
info[:meta].merge!(version_info[:meta]) if version_info.present? && version_info.key?(:meta)
|
||||
|
||||
info[:resources] = resource_version_info if check_for_resources
|
||||
|
||||
next if newer_only && !info[:version][:outdated]
|
||||
|
||||
has_a_newer_upstream_version ||= true
|
||||
@ -331,10 +368,12 @@ module Homebrew
|
||||
if json
|
||||
progress&.increment
|
||||
info.except!(:meta) unless verbose
|
||||
resource_version_info.map! { |r| r.except!(:meta) } if check_for_resources && !verbose
|
||||
next info
|
||||
end
|
||||
|
||||
puts if debug
|
||||
print_latest_version(info, verbose: verbose, ambiguous_cask: ambiguous_casks.include?(formula_or_cask))
|
||||
print_resources_info(resource_version_info, verbose: verbose) if check_for_resources
|
||||
nil
|
||||
rescue => e
|
||||
Homebrew.failed = true
|
||||
@ -344,11 +383,12 @@ module Homebrew
|
||||
progress&.increment
|
||||
status_hash(formula_or_cask, "error", [e.to_s], full_name: use_full_name, verbose: verbose) unless quiet
|
||||
elsif !quiet
|
||||
name = formula_or_cask_name(formula_or_cask, full_name: use_full_name)
|
||||
name = package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
name += " (cask)" if ambiguous_casks.include?(formula_or_cask)
|
||||
|
||||
onoe "#{Tty.blue}#{name}#{Tty.reset}: #{e}"
|
||||
$stderr.puts e.backtrace if debug && !e.is_a?(Livecheck::Error)
|
||||
print_resources_info(resource_version_info, verbose: verbose) if check_for_resources
|
||||
nil
|
||||
end
|
||||
end
|
||||
@ -367,16 +407,20 @@ module Homebrew
|
||||
|
||||
puts JSON.pretty_generate(formulae_checked.compact)
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), full_name: T::Boolean).returns(String) }
|
||||
def formula_or_cask_name(formula_or_cask, full_name: false)
|
||||
case formula_or_cask
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask, Resource), full_name: T::Boolean).returns(String) }
|
||||
def package_or_resource_name(package_or_resource, full_name: false)
|
||||
case package_or_resource
|
||||
when Formula
|
||||
formula_name(formula_or_cask, full_name: full_name)
|
||||
formula_name(package_or_resource, full_name: full_name)
|
||||
when Cask::Cask
|
||||
cask_name(formula_or_cask, full_name: full_name)
|
||||
cask_name(package_or_resource, full_name: full_name)
|
||||
when Resource
|
||||
package_or_resource.name
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
end
|
||||
|
||||
@ -396,40 +440,44 @@ module Homebrew
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
status_str: String,
|
||||
messages: T.nilable(T::Array[String]),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def status_hash(formula_or_cask, status_str, messages = nil, full_name: false, verbose: false)
|
||||
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
|
||||
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
|
||||
def status_hash(package_or_resource, status_str, messages = nil, full_name: false, verbose: false)
|
||||
formula = package_or_resource if package_or_resource.is_a?(Formula)
|
||||
cask = package_or_resource if package_or_resource.is_a?(Cask::Cask)
|
||||
resource = package_or_resource if package_or_resource.is_a?(Resource)
|
||||
|
||||
status_hash = {}
|
||||
if formula
|
||||
status_hash[:formula] = formula_name(formula, full_name: full_name)
|
||||
elsif cask
|
||||
status_hash[:cask] = cask_name(formula_or_cask, full_name: full_name)
|
||||
status_hash[:cask] = cask_name(cask, full_name: full_name)
|
||||
elsif resource
|
||||
status_hash[:resource] = resource.name
|
||||
end
|
||||
status_hash[:status] = status_str
|
||||
status_hash[:messages] = messages if messages.is_a?(Array)
|
||||
|
||||
status_hash[:meta] = {
|
||||
livecheckable: formula_or_cask.livecheckable?,
|
||||
livecheckable: package_or_resource.livecheckable?,
|
||||
}
|
||||
status_hash[:meta][:head_only] = true if formula&.head_only?
|
||||
|
||||
status_hash
|
||||
end
|
||||
|
||||
# Formats and prints the livecheck result for a formula.
|
||||
# Formats and prints the livecheck result for a formula/cask/resource.
|
||||
sig { params(info: Hash, verbose: T::Boolean, ambiguous_cask: T::Boolean).void }
|
||||
def print_latest_version(info, verbose:, ambiguous_cask: false)
|
||||
formula_or_cask_s = "#{Tty.blue}#{info[:formula] || info[:cask]}#{Tty.reset}"
|
||||
formula_or_cask_s += " (cask)" if ambiguous_cask
|
||||
formula_or_cask_s += " (guessed)" if !info[:meta][:livecheckable] && verbose
|
||||
def print_latest_version(info, verbose: false, ambiguous_cask: false)
|
||||
package_or_resource_s = info[:resource].present? ? " " : ""
|
||||
package_or_resource_s += "#{Tty.blue}#{info[:formula] || info[:cask] || info[:resource]}#{Tty.reset}"
|
||||
package_or_resource_s += " (cask)" if ambiguous_cask
|
||||
package_or_resource_s += " (guessed)" if verbose && !info[:meta][:livecheckable]
|
||||
|
||||
current_s = if info[:version][:newer_than_upstream]
|
||||
"#{Tty.red}#{info[:version][:current]}#{Tty.reset}"
|
||||
@ -443,47 +491,61 @@ module Homebrew
|
||||
info[:version][:latest]
|
||||
end
|
||||
|
||||
puts "#{formula_or_cask_s}: #{current_s} ==> #{latest_s}"
|
||||
puts "#{package_or_resource_s}: #{current_s} ==> #{latest_s}"
|
||||
end
|
||||
|
||||
# Prints the livecheck result for the resources of a given Formula.
|
||||
sig { params(info: T::Array[Hash], verbose: T::Boolean).void }
|
||||
def print_resources_info(info, verbose: false)
|
||||
info.each do |r_info|
|
||||
if r_info[:status] && r_info[:messages]
|
||||
SkipConditions.print_skip_information(r_info)
|
||||
else
|
||||
print_latest_version(r_info, verbose: verbose)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
livecheck_url: T.any(String, Symbol),
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
).returns(T.nilable(String))
|
||||
}
|
||||
def livecheck_url_to_string(livecheck_url, formula_or_cask)
|
||||
def livecheck_url_to_string(livecheck_url, package_or_resource)
|
||||
case livecheck_url
|
||||
when String
|
||||
livecheck_url
|
||||
when :url
|
||||
formula_or_cask.url&.to_s if formula_or_cask.is_a?(Cask::Cask)
|
||||
package_or_resource.url&.to_s if package_or_resource.is_a?(Cask::Cask) || package_or_resource.is_a?(Resource)
|
||||
when :head, :stable
|
||||
formula_or_cask.send(livecheck_url)&.url if formula_or_cask.is_a?(Formula)
|
||||
package_or_resource.send(livecheck_url)&.url if package_or_resource.is_a?(Formula)
|
||||
when :homepage
|
||||
formula_or_cask.homepage
|
||||
package_or_resource.homepage unless package_or_resource.is_a?(Resource)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an Array containing the formula/cask URLs that can be used by livecheck.
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Array[String]) }
|
||||
def checkable_urls(formula_or_cask)
|
||||
# Returns an Array containing the formula/cask/resource URLs that can be used by livecheck.
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask, Resource)).returns(T::Array[String]) }
|
||||
def checkable_urls(package_or_resource)
|
||||
urls = []
|
||||
|
||||
case formula_or_cask
|
||||
case package_or_resource
|
||||
when Formula
|
||||
if formula_or_cask.stable
|
||||
urls << formula_or_cask.stable.url
|
||||
urls.concat(formula_or_cask.stable.mirrors)
|
||||
if package_or_resource.stable
|
||||
urls << package_or_resource.stable.url
|
||||
urls.concat(package_or_resource.stable.mirrors)
|
||||
end
|
||||
urls << formula_or_cask.head.url if formula_or_cask.head
|
||||
urls << formula_or_cask.homepage if formula_or_cask.homepage
|
||||
urls << package_or_resource.head.url if package_or_resource.head
|
||||
urls << package_or_resource.homepage if package_or_resource.homepage
|
||||
when Cask::Cask
|
||||
urls << formula_or_cask.appcast.to_s if formula_or_cask.appcast
|
||||
urls << formula_or_cask.url.to_s if formula_or_cask.url
|
||||
urls << formula_or_cask.homepage if formula_or_cask.homepage
|
||||
urls << package_or_resource.appcast.to_s if package_or_resource.appcast
|
||||
urls << package_or_resource.url.to_s if package_or_resource.url
|
||||
urls << package_or_resource.homepage if package_or_resource.homepage
|
||||
when Resource
|
||||
urls << package_or_resource.url
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
|
||||
urls.compact.uniq
|
||||
@ -561,7 +623,7 @@ module Homebrew
|
||||
homebrew_curl_root_domains.include?(url_root_domain)
|
||||
end
|
||||
|
||||
# Identifies the latest version of the formula and returns a Hash containing
|
||||
# Identifies the latest version of the formula/cask and returns a Hash containing
|
||||
# the version information. Returns nil if a latest version couldn't be found.
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
sig {
|
||||
@ -713,7 +775,6 @@ module Homebrew
|
||||
version.to_s.include?(rejection)
|
||||
end
|
||||
end
|
||||
|
||||
next if match_version_map.blank?
|
||||
|
||||
if debug
|
||||
@ -770,6 +831,195 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
|
||||
# Identifies the latest version of a resource and returns a Hash containing the
|
||||
# version information. Returns nil if a latest version couldn't be found.
|
||||
sig {
|
||||
params(
|
||||
resource: Resource,
|
||||
json: T::Boolean,
|
||||
debug: T::Boolean,
|
||||
quiet: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def resource_version(
|
||||
resource,
|
||||
json: false,
|
||||
debug: false,
|
||||
quiet: false,
|
||||
verbose: false
|
||||
)
|
||||
has_livecheckable = resource.livecheckable?
|
||||
|
||||
if debug
|
||||
puts "\n\n"
|
||||
puts "Resource: #{resource.name}"
|
||||
puts "Livecheckable?: #{has_livecheckable ? "Yes" : "No"}"
|
||||
end
|
||||
|
||||
resource_version_info = {}
|
||||
|
||||
livecheck = resource.livecheck
|
||||
livecheck_url = livecheck.url
|
||||
livecheck_regex = livecheck.regex
|
||||
livecheck_strategy = livecheck.strategy
|
||||
livecheck_strategy_block = livecheck.strategy_block
|
||||
|
||||
livecheck_url_string = livecheck_url_to_string(livecheck_url, resource)
|
||||
|
||||
urls = [livecheck_url_string] if livecheck_url_string
|
||||
urls ||= checkable_urls(resource)
|
||||
|
||||
checked_urls = []
|
||||
# rubocop:disable Metrics/BlockLength
|
||||
urls.each_with_index do |original_url, i|
|
||||
# Only preprocess the URL when it's appropriate
|
||||
url = if STRATEGY_SYMBOLS_TO_SKIP_PREPROCESS_URL.include?(livecheck_strategy)
|
||||
original_url
|
||||
else
|
||||
preprocess_url(original_url)
|
||||
end
|
||||
next if checked_urls.include?(url)
|
||||
|
||||
strategies = Strategy.from_url(
|
||||
url,
|
||||
livecheck_strategy: livecheck_strategy,
|
||||
url_provided: livecheck_url.present?,
|
||||
regex_provided: livecheck_regex.present?,
|
||||
block_provided: livecheck_strategy_block.present?,
|
||||
)
|
||||
strategy = Strategy.from_symbol(livecheck_strategy) || strategies.first
|
||||
strategy_name = livecheck_strategy_names[strategy]
|
||||
|
||||
if debug
|
||||
puts
|
||||
if livecheck_url.is_a?(Symbol)
|
||||
# This assumes the URL symbol will fit within the available space
|
||||
puts "URL (#{livecheck_url}):".ljust(18, " ") + original_url
|
||||
else
|
||||
puts "URL: #{original_url}"
|
||||
end
|
||||
puts "URL (processed): #{url}" if url != original_url
|
||||
if strategies.present? && verbose
|
||||
puts "Strategies: #{strategies.map { |s| livecheck_strategy_names[s] }.join(", ")}"
|
||||
end
|
||||
puts "Strategy: #{strategy.blank? ? "None" : strategy_name}"
|
||||
puts "Regex: #{livecheck_regex.inspect}" if livecheck_regex.present?
|
||||
end
|
||||
|
||||
if livecheck_strategy.present?
|
||||
if livecheck_url.blank? && strategy.method(:find_versions).parameters.include?([:keyreq, :url])
|
||||
odebug "#{strategy_name} strategy requires a URL"
|
||||
next
|
||||
elsif livecheck_strategy != :page_match && strategies.exclude?(strategy)
|
||||
odebug "#{strategy_name} strategy does not apply to this URL"
|
||||
next
|
||||
end
|
||||
end
|
||||
puts if debug && strategy.blank?
|
||||
next if strategy.blank?
|
||||
|
||||
strategy_data = strategy.find_versions(
|
||||
url: url,
|
||||
regex: livecheck_regex,
|
||||
homebrew_curl: false,
|
||||
&livecheck_strategy_block
|
||||
)
|
||||
match_version_map = strategy_data[:matches]
|
||||
regex = strategy_data[:regex]
|
||||
messages = strategy_data[:messages]
|
||||
checked_urls << url
|
||||
|
||||
if messages.is_a?(Array) && match_version_map.blank?
|
||||
puts messages unless json
|
||||
next if i + 1 < urls.length
|
||||
|
||||
return status_hash(resource, "error", messages, verbose: verbose)
|
||||
end
|
||||
|
||||
if debug
|
||||
if strategy_data[:url].present? && strategy_data[:url] != url
|
||||
puts "URL (strategy): #{strategy_data[:url]}"
|
||||
end
|
||||
puts "URL (final): #{strategy_data[:final_url]}" if strategy_data[:final_url].present?
|
||||
if strategy_data[:regex].present? && strategy_data[:regex] != livecheck_regex
|
||||
puts "Regex (strategy): #{strategy_data[:regex].inspect}"
|
||||
end
|
||||
puts "Cached?: Yes" if strategy_data[:cached] == true
|
||||
end
|
||||
|
||||
match_version_map.delete_if do |_match, version|
|
||||
next true if version.blank?
|
||||
next false if has_livecheckable
|
||||
|
||||
UNSTABLE_VERSION_KEYWORDS.any? do |rejection|
|
||||
version.to_s.include?(rejection)
|
||||
end
|
||||
end
|
||||
next if match_version_map.blank?
|
||||
|
||||
if debug
|
||||
puts
|
||||
puts "Matched Versions:"
|
||||
|
||||
if verbose
|
||||
match_version_map.each do |match, version|
|
||||
puts "#{match} => #{version.inspect}"
|
||||
end
|
||||
else
|
||||
puts match_version_map.values.join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
res_current = resource.version
|
||||
res_latest = Version.new(match_version_map.values.max_by { |v| LivecheckVersion.create(resource, v) })
|
||||
|
||||
return status_hash(resource, "error", ["Unable to get versions"], verbose: verbose) if res_latest.blank?
|
||||
|
||||
is_outdated = res_current < res_latest
|
||||
is_newer_than_upstream = res_current > res_latest
|
||||
|
||||
resource_version_info = {
|
||||
resource: resource.name,
|
||||
version: {
|
||||
current: res_current.to_s,
|
||||
latest: res_latest.to_s,
|
||||
outdated: is_outdated,
|
||||
newer_than_upstream: is_newer_than_upstream,
|
||||
},
|
||||
}
|
||||
|
||||
resource_version_info[:meta] = { livecheckable: has_livecheckable, url: {} }
|
||||
if livecheck_url.is_a?(Symbol) && livecheck_url_string
|
||||
resource_version_info[:meta][:url][:symbol] = livecheck_url
|
||||
end
|
||||
resource_version_info[:meta][:url][:original] = original_url
|
||||
resource_version_info[:meta][:url][:processed] = url if url != original_url
|
||||
if strategy_data[:url].present? && strategy_data[:url] != url
|
||||
resource_version_info[:meta][:url][:strategy] = strategy_data[:url]
|
||||
end
|
||||
resource_version_info[:meta][:url][:final] = strategy_data[:final_url] if strategy_data[:final_url]
|
||||
resource_version_info[:meta][:strategy] = strategy.present? ? strategy_name : nil
|
||||
if strategies.present?
|
||||
resource_version_info[:meta][:strategies] = strategies.map { |s| livecheck_strategy_names[s] }
|
||||
end
|
||||
resource_version_info[:meta][:regex] = regex.inspect if regex.present?
|
||||
resource_version_info[:meta][:cached] = true if strategy_data[:cached] == true
|
||||
|
||||
rescue => e
|
||||
Homebrew.failed = true
|
||||
if json
|
||||
status_hash(resource, "error", [e.to_s], verbose: verbose)
|
||||
elsif !quiet
|
||||
onoe "#{Tty.blue}#{resource.name}#{Tty.reset}: #{e}"
|
||||
$stderr.puts e.backtrace if debug && !e.is_a?(Livecheck::Error)
|
||||
nil
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength
|
||||
resource_version_info
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
end
|
||||
|
||||
@ -11,15 +11,17 @@ module Homebrew
|
||||
|
||||
include Comparable
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), version: Version).returns(LivecheckVersion) }
|
||||
def self.create(formula_or_cask, version)
|
||||
versions = case formula_or_cask
|
||||
when Formula
|
||||
sig {
|
||||
params(package_or_resource: T.any(Formula, Cask::Cask, Resource), version: Version).returns(LivecheckVersion)
|
||||
}
|
||||
def self.create(package_or_resource, version)
|
||||
versions = case package_or_resource
|
||||
when Formula, Resource
|
||||
[version]
|
||||
when Cask::Cask
|
||||
version.to_s.split(/[,:]/).map { |s| Version.new(s) }
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
new(versions)
|
||||
end
|
||||
|
||||
@ -6,7 +6,7 @@ require "livecheck/livecheck"
|
||||
module Homebrew
|
||||
module Livecheck
|
||||
# The `Livecheck::SkipConditions` module primarily contains methods that
|
||||
# check for various formula/cask conditions where a check should be skipped.
|
||||
# check for various formula/cask/resource conditions where a check should be skipped.
|
||||
#
|
||||
# @api private
|
||||
module SkipConditions
|
||||
@ -16,14 +16,14 @@ module Homebrew
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
livecheckable: T::Boolean,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def formula_or_cask_skip(formula_or_cask, livecheckable, full_name: false, verbose: false)
|
||||
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
|
||||
def package_or_resource_skip(package_or_resource, livecheckable, full_name: false, verbose: false)
|
||||
formula = package_or_resource if package_or_resource.is_a?(Formula)
|
||||
|
||||
if (stable_url = formula&.stable&.url)
|
||||
stable_is_gist = stable_url.match?(%r{https?://gist\.github(?:usercontent)?\.com/}i)
|
||||
@ -33,8 +33,8 @@ module Homebrew
|
||||
stable_from_internet_archive = stable_url.match?(%r{https?://web\.archive\.org/}i)
|
||||
end
|
||||
|
||||
skip_message = if formula_or_cask.livecheck.skip_msg.present?
|
||||
formula_or_cask.livecheck.skip_msg
|
||||
skip_message = if package_or_resource.livecheck.skip_msg.present?
|
||||
package_or_resource.livecheck.skip_msg
|
||||
elsif !livecheckable
|
||||
if stable_from_google_code_archive
|
||||
"Stable URL is from Google Code Archive"
|
||||
@ -45,10 +45,10 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
return {} if !formula_or_cask.livecheck.skip? && skip_message.blank?
|
||||
return {} if !package_or_resource.livecheck.skip? && skip_message.blank?
|
||||
|
||||
skip_messages = skip_message ? [skip_message] : nil
|
||||
Livecheck.status_hash(formula_or_cask, "skipped", skip_messages, full_name: full_name, verbose: verbose)
|
||||
Livecheck.status_hash(package_or_resource, "skipped", skip_messages, full_name: full_name, verbose: verbose)
|
||||
end
|
||||
|
||||
sig {
|
||||
@ -157,7 +157,7 @@ module Homebrew
|
||||
|
||||
# Skip conditions for formulae.
|
||||
FORMULA_CHECKS = [
|
||||
:formula_or_cask_skip,
|
||||
:package_or_resource_skip,
|
||||
:formula_head_only,
|
||||
:formula_deprecated,
|
||||
:formula_disabled,
|
||||
@ -166,76 +166,85 @@ module Homebrew
|
||||
|
||||
# Skip conditions for casks.
|
||||
CASK_CHECKS = [
|
||||
:formula_or_cask_skip,
|
||||
:package_or_resource_skip,
|
||||
:cask_discontinued,
|
||||
:cask_version_latest,
|
||||
:cask_url_unversioned,
|
||||
].freeze
|
||||
|
||||
# If a formula/cask should be skipped, we return a hash from
|
||||
# Skip conditions for resources.
|
||||
RESOURCE_CHECKS = [
|
||||
:package_or_resource_skip,
|
||||
].freeze
|
||||
|
||||
# If a formula/cask/resource should be skipped, we return a hash from
|
||||
# `Livecheck#status_hash`, which contains a `status` type and sometimes
|
||||
# error `messages`.
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def skip_information(formula_or_cask, full_name: false, verbose: false)
|
||||
livecheckable = formula_or_cask.livecheckable?
|
||||
def skip_information(package_or_resource, full_name: false, verbose: false)
|
||||
livecheckable = package_or_resource.livecheckable?
|
||||
|
||||
checks = case formula_or_cask
|
||||
checks = case package_or_resource
|
||||
when Formula
|
||||
FORMULA_CHECKS
|
||||
when Cask::Cask
|
||||
CASK_CHECKS
|
||||
when Resource
|
||||
RESOURCE_CHECKS
|
||||
end
|
||||
return {} unless checks
|
||||
|
||||
checks.each do |method_name|
|
||||
skip_hash = send(method_name, formula_or_cask, livecheckable, full_name: full_name, verbose: verbose)
|
||||
skip_hash = send(method_name, package_or_resource, livecheckable, full_name: full_name, verbose: verbose)
|
||||
return skip_hash if skip_hash.present?
|
||||
end
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
# Skip conditions for formulae/casks referenced in a `livecheck` block
|
||||
# Skip conditions for formulae/casks/resources referenced in a `livecheck` block
|
||||
# are treated differently than normal. We only respect certain skip
|
||||
# conditions (returning the related hash) and others are treated as
|
||||
# errors.
|
||||
sig {
|
||||
params(
|
||||
livecheck_formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
original_formula_or_cask_name: String,
|
||||
livecheck_package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
original_package_or_resource_name: String,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(T.nilable(Hash))
|
||||
}
|
||||
def referenced_skip_information(
|
||||
livecheck_formula_or_cask,
|
||||
original_formula_or_cask_name,
|
||||
livecheck_package_or_resource,
|
||||
original_package_or_resource_name,
|
||||
full_name: false,
|
||||
verbose: false
|
||||
)
|
||||
skip_info = SkipConditions.skip_information(
|
||||
livecheck_formula_or_cask,
|
||||
livecheck_package_or_resource,
|
||||
full_name: full_name,
|
||||
verbose: verbose,
|
||||
)
|
||||
return if skip_info.blank?
|
||||
|
||||
referenced_name = Livecheck.formula_or_cask_name(livecheck_formula_or_cask, full_name: full_name)
|
||||
referenced_type = case livecheck_formula_or_cask
|
||||
referenced_name = Livecheck.package_or_resource_name(livecheck_package_or_resource, full_name: full_name)
|
||||
referenced_type = case livecheck_package_or_resource
|
||||
when Formula
|
||||
:formula
|
||||
when Cask::Cask
|
||||
:cask
|
||||
when Resource
|
||||
:resource
|
||||
end
|
||||
|
||||
if skip_info[:status] != "error" &&
|
||||
!(skip_info[:status] == "skipped" && livecheck_formula_or_cask.livecheck.skip?)
|
||||
!(skip_info[:status] == "skipped" && livecheck_package_or_resource.livecheck.skip?)
|
||||
error_msg_end = if skip_info[:status] == "skipped"
|
||||
"automatically skipped"
|
||||
else
|
||||
@ -245,7 +254,7 @@ module Homebrew
|
||||
raise "Referenced #{referenced_type} (#{referenced_name}) is #{error_msg_end}"
|
||||
end
|
||||
|
||||
skip_info[referenced_type] = original_formula_or_cask_name
|
||||
skip_info[referenced_type] = original_package_or_resource_name
|
||||
skip_info
|
||||
end
|
||||
|
||||
@ -258,6 +267,8 @@ module Homebrew
|
||||
skip_hash[:formula]
|
||||
elsif skip_hash[:cask].is_a?(String)
|
||||
skip_hash[:cask]
|
||||
elsif skip_hash[:resource].is_a?(String)
|
||||
" #{skip_hash[:resource]}"
|
||||
end
|
||||
return unless name
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ describe Homebrew::Livecheck do
|
||||
let(:homepage_url) { "https://brew.sh" }
|
||||
let(:livecheck_url) { "https://formulae.brew.sh/api/formula/ruby.json" }
|
||||
let(:stable_url) { "https://brew.sh/test-0.0.1.tgz" }
|
||||
let(:resource_url) { "https://brew.sh/foo-1.0.tar.gz" }
|
||||
|
||||
let(:f) do
|
||||
formula("test") do
|
||||
@ -23,8 +24,20 @@ describe Homebrew::Livecheck do
|
||||
url "https://formulae.brew.sh/api/formula/ruby.json"
|
||||
regex(/"stable":"(\d+(?:\.\d+)+)"/i)
|
||||
end
|
||||
|
||||
resource "foo" do
|
||||
url "https://brew.sh/foo-1.0.tar.gz"
|
||||
sha256 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
livecheck do
|
||||
url "https://brew.sh/test/releases"
|
||||
regex(/foo[._-]v?(\d+(?:\.\d+)+)\.t/i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:r) { f.resources.first }
|
||||
|
||||
let(:c) do
|
||||
Cask::CaskLoader.load(+<<-RUBY)
|
||||
@ -44,15 +57,6 @@ describe Homebrew::Livecheck do
|
||||
RUBY
|
||||
end
|
||||
|
||||
let(:f_duplicate_urls) do
|
||||
formula("test_duplicate_urls") do
|
||||
desc "Test formula with a duplicate URL"
|
||||
homepage "https://github.com/Homebrew/brew.git"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
end
|
||||
end
|
||||
|
||||
describe "::resolve_livecheck_reference" do
|
||||
context "when a formula/cask has a livecheck block without formula/cask methods" do
|
||||
it "returns [nil, []]" do
|
||||
@ -83,7 +87,7 @@ describe Homebrew::Livecheck do
|
||||
end
|
||||
|
||||
describe "::status_hash" do
|
||||
it "returns a hash containing the livecheck status" do
|
||||
it "returns a hash containing the livecheck status for a formula" do
|
||||
expect(livecheck.status_hash(f, "error", ["Unable to get versions"]))
|
||||
.to eq({
|
||||
formula: "test",
|
||||
@ -94,6 +98,18 @@ describe Homebrew::Livecheck do
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
it "returns a hash containing the livecheck status for a resource" do
|
||||
expect(livecheck.status_hash(r, "error", ["Unable to get versions"]))
|
||||
.to eq({
|
||||
resource: "foo",
|
||||
status: "error",
|
||||
messages: ["Unable to get versions"],
|
||||
meta: {
|
||||
livecheckable: true,
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "::livecheck_url_to_string" do
|
||||
@ -101,14 +117,27 @@ describe Homebrew::Livecheck do
|
||||
homepage_url_s = homepage_url
|
||||
stable_url_s = stable_url
|
||||
head_url_s = head_url
|
||||
resource_url_s = resource_url
|
||||
|
||||
formula("test_livecheck_url") do
|
||||
desc "Test Livecheck URL formula"
|
||||
homepage homepage_url_s
|
||||
url stable_url_s
|
||||
head head_url_s
|
||||
|
||||
resource "foo" do
|
||||
url resource_url_s
|
||||
sha256 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
livecheck do
|
||||
url "https://brew.sh/test/releases"
|
||||
regex(/foo[._-]v?(\d+(?:\.\d+)+)\.t/i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:r_livecheck_url) { f_livecheck_url.resources.first }
|
||||
|
||||
let(:c_livecheck_url) do
|
||||
Cask::CaskLoader.load(+<<-RUBY)
|
||||
@ -123,30 +152,48 @@ describe Homebrew::Livecheck do
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "returns a URL string when given a livecheck_url string" do
|
||||
it "returns a URL string when given a livecheck_url string for a formula" do
|
||||
expect(livecheck.livecheck_url_to_string(livecheck_url, f_livecheck_url)).to eq(livecheck_url)
|
||||
end
|
||||
|
||||
it "returns a URL string when given a livecheck_url string for a resource" do
|
||||
expect(livecheck.livecheck_url_to_string(livecheck_url, r_livecheck_url)).to eq(livecheck_url)
|
||||
end
|
||||
|
||||
it "returns a URL symbol when given a valid livecheck_url symbol" do
|
||||
expect(livecheck.livecheck_url_to_string(:head, f_livecheck_url)).to eq(head_url)
|
||||
expect(livecheck.livecheck_url_to_string(:homepage, f_livecheck_url)).to eq(homepage_url)
|
||||
expect(livecheck.livecheck_url_to_string(:homepage, c_livecheck_url)).to eq(homepage_url)
|
||||
expect(livecheck.livecheck_url_to_string(:stable, f_livecheck_url)).to eq(stable_url)
|
||||
expect(livecheck.livecheck_url_to_string(:url, c_livecheck_url)).to eq(cask_url)
|
||||
expect(livecheck.livecheck_url_to_string(:url, r_livecheck_url)).to eq(resource_url)
|
||||
end
|
||||
|
||||
it "returns nil when not given a string or valid symbol" do
|
||||
expect(livecheck.livecheck_url_to_string(nil, f_livecheck_url)).to be_nil
|
||||
expect(livecheck.livecheck_url_to_string(nil, c_livecheck_url)).to be_nil
|
||||
expect(livecheck.livecheck_url_to_string(nil, r_livecheck_url)).to be_nil
|
||||
expect(livecheck.livecheck_url_to_string(:invalid_symbol, f_livecheck_url)).to be_nil
|
||||
expect(livecheck.livecheck_url_to_string(:invalid_symbol, c_livecheck_url)).to be_nil
|
||||
expect(livecheck.livecheck_url_to_string(:invalid_symbol, r_livecheck_url)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "::checkable_urls" do
|
||||
let(:resource_url) { "https://brew.sh/foo-1.0.tar.gz" }
|
||||
let(:f_duplicate_urls) do
|
||||
formula("test_duplicate_urls") do
|
||||
desc "Test formula with a duplicate URL"
|
||||
homepage "https://github.com/Homebrew/brew.git"
|
||||
url "https://brew.sh/test-0.0.1.tgz"
|
||||
head "https://github.com/Homebrew/brew.git"
|
||||
end
|
||||
end
|
||||
|
||||
it "returns the list of URLs to check" do
|
||||
expect(livecheck.checkable_urls(f)).to eq([stable_url, head_url, homepage_url])
|
||||
expect(livecheck.checkable_urls(c)).to eq([cask_url, homepage_url])
|
||||
expect(livecheck.checkable_urls(r)).to eq([resource_url])
|
||||
expect(livecheck.checkable_urls(f_duplicate_urls)).to eq([stable_url, head_url])
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,6 +6,7 @@ require "livecheck/livecheck_version"
|
||||
describe Homebrew::Livecheck::LivecheckVersion do
|
||||
let(:formula) { instance_double(Formula) }
|
||||
let(:cask) { instance_double(Cask::Cask) }
|
||||
let(:resource) { instance_double(Resource) }
|
||||
|
||||
before do
|
||||
# Case statements use #=== for case equality purposes
|
||||
@ -13,6 +14,8 @@ describe Homebrew::Livecheck::LivecheckVersion do
|
||||
allow(Formula).to receive(:===).with(formula).and_return(true)
|
||||
allow(Cask::Cask).to receive(:===).and_call_original
|
||||
allow(Cask::Cask).to receive(:===).with(cask).and_return(true)
|
||||
allow(Resource).to receive(:===).and_call_original
|
||||
allow(Resource).to receive(:===).with(resource).and_return(true)
|
||||
end
|
||||
|
||||
specify "::create" do
|
||||
@ -28,5 +31,11 @@ describe Homebrew::Livecheck::LivecheckVersion do
|
||||
.to eq ["1.0", "100", "1426778671"]
|
||||
expect(described_class.create(cask, Version.new("0.17.0,20210111183933,226")).versions)
|
||||
.to eq ["0.17.0", "20210111183933", "226"]
|
||||
|
||||
expect(described_class.create(resource, Version.new("1.1.6")).versions).to eq ["1.1.6"]
|
||||
expect(described_class.create(resource, Version.new("2.19.0,1.8.0")).versions).to eq ["2.19.0,1.8.0"]
|
||||
expect(described_class.create(resource, Version.new("1.0,100:1426778671")).versions).to eq ["1.0,100:1426778671"]
|
||||
expect(described_class.create(resource, Version.new("0.17.0,20210111183933,226")).versions)
|
||||
.to eq ["0.17.0,20210111183933,226"]
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user