From 268f801038c756686a25ef533e28f246cf33dd18 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 6 Jan 2025 00:12:03 +0000 Subject: [PATCH] Bump more files to Sorbet `typed: strict` --- Library/.rubocop.yml | 1 + Library/Homebrew/api/formula.rb | 9 +-- Library/Homebrew/bundle_version.rb | 8 ++- Library/Homebrew/cask_dependent.rb | 66 ++++++++++++------- Library/Homebrew/dev-cmd/bump-cask-pr.rb | 4 +- Library/Homebrew/formula_info.rb | 19 +++++- Library/Homebrew/utils/inreplace.rb | 14 +++- Library/Homebrew/utils/repology.rb | 14 +++- .../utils/ruby_check_version_script.rb | 2 +- 9 files changed, 94 insertions(+), 43 deletions(-) diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index 96c2f71e1f..c45838da8c 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -284,6 +284,7 @@ Sorbet/StrictSigil: - "Taps/**/*" - "/**/{Formula,Casks}/**/*.rb" - "**/{Formula,Casks}/**/*.rb" + - "Homebrew/utils/ruby_check_version_script.rb" # A standalone script. - "Homebrew/{standalone,startup}/*.rb" # These are loaded before sorbet-runtime - "Homebrew/test/**/*.rb" diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index 0baaa94e9d..d905bb4c85 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "extend/cachable" @@ -15,7 +15,7 @@ module Homebrew private_class_method :cache - sig { params(name: String).returns(Hash) } + sig { params(name: String).returns(T::Hash[String, T.untyped]) } def self.fetch(name) Homebrew::API.fetch "formula/#{name}.json" end @@ -41,6 +41,7 @@ module Homebrew end end + sig { returns(Pathname) } def self.cached_json_file_path if Homebrew::API.internal_json_v3? HOMEBREW_CACHE_API/INTERNAL_V3_API_FILENAME @@ -75,7 +76,7 @@ module Homebrew end private_class_method :download_and_cache_data! - sig { returns(T::Hash[String, Hash]) } + sig { returns(T::Hash[String, T.untyped]) } def self.all_formulae unless cache.key?("formulae") json_updated = download_and_cache_data! @@ -105,7 +106,7 @@ module Homebrew cache["renames"] end - sig { returns(Hash) } + sig { returns(T::Hash[String, T.untyped]) } def self.tap_migrations # Not sure that we need to reload here. unless cache.key?("tap_migrations") diff --git a/Library/Homebrew/bundle_version.rb b/Library/Homebrew/bundle_version.rb index 065e3098ba..8ebcf966fb 100644 --- a/Library/Homebrew/bundle_version.rb +++ b/Library/Homebrew/bundle_version.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "system_command" @@ -51,14 +51,15 @@ module Homebrew # Remove version from short version, if present. short_version = short_version&.sub(/\s*\(#{Regexp.escape(version)}\)\Z/, "") if version - @short_version = short_version.presence - @version = version.presence + @short_version = T.let(short_version.presence, T.nilable(String)) + @version = T.let(version.presence, T.nilable(String)) return if @short_version || @version raise ArgumentError, "`short_version` and `version` cannot both be `nil` or empty" end + sig { params(other: BundleVersion).returns(T.any(Integer, NilClass)) } def <=>(other) return super unless instance_of?(other.class) @@ -82,6 +83,7 @@ module Homebrew difference end + sig { params(other: BundleVersion).returns(T::Boolean) } def ==(other) instance_of?(other.class) && short_version == other.short_version && version == other.version end diff --git a/Library/Homebrew/cask_dependent.rb b/Library/Homebrew/cask_dependent.rb index cdea653623..01bd0a7f19 100644 --- a/Library/Homebrew/cask_dependent.rb +++ b/Library/Homebrew/cask_dependent.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "requirement" @@ -12,66 +12,82 @@ class CaskDependent end end + sig { returns(Cask::Cask) } attr_reader :cask + sig { params(cask: Cask::Cask).void } def initialize(cask) - @cask = cask + @cask = T.let(cask, Cask::Cask) end + sig { returns(String) } def name @cask.token end + sig { returns(String) } def full_name @cask.full_name end + sig { returns(T::Array[Dependency]) } def runtime_dependencies deps.flat_map { |dep| [dep, *dep.to_formula.runtime_dependencies] }.uniq end + sig { returns(T::Array[Dependency]) } def deps - @deps ||= @cask.depends_on.formula.map do |f| - Dependency.new f - end + @deps ||= T.let( + @cask.depends_on.formula.map do |f| + Dependency.new f + end, + T.nilable(T::Array[Dependency]), + ) end + sig { returns(T::Array[CaskDependent::Requirement]) } def requirements - @requirements ||= begin - requirements = [] - dsl_reqs = @cask.depends_on + @requirements ||= T.let( + begin + requirements = [] + dsl_reqs = @cask.depends_on - dsl_reqs.arch&.each do |arch| - arch = if arch[:bits] == 64 - if arch[:type] == :intel - :x86_64 + dsl_reqs.arch&.each do |arch| + arch = if arch[:bits] == 64 + if arch[:type] == :intel + :x86_64 + else + :"#{arch[:type]}64" + end + elsif arch[:type] == :intel && arch[:bits] == 32 + :i386 else - :"#{arch[:type]}64" + arch[:type] end - elsif arch[:type] == :intel && arch[:bits] == 32 - :i386 - else - arch[:type] + requirements << ArchRequirement.new([arch]) end - requirements << ArchRequirement.new([arch]) - end - dsl_reqs.cask.each do |cask_ref| - requirements << CaskDependent::Requirement.new([{ cask: cask_ref }]) - end - requirements << dsl_reqs.macos if dsl_reqs.macos + dsl_reqs.cask.each do |cask_ref| + requirements << CaskDependent::Requirement.new([{ cask: cask_ref }]) + end + requirements << dsl_reqs.macos if dsl_reqs.macos - requirements - end + requirements + end, + T.nilable(T::Array[CaskDependent::Requirement]), + ) end + sig { params(block: T.nilable(T.proc.returns(T::Array[Dependency]))).returns(T::Array[Dependency]) } def recursive_dependencies(&block) Dependency.expand(self, &block) end + sig { params(block: T.nilable(T.proc.returns(CaskDependent::Requirement))).returns(Requirements) } def recursive_requirements(&block) Requirement.expand(self, &block) end + sig { returns(T::Boolean) } def any_version_installed? @cask.installed? end diff --git a/Library/Homebrew/dev-cmd/bump-cask-pr.rb b/Library/Homebrew/dev-cmd/bump-cask-pr.rb index 579e9db915..a46968cf83 100644 --- a/Library/Homebrew/dev-cmd/bump-cask-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-cask-pr.rb @@ -186,8 +186,8 @@ module Homebrew cask: Cask::Cask, new_hash: T.any(NilClass, String, Symbol), new_version: BumpVersionParser, - replacement_pairs: T::Array[[T.any(Regexp, String), T.any(Regexp, String)]], - ).returns(T::Array[[T.any(Regexp, String), T.any(Regexp, String)]]) + replacement_pairs: T::Array[[T.any(Regexp, String), T.any(Pathname, String)]], + ).returns(T::Array[[T.any(Regexp, String), T.any(Pathname, String)]]) } def replace_version_and_checksum(cask, new_hash, new_version, replacement_pairs) # When blocks are absent, arch is not relevant. For consistency, we simulate the arm architecture. diff --git a/Library/Homebrew/formula_info.rb b/Library/Homebrew/formula_info.rb index 7ec8734e4e..56be071d19 100644 --- a/Library/Homebrew/formula_info.rb +++ b/Library/Homebrew/formula_info.rb @@ -1,17 +1,20 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true # Formula information drawn from an external `brew info --json` call. class FormulaInfo # The whole info structure parsed from the JSON. + sig { returns(T::Hash[String, T.untyped]) } attr_accessor :info + sig { params(info: T::Hash[String, T.untyped]).void } def initialize(info) - @info = info + @info = T.let(info, T::Hash[String, T.untyped]) end # Looks up formula on disk and reads its info. # Returns nil if formula is absent or if there was an error reading it. + sig { params(name: Pathname).returns(T.nilable(FormulaInfo)) } def self.lookup(name) json = Utils.popen_read( *HOMEBREW_RUBY_EXEC_ARGS, @@ -27,12 +30,16 @@ class FormulaInfo FormulaInfo.new(JSON.parse(json)[0]) end + sig { returns(T::Array[String]) } def bottle_tags return [] unless info["bottle"]["stable"] info["bottle"]["stable"]["files"].keys end + sig { + params(my_bottle_tag: T.any(Utils::Bottles::Tag, T.nilable(String))).returns(T.nilable(T::Hash[String, String])) + } def bottle_info(my_bottle_tag = Utils::Bottles.tag) tag_s = my_bottle_tag.to_s return unless info["bottle"]["stable"] @@ -43,29 +50,35 @@ class FormulaInfo { "url" => btl_info["url"], "sha256" => btl_info["sha256"] } end + sig { returns(T.nilable(T::Hash[String, String])) } def bottle_info_any bottle_info(any_bottle_tag) end + sig { returns(T.nilable(String)) } def any_bottle_tag tag = Utils::Bottles.tag.to_s # Prefer native bottles as a convenience for download caching bottle_tags.include?(tag) ? tag : bottle_tags.first end + sig { params(spec_type: Symbol).returns(Version) } def version(spec_type) version_str = info["versions"][spec_type.to_s] - version_str && Version.new(version_str) + Version.new(version_str) end + sig { params(spec_type: Symbol).returns(PkgVersion) } def pkg_version(spec_type = :stable) PkgVersion.new(version(spec_type), revision) end + sig { returns(Integer) } def revision info["revision"] end + sig { params(str: String).void } def self.force_utf8!(str) str.force_encoding("UTF-8") if str.respond_to?(:force_encoding) end diff --git a/Library/Homebrew/utils/inreplace.rb b/Library/Homebrew/utils/inreplace.rb index 3d380301d1..360b90be72 100644 --- a/Library/Homebrew/utils/inreplace.rb +++ b/Library/Homebrew/utils/inreplace.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "utils/string_inreplace_extension" @@ -8,6 +8,7 @@ module Utils module Inreplace # Error during text replacement. class Error < RuntimeError + sig { params(errors: T::Hash[String, T::Array[String]]).void } def initialize(errors) formatted_errors = errors.reduce(+"inreplace failed\n") do |s, (path, errs)| s << "#{path}:\n" << errs.map { |e| " #{e}\n" }.join @@ -82,12 +83,19 @@ module Utils raise Utils::Inreplace::Error, errors if errors.present? end + sig { + params( + path: T.any(String, Pathname), + replacement_pairs: T::Array[[T.any(Regexp, Pathname, String), T.any(Pathname, String)]], + read_only_run: T::Boolean, + silent: T::Boolean, + ).returns(String) + } def self.inreplace_pairs(path, replacement_pairs, read_only_run: false, silent: false) str = File.binread(path) contents = StringInreplaceExtension.new(str) replacement_pairs.each do |old, new| - ohai "replace #{old.inspect} with #{new.inspect}" unless silent - unless old + if old.blank? contents.errors << "No old value for new value #{new}! Did you pass the wrong arguments?" next end diff --git a/Library/Homebrew/utils/repology.rb b/Library/Homebrew/utils/repology.rb index a382f47f5e..c2681327d2 100644 --- a/Library/Homebrew/utils/repology.rb +++ b/Library/Homebrew/utils/repology.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "utils/curl" @@ -10,6 +10,7 @@ module Repology MAX_PAGINATION = 15 private_constant :MAX_PAGINATION + sig { params(last_package_in_response: T.nilable(String), repository: String).returns(T::Hash[String, T.untyped]) } def self.query_api(last_package_in_response = "", repository:) last_package_in_response += "/" if last_package_in_response.present? url = "https://repology.org/api/v1/projects/#{last_package_in_response}?inrepo=#{repository}&outdated=1" @@ -27,6 +28,7 @@ module Repology raise end + sig { params(name: String, repository: String).returns(T.nilable(T::Hash[String, T.untyped])) } def self.single_package_query(name, repository:) url = "https://repology.org/api/v1/project/#{name}" @@ -47,6 +49,13 @@ module Repology nil end + sig { + params( + limit: T.nilable(Integer), + last_package: T.nilable(String), + repository: String, + ).returns(T::Hash[String, T.untyped]) + } def self.parse_api_response(limit = nil, last_package = "", repository:) package_term = case repository when HOMEBREW_CORE @@ -80,6 +89,7 @@ module Repology outdated_packages.sort.to_h end + sig { params(repositories: T::Array[String]).returns(T.any(String, Version)) } def self.latest_version(repositories) # The status is "unique" when the package is present only in Homebrew, so # Repology has no way of knowing if the package is up-to-date. @@ -97,6 +107,6 @@ module Repology # scheme return "no latest version" if latest_version.blank? - Version.new(latest_version["version"]) + Version.new(T.must(latest_version["version"])) end end diff --git a/Library/Homebrew/utils/ruby_check_version_script.rb b/Library/Homebrew/utils/ruby_check_version_script.rb index 8a3471269c..cb77d2e4d3 100755 --- a/Library/Homebrew/utils/ruby_check_version_script.rb +++ b/Library/Homebrew/utils/ruby_check_version_script.rb @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: true # frozen_string_literal: true HOMEBREW_REQUIRED_RUBY_VERSION = ARGV.first.freeze