Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

livecheck: enable Sorbet type checking #10220

Merged
merged 2 commits into from Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 34 additions & 0 deletions Library/Homebrew/cask/cask.rbi
Expand Up @@ -2,10 +2,44 @@

module Cask
class Cask
def appcast; end

def artifacts; end

def auto_updates; end

def caveats; end

def conflicts_with; end

def container; end

def desc; end

def depends_on; end

def homepage; end

def language; end

def languages; end

def name; end

def sha256; end

def staged_path; end

def url; end

def version; end

def appdir; end

def discontinued?; end

def livecheck; end

def livecheckable?; end
end
end
106 changes: 73 additions & 33 deletions Library/Homebrew/livecheck/livecheck.rb
@@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true

require "livecheck/error"
Expand Down Expand Up @@ -45,6 +45,7 @@ module Livecheck
rc
].freeze

sig { returns(T::Hash[Class, String]) }
def livecheck_strategy_names
return @livecheck_strategy_names if defined?(@livecheck_strategy_names)

Expand All @@ -59,7 +60,17 @@ def livecheck_strategy_names

# Executes the livecheck logic for each formula/cask in the
# `formulae_and_casks_to_check` array and prints the results.
# @return [nil]
sig do
params(
formulae_and_casks_to_check: T::Enumerable[T.any(Formula, Cask::Cask)],
full_name: T::Boolean,
json: T::Boolean,
newer_only: T::Boolean,
debug: T::Boolean,
quiet: T::Boolean,
verbose: T::Boolean,
).void
end
def run_checks(
formulae_and_casks_to_check,
full_name: false, json: false, newer_only: false, debug: false, quiet: false, verbose: false
Expand All @@ -81,14 +92,10 @@ def run_checks(
Dir["#{tap_strategy_path}/*.rb"].sort.each(&method(:require)) if Dir.exist?(tap_strategy_path)
end

has_a_newer_upstream_version = false
has_a_newer_upstream_version = T.let(false, T::Boolean)

if json && !quiet && $stderr.tty?
formulae_and_casks_total = if formulae_and_casks_to_check == Formula
formulae_and_casks_to_check.count
else
formulae_and_casks_to_check.length
end
formulae_and_casks_total = formulae_and_casks_to_check.count

Tty.with($stderr) do |stderr|
stderr.puts Formatter.headline("Running checks", color: :blue)
Expand Down Expand Up @@ -245,6 +252,15 @@ def formula_name(formula, full_name: false)
full_name ? formula.full_name : formula.name
end

sig do
params(
formula_or_cask: T.any(Formula, Cask::Cask),
status_str: String,
messages: T.nilable(T::Array[String]),
full_name: T::Boolean,
verbose: T::Boolean,
).returns(Hash)
end
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)
Expand All @@ -268,58 +284,72 @@ def status_hash(formula_or_cask, status_str, messages = nil, full_name: false, v

# If a formula has to be skipped, it prints or returns a Hash contaning the reason
# for doing so; returns false otherwise.
# @return [Hash, nil, Boolean]
sig do
params(
formula_or_cask: T.any(Formula, Cask::Cask),
json: T::Boolean,
full_name: T::Boolean,
quiet: T::Boolean,
verbose: T::Boolean,
).returns(T.nilable(T.any(Hash, T::Boolean)))
end
def skip_conditions(formula_or_cask, json: false, full_name: false, quiet: 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)
case formula_or_cask
when Formula
formula = formula_or_cask
name = formula_name(formula, full_name: full_name)
when Cask::Cask
cask = formula_or_cask
name = cask_name(cask, full_name: full_name)
end

if formula&.deprecated? && !formula.livecheckable?
return status_hash(formula, "deprecated", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{formula_name(formula, full_name: full_name)}#{Tty.reset} : deprecated" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : deprecated" unless quiet
return
end

if cask&.discontinued? && !cask.livecheckable?
return status_hash(cask, "discontinued", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{cask_name(cask, full_name: full_name)}#{Tty.reset} : discontinued" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : discontinued" unless quiet
return
end

if formula&.disabled? && !formula.livecheckable?
return status_hash(formula, "disabled", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{formula_name(formula, full_name: full_name)}#{Tty.reset} : disabled" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : disabled" unless quiet
return
end

if formula&.versioned_formula? && !formula.livecheckable?
return status_hash(formula, "versioned", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{formula_name(formula, full_name: full_name)}#{Tty.reset} : versioned" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : versioned" unless quiet
return
end

if cask&.version&.latest? && !cask.livecheckable?
if cask.present? && cask.version&.latest? && !cask.livecheckable?
return status_hash(cask, "latest", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{cask_name(cask, full_name: full_name)}#{Tty.reset} : latest" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : latest" unless quiet
return
end

if cask&.url&.unversioned? && !cask.livecheckable?
if cask.present? && cask.url&.unversioned? && !cask.livecheckable?
Comment on lines -311 to +341
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return status_hash(cask, "unversioned", full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{cask_name(cask, full_name: full_name)}#{Tty.reset} : unversioned" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : unversioned" unless quiet
return
end

if formula&.head_only? && !formula.any_version_installed?
head_only_msg = "HEAD only formula must be installed to be livecheckable"
return status_hash(formula, "error", [head_only_msg], full_name: full_name, verbose: verbose) if json

puts "#{Tty.red}#{formula_name(formula, full_name: full_name)}#{Tty.reset} : #{head_only_msg}" unless quiet
puts "#{Tty.red}#{name}#{Tty.reset} : #{head_only_msg}" unless quiet
return
end

Expand All @@ -337,18 +367,15 @@ def skip_conditions(formula_or_cask, json: false, full_name: false, quiet: false
return status_hash(formula_or_cask, "skipped", skip_messages, full_name: full_name, verbose: verbose)
end

unless quiet
puts "#{Tty.red}#{formula_or_cask_name(formula_or_cask, full_name: full_name)}#{Tty.reset} : skipped" \
"#{" - #{skip_message}" if skip_message}"
end
puts "#{Tty.red}#{name}#{Tty.reset} : skipped#{" - #{skip_message}" if skip_message}" unless quiet
return
end

false
end

# Formats and prints the livecheck result for a formula.
# @return [nil]
sig { params(info: Hash, verbose: T::Boolean).void }
def print_latest_version(info, verbose:)
formula_or_cask_s = "#{Tty.blue}#{info[:formula] || info[:cask]}#{Tty.reset}"
formula_or_cask_s += " (guessed)" if !info[:meta][:livecheckable] && verbose
Expand All @@ -369,7 +396,7 @@ def print_latest_version(info, verbose:)
end

# Returns an Array containing the formula/cask URLs that can be used by livecheck.
# @return [Array]
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Array[String]) }
def checkable_urls(formula_or_cask)
urls = []

Expand All @@ -391,16 +418,20 @@ def checkable_urls(formula_or_cask)
end

# Preprocesses and returns the URL used by livecheck.
# @return [String]
sig { params(url: String).returns(String) }
def preprocess_url(url)
begin
uri = URI.parse url
rescue URI::InvalidURIError
return url
end

host = uri.host == "github.s3.amazonaws.com" ? "github.com" : uri.host
path = uri.path.delete_prefix("/").delete_suffix(".git")
host = uri.host
path = uri.path
return url if host.nil? || path.nil?

host = "github.com" if host == "github.s3.amazonaws.com"
path = path.delete_prefix("/").delete_suffix(".git")
scheme = uri.scheme

if host.end_with?("github.com")
Expand Down Expand Up @@ -430,7 +461,15 @@ def preprocess_url(url)

# Identifies the latest version of the formula and returns a Hash containing
# the version information. Returns nil if a latest version couldn't be found.
# @return [Hash, nil]
sig do
params(
formula_or_cask: T.any(Formula, Cask::Cask),
json: T::Boolean,
full_name: T::Boolean,
verbose: T::Boolean,
debug: T::Boolean,
).returns(T.nilable(Hash))
end
def latest_version(formula_or_cask, json: false, full_name: false, verbose: false, debug: false)
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
Expand Down Expand Up @@ -514,12 +553,13 @@ def latest_version(formula_or_cask, json: false, full_name: false, verbose: fals
strategy_data = strategy.find_versions(url, livecheck_regex, &livecheck.strategy_block)
match_version_map = strategy_data[:matches]
regex = strategy_data[:regex]
messages = strategy_data[:messages]

if strategy_data[:messages].is_a?(Array) && match_version_map.blank?
puts strategy_data[:messages] unless json
if messages.is_a?(Array) && match_version_map.blank?
puts messages unless json
next if i + 1 < urls.length

return status_hash(formula, "error", strategy_data[:messages], full_name: full_name, verbose: verbose)
return status_hash(formula_or_cask, "error", messages, full_name: full_name, verbose: verbose)
end

if debug
Expand Down
7 changes: 7 additions & 0 deletions Library/Homebrew/livecheck/livecheck.rbi
@@ -0,0 +1,7 @@
# typed: strict

module Homebrew
module Livecheck
include ::Kernel
end
end