mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Merge pull request #20130 from Homebrew/sorbet_cmd
cmd: set `typed: strict`
This commit is contained in:
commit
2916610699
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
@ -6,6 +6,7 @@ require "formula"
|
||||
require "fetch"
|
||||
require "cask/download"
|
||||
require "retryable_download"
|
||||
require "download_queue"
|
||||
|
||||
module Homebrew
|
||||
module Cmd
|
||||
@ -69,15 +70,16 @@ module Homebrew
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def concurrency
|
||||
@concurrency ||= args.concurrency&.to_i || 1
|
||||
@concurrency ||= T.let(args.concurrency&.to_i || 1, T.nilable(Integer))
|
||||
end
|
||||
|
||||
sig { returns(DownloadQueue) }
|
||||
def download_queue
|
||||
@download_queue ||= begin
|
||||
require "download_queue"
|
||||
@download_queue ||= T.let(begin
|
||||
DownloadQueue.new(concurrency)
|
||||
end
|
||||
end, T.nilable(DownloadQueue))
|
||||
end
|
||||
|
||||
class Spinner
|
||||
@ -96,8 +98,8 @@ module Homebrew
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
@start = Time.now
|
||||
@i = 0
|
||||
@start = T.let(Time.now, Time)
|
||||
@i = T.let(0, Integer)
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
@ -136,7 +138,7 @@ module Homebrew
|
||||
bucket.each do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
formula = T.cast(formula_or_cask, Formula)
|
||||
formula = formula_or_cask
|
||||
ref = formula.loaded_from_api? ? formula.full_name : formula.path
|
||||
|
||||
os_arch_combinations.each do |os, arch|
|
||||
@ -189,7 +191,9 @@ module Homebrew
|
||||
|
||||
next if fetched_bottle
|
||||
|
||||
fetch_downloadable(formula.resource)
|
||||
if (resource = formula.resource)
|
||||
fetch_downloadable(resource)
|
||||
end
|
||||
|
||||
formula.resources.each do |r|
|
||||
fetch_downloadable(r)
|
||||
@ -231,7 +235,7 @@ module Homebrew
|
||||
end
|
||||
else
|
||||
spinner = Spinner.new
|
||||
remaining_downloads = downloads.dup
|
||||
remaining_downloads = downloads.dup.to_a
|
||||
previous_pending_line_count = 0
|
||||
|
||||
begin
|
||||
@ -332,10 +336,13 @@ module Homebrew
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T::Hash[T.any(Resource, Bottle, Cask::Download), Concurrent::Promises::Future]) }
|
||||
def downloads
|
||||
@downloads ||= {}
|
||||
@downloads ||= T.let({}, T.nilable(T::Hash[T.any(Resource, Bottle, Cask::Download),
|
||||
Concurrent::Promises::Future]))
|
||||
end
|
||||
|
||||
sig { params(downloadable: T.any(Resource, Bottle, Cask::Download)).void }
|
||||
def fetch_downloadable(downloadable)
|
||||
downloads[downloadable] ||= begin
|
||||
tries = args.retry? ? {} : { tries: 1 }
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
@ -18,7 +18,7 @@ module Homebrew
|
||||
class Info < AbstractCommand
|
||||
VALID_DAYS = %w[30 90 365].freeze
|
||||
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
|
||||
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze
|
||||
VALID_CATEGORIES = T.let((VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze, T::Array[String])
|
||||
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
@ -96,14 +96,17 @@ module Homebrew
|
||||
end
|
||||
|
||||
print_analytics
|
||||
elsif args.json
|
||||
elsif (json = args.json)
|
||||
all = args.eval_all?
|
||||
|
||||
print_json(all)
|
||||
print_json(json, all)
|
||||
elsif args.github?
|
||||
raise FormulaOrCaskUnspecifiedError if args.no_named?
|
||||
|
||||
exec_browser(*args.named.to_formulae_and_casks.map { |f| github_info(f) })
|
||||
exec_browser(*args.named.to_formulae_and_casks.map do |formula_keg_or_cask|
|
||||
formula_or_cask = T.cast(formula_keg_or_cask, T.any(Formula, Cask::Cask))
|
||||
github_info(formula_or_cask)
|
||||
end)
|
||||
elsif args.no_named?
|
||||
print_statistics
|
||||
else
|
||||
@ -111,6 +114,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(remote: String, path: String).returns(String) }
|
||||
def github_remote_path(remote, path)
|
||||
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
|
||||
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}"
|
||||
@ -175,6 +179,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(version: T.any(T::Boolean, String)).returns(Symbol) }
|
||||
def json_version(version)
|
||||
version_hash = {
|
||||
true => :default,
|
||||
@ -187,11 +192,11 @@ module Homebrew
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(all: T::Boolean).void }
|
||||
def print_json(all)
|
||||
sig { params(json: T.any(T::Boolean, String), all: T::Boolean).void }
|
||||
def print_json(json, all)
|
||||
raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named?
|
||||
|
||||
json = case json_version(args.json)
|
||||
json = case json_version(json)
|
||||
when :v1, :default
|
||||
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
|
||||
|
||||
@ -240,25 +245,31 @@ module Homebrew
|
||||
puts JSON.pretty_generate(json)
|
||||
end
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(String) }
|
||||
def github_info(formula_or_cask)
|
||||
return formula_or_cask.path if formula_or_cask.tap.blank? || formula_or_cask.tap.remote.blank?
|
||||
|
||||
path = case formula_or_cask
|
||||
when Formula
|
||||
formula = formula_or_cask
|
||||
formula.path.relative_path_from(T.must(formula.tap).path)
|
||||
tap = formula.tap
|
||||
return formula.path.to_s if tap.blank? || tap.remote.blank?
|
||||
|
||||
formula.path.relative_path_from(tap.path)
|
||||
when Cask::Cask
|
||||
cask = formula_or_cask
|
||||
tap = cask.tap
|
||||
return cask.sourcefile_path.to_s if tap.blank? || tap.remote.blank?
|
||||
|
||||
if cask.sourcefile_path.blank? || cask.sourcefile_path.extname != ".rb"
|
||||
return "#{cask.tap.default_remote}/blob/HEAD/#{cask.tap.relative_cask_path(cask.token)}"
|
||||
return "#{tap.default_remote}/blob/HEAD/#{tap.relative_cask_path(cask.token)}"
|
||||
end
|
||||
|
||||
cask.sourcefile_path.relative_path_from(cask.tap.path)
|
||||
cask.sourcefile_path.relative_path_from(tap.path)
|
||||
end
|
||||
|
||||
github_remote_path(formula_or_cask.tap.remote, path)
|
||||
github_remote_path(tap.remote, path)
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
def info_formula(formula)
|
||||
specs = []
|
||||
|
||||
@ -356,6 +367,7 @@ module Homebrew
|
||||
Utils::Analytics.formula_output(formula, args:)
|
||||
end
|
||||
|
||||
sig { params(dependencies: T::Array[Dependency]).returns(String) }
|
||||
def decorate_dependencies(dependencies)
|
||||
deps_status = dependencies.map do |dep|
|
||||
if dep.satisfied?([])
|
||||
@ -367,6 +379,7 @@ module Homebrew
|
||||
deps_status.join(", ")
|
||||
end
|
||||
|
||||
sig { params(requirements: T::Array[Requirement]).returns(String) }
|
||||
def decorate_requirements(requirements)
|
||||
req_status = requirements.map do |req|
|
||||
req_s = req.display_s
|
||||
@ -375,12 +388,14 @@ module Homebrew
|
||||
req_status.join(", ")
|
||||
end
|
||||
|
||||
sig { params(dep: Dependency).returns(String) }
|
||||
def dep_display_s(dep)
|
||||
return dep.name if dep.option_tags.empty?
|
||||
|
||||
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).void }
|
||||
def info_cask(cask)
|
||||
require "cask/info"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
@ -39,13 +39,15 @@ module Homebrew
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def auto_update_header
|
||||
@auto_update_header ||= begin
|
||||
@auto_update_header ||= T.let(begin
|
||||
ohai "Auto-updated Homebrew!" if args.auto_update?
|
||||
true
|
||||
end
|
||||
end, T.nilable(T::Boolean))
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def output_update_report
|
||||
# Run `brew update` (again) if we've got a linuxbrew-core CoreTap
|
||||
if CoreTap.instance.installed? && CoreTap.instance.linuxbrew_core? &&
|
||||
@ -293,14 +295,17 @@ module Homebrew
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def no_changes_message
|
||||
"No changes to formulae or casks."
|
||||
end
|
||||
|
||||
sig { params(revision: String).returns(String) }
|
||||
def shorten_revision(revision)
|
||||
Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "rev-parse", "--short", revision).chomp
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def tap_or_untap_core_taps_if_necessary
|
||||
return if ENV["HOMEBREW_UPDATE_TEST"]
|
||||
|
||||
@ -340,6 +345,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(repository: Pathname).void }
|
||||
def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
|
||||
command = "brew update"
|
||||
Utils::Link.link_completions(repository, command)
|
||||
@ -352,10 +358,12 @@ module Homebrew
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def migrate_gcc_dependents_if_needed
|
||||
# do nothing
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def analytics_message
|
||||
return if Utils::Analytics.messages_displayed?
|
||||
return if Utils::Analytics.no_message_output?
|
||||
@ -385,6 +393,7 @@ module Homebrew
|
||||
Utils::Analytics.messages_displayed! if $stdout.tty?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def donation_message
|
||||
return if Settings.read("donationmessage") == "true"
|
||||
|
||||
@ -395,6 +404,7 @@ module Homebrew
|
||||
Settings.write "donationmessage", true if $stdout.tty?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def install_from_api_message
|
||||
return if Settings.read("installfromapimessage") == "true"
|
||||
|
||||
@ -419,30 +429,38 @@ require "extend/os/cmd/update-report"
|
||||
|
||||
class Reporter
|
||||
class ReporterRevisionUnsetError < RuntimeError
|
||||
sig { params(var_name: String).void }
|
||||
def initialize(var_name)
|
||||
super "#{var_name} is unset!"
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(tap: Tap, api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
|
||||
api_dir_prefix: T.nilable(Pathname)).void
|
||||
}
|
||||
def initialize(tap, api_names_txt: nil, api_names_before_txt: nil, api_dir_prefix: nil)
|
||||
@tap = tap
|
||||
|
||||
# This is slightly involved/weird but all the #report logic is shared so it's worth it.
|
||||
if installed_from_api?(api_names_txt, api_names_before_txt, api_dir_prefix)
|
||||
@api_names_txt = api_names_txt
|
||||
@api_names_before_txt = api_names_before_txt
|
||||
@api_dir_prefix = api_dir_prefix
|
||||
@api_names_txt = T.let(api_names_txt, T.nilable(Pathname))
|
||||
@api_names_before_txt = T.let(api_names_before_txt, T.nilable(Pathname))
|
||||
@api_dir_prefix = T.let(api_dir_prefix, T.nilable(Pathname))
|
||||
else
|
||||
initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{tap.repository_var_suffix}"
|
||||
@initial_revision = ENV[initial_revision_var].to_s
|
||||
@initial_revision = T.let(ENV[initial_revision_var].to_s, String)
|
||||
raise ReporterRevisionUnsetError, initial_revision_var if @initial_revision.empty?
|
||||
|
||||
current_revision_var = "HOMEBREW_UPDATE_AFTER#{tap.repository_var_suffix}"
|
||||
@current_revision = ENV[current_revision_var].to_s
|
||||
@current_revision = T.let(ENV[current_revision_var].to_s, String)
|
||||
raise ReporterRevisionUnsetError, current_revision_var if @current_revision.empty?
|
||||
end
|
||||
|
||||
@report = T.let(nil, T.nilable(T::Hash[Symbol, T::Array[String]]))
|
||||
end
|
||||
|
||||
sig { params(auto_update: T::Boolean).returns(T::Hash[Symbol, T::Array[String]]) }
|
||||
def report(auto_update: false)
|
||||
return @report if @report
|
||||
|
||||
@ -483,9 +501,9 @@ class Reporter
|
||||
case status
|
||||
when "A", "D"
|
||||
full_name = tap.formula_file_to_name(src)
|
||||
name = full_name.split("/").last
|
||||
name = T.must(full_name.split("/").last)
|
||||
new_tap = tap.tap_migrations[name]
|
||||
@report[status.to_sym] << full_name unless new_tap
|
||||
@report[T.must(status).to_sym] << full_name unless new_tap
|
||||
when "M"
|
||||
name = tap.formula_file_to_name(src)
|
||||
|
||||
@ -585,6 +603,7 @@ class Reporter
|
||||
@report
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def updated?
|
||||
if installed_from_api?
|
||||
diff.present?
|
||||
@ -593,9 +612,10 @@ class Reporter
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def migrate_tap_migration
|
||||
(report[:D] + report[:DC]).each do |full_name|
|
||||
name = full_name.split("/").last
|
||||
(Array(report[:D]) + Array(report[:DC])).each do |full_name|
|
||||
name = T.must(full_name.split("/").last)
|
||||
new_tap_name = tap.tap_migrations[name]
|
||||
next if new_tap_name.nil? # skip if not in tap_migrations list.
|
||||
|
||||
@ -610,7 +630,7 @@ class Reporter
|
||||
end
|
||||
|
||||
# This means it is a cask
|
||||
if report[:DC].include? full_name
|
||||
if Array(report[:DC]).include? full_name
|
||||
next unless (HOMEBREW_PREFIX/"Caskroom"/new_name).exist?
|
||||
|
||||
new_tap = Tap.fetch(new_tap_name)
|
||||
@ -676,12 +696,14 @@ class Reporter
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def migrate_cask_rename
|
||||
Cask::Caskroom.casks.each do |cask|
|
||||
Cask::Migrator.migrate_if_needed(cask)
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(force: T::Boolean, verbose: T::Boolean).void }
|
||||
def migrate_formula_rename(force:, verbose:)
|
||||
Formula.installed.each do |formula|
|
||||
next unless Migrator.needs_migration?(formula)
|
||||
@ -705,14 +727,36 @@ class Reporter
|
||||
|
||||
private
|
||||
|
||||
attr_reader :tap, :initial_revision, :current_revision, :api_names_txt, :api_names_before_txt, :api_dir_prefix
|
||||
sig { returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :initial_revision
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :current_revision
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_before_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_dir_prefix
|
||||
|
||||
sig {
|
||||
params(api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
|
||||
api_dir_prefix: T.nilable(Pathname)).returns(T::Boolean)
|
||||
}
|
||||
def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt,
|
||||
api_dir_prefix = @api_dir_prefix)
|
||||
!api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil?
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def diff
|
||||
@diff ||= T.let(nil, T.nilable(String))
|
||||
@diff ||= if installed_from_api?
|
||||
# Hack `git diff` output with regexes to look like `git diff-tree` output.
|
||||
# Yes, I know this is a bit filthy but it saves duplicating the #report logic.
|
||||
@ -720,12 +764,14 @@ class Reporter
|
||||
header_regex = /^(---|\+\+\+) /
|
||||
add_delete_characters = ["+", "-"].freeze
|
||||
|
||||
api_dir_prefix_basename = T.must(api_dir_prefix).basename
|
||||
|
||||
diff_output.lines.filter_map do |line|
|
||||
next if line.match?(header_regex)
|
||||
next unless add_delete_characters.include?(line[0])
|
||||
|
||||
line.sub(/^\+/, "A #{api_dir_prefix.basename}/")
|
||||
.sub(/^-/, "D #{api_dir_prefix.basename}/")
|
||||
line.sub(/^\+/, "A #{api_dir_prefix_basename}/")
|
||||
.sub(/^-/, "D #{api_dir_prefix_basename}/")
|
||||
.sub(/$/, ".rb")
|
||||
.chomp
|
||||
end.join("\n")
|
||||
@ -739,28 +785,33 @@ class Reporter
|
||||
end
|
||||
|
||||
class ReporterHub
|
||||
sig { returns(T::Array[Reporter]) }
|
||||
attr_reader :reporters
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
@hash = {}
|
||||
@reporters = []
|
||||
@hash = T.let({}, T::Hash[Symbol, T::Array[String]])
|
||||
@reporters = T.let([], T::Array[Reporter])
|
||||
end
|
||||
|
||||
sig { params(key: Symbol).returns(T::Array[String]) }
|
||||
def select_formula_or_cask(key)
|
||||
@hash.fetch(key, [])
|
||||
end
|
||||
|
||||
sig { params(reporter: Reporter, auto_update: T::Boolean).void }
|
||||
def add(reporter, auto_update: false)
|
||||
@reporters << reporter
|
||||
report = reporter.report(auto_update:).delete_if { |_k, v| v.empty? }
|
||||
@hash.update(report) { |_key, oldval, newval| oldval.concat(newval) }
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def empty?
|
||||
@hash.empty?
|
||||
end
|
||||
|
||||
sig { params(auto_update: T::Boolean).void }
|
||||
def dump(auto_update: false)
|
||||
unless Homebrew::EnvConfig.no_update_report_new?
|
||||
dump_new_formula_report
|
||||
@ -815,12 +866,14 @@ class ReporterHub
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def dump_new_formula_report
|
||||
formulae = select_formula_or_cask(:A).sort.reject { |name| installed?(name) }
|
||||
|
||||
output_dump_formula_or_cask_report "New Formulae", formulae
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def dump_new_cask_report
|
||||
return if Homebrew::SimulateSystem.simulating_or_running_on_linux?
|
||||
|
||||
@ -831,6 +884,7 @@ class ReporterHub
|
||||
output_dump_formula_or_cask_report "New Casks", casks
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def dump_deleted_formula_report
|
||||
formulae = select_formula_or_cask(:D).sort.filter_map do |name|
|
||||
pretty_uninstalled(name) if installed?(name)
|
||||
@ -839,37 +893,43 @@ class ReporterHub
|
||||
output_dump_formula_or_cask_report "Deleted Installed Formulae", formulae
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def dump_deleted_cask_report
|
||||
return if Homebrew::SimulateSystem.simulating_or_running_on_linux?
|
||||
|
||||
casks = select_formula_or_cask(:DC).sort.filter_map do |name|
|
||||
name = name.split("/").last
|
||||
name = T.must(name.split("/").last)
|
||||
pretty_uninstalled(name) if cask_installed?(name)
|
||||
end
|
||||
|
||||
output_dump_formula_or_cask_report "Deleted Installed Casks", casks
|
||||
end
|
||||
|
||||
sig { params(title: String, formulae_or_casks: T::Array[String]).void }
|
||||
def output_dump_formula_or_cask_report(title, formulae_or_casks)
|
||||
return if formulae_or_casks.blank?
|
||||
|
||||
ohai title, Formatter.columns(formulae_or_casks.sort)
|
||||
end
|
||||
|
||||
sig { params(formula: String).returns(T::Boolean) }
|
||||
def installed?(formula)
|
||||
(HOMEBREW_CELLAR/formula.split("/").last).directory?
|
||||
end
|
||||
|
||||
sig { params(formula: String).returns(T::Boolean) }
|
||||
def outdated?(formula)
|
||||
Formula[formula].outdated?
|
||||
rescue FormulaUnavailableError
|
||||
false
|
||||
end
|
||||
|
||||
sig { params(cask: String).returns(T::Boolean) }
|
||||
def cask_installed?(cask)
|
||||
(Cask::Caskroom.path/cask).directory?
|
||||
end
|
||||
|
||||
sig { params(cask: String).returns(T::Boolean) }
|
||||
def cask_outdated?(cask)
|
||||
Cask::CaskLoader.load(cask).outdated?
|
||||
rescue Cask::CaskError
|
||||
|
Loading…
x
Reference in New Issue
Block a user