diff --git a/Library/Homebrew/cask/audit.rb b/Library/Homebrew/cask/audit.rb index 0b36542061..aea4b25200 100644 --- a/Library/Homebrew/cask/audit.rb +++ b/Library/Homebrew/cask/audit.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cask/denylist" @@ -19,6 +19,14 @@ module Cask include SystemCommand::Mixin include ::Utils::Curl + Error = T.type_alias do + { + message: T.nilable(String), + location: T.nilable(Homebrew::SourceLocation), + corrected: T::Boolean, + } + end + sig { returns(Cask) } attr_reader :cask @@ -47,6 +55,7 @@ module Cask download ||= online || signing @cask = cask + @download = T.let(nil, T.nilable(Download)) @download = Download.new(cask, quarantine:) if download @online = online @strict = strict @@ -88,8 +97,9 @@ module Cask self end + sig { returns(T::Array[Error]) } def errors - @errors ||= [] + @errors ||= T.let([], T.nilable(T::Array[Error])) end sig { returns(T::Boolean) } @@ -113,9 +123,10 @@ module Cask # Only raise non-critical audits if the user specified `--strict`. return if strict_only && !@strict - errors << ({ message:, location:, corrected: false }) + errors << { message:, location:, corrected: false } end + sig { returns(T.nilable(String)) } def result Formatter.error("failed") if errors? end @@ -346,6 +357,7 @@ module Cask location: url.location end + sig { void } def audit_download_url_is_osdn return if (url = cask.url).nil? return if block_url_offline? @@ -541,8 +553,15 @@ module Cask end end - sig { void } - def extract_artifacts + sig { + params( + _block: T.nilable(T.proc.params( + arg0: T::Array[T.any(Artifact::Pkg, Artifact::Relocated)], + arg1: Pathname, + ).void), + ).void + } + def extract_artifacts(&_block) return unless online? return if (download = self.download).nil? @@ -557,7 +576,7 @@ module Cask return if artifacts.empty? - @tmpdir ||= Pathname(Dir.mktmpdir("cask-audit", HOMEBREW_TEMP)) + @tmpdir ||= T.let(Pathname(Dir.mktmpdir("cask-audit", HOMEBREW_TEMP)), T.nilable(Pathname)) # Clean up tmp dir when @tmpdir object is destroyed ObjectSpace.define_finalizer( @@ -606,7 +625,8 @@ module Cask .extract_nestedly(to: @tmpdir, verbose: false) end - @artifacts_extracted = true # Set the flag to indicate that extraction has occurred. + # Set the flag to indicate that extraction has occurred. + @artifacts_extracted = T.let(true, T.nilable(TrueClass)) # Yield the artifacts and temp directory to the block if provided. yield artifacts, @tmpdir if block_given? @@ -626,8 +646,8 @@ module Cask extract_artifacts do |artifacts, tmpdir| is_container = artifacts.any? { |a| a.is_a?(Artifact::App) || a.is_a?(Artifact::Pkg) } - artifacts.filter { |a| a.is_a?(Artifact::App) || a.is_a?(Artifact::Binary) } - .each do |artifact| + artifacts.each do |artifact| + next if !artifact.is_a?(Artifact::App) && !artifact.is_a?(Artifact::Binary) next if artifact.is_a?(Artifact::Binary) && is_container path = tmpdir/artifact.source.relative_path_from(cask.staged_path) @@ -644,10 +664,10 @@ module Cask system_command("lipo", args: ["-archs", main_binary], print_stderr: false) when Artifact::Binary - binary_path = path.to_s.gsub(cask.appdir, tmpdir) + binary_path = path.to_s.gsub(cask.appdir, tmpdir.to_s) system_command("lipo", args: ["-archs", binary_path], print_stderr: true) else - add_error "Unknown artifact type: #{artifact.class}", location: url.location + T.absurd(artifact) end # binary stanza can contain shell scripts, so we just continue if lipo fails. @@ -795,7 +815,7 @@ module Cask return unless online? min_os = T.let(nil, T.untyped) - @staged_path ||= cask.staged_path + @staged_path ||= T.let(cask.staged_path, T.nilable(Pathname)) extract_artifacts do |artifacts, tmpdir| artifacts.each do |artifact| @@ -1104,15 +1124,15 @@ module Cask sig { returns(T::Boolean) } def bad_osdn_url? - domain.match?(%r{^(?:\w+\.)*osdn\.jp(?=/|$)}) + T.must(domain).match?(%r{^(?:\w+\.)*osdn\.jp(?=/|$)}) end - # sig { returns(String) } + sig { returns(T.nilable(String)) } def homepage URI(cask.homepage.to_s).host end - # sig { returns(String) } + sig { returns(T.nilable(String)) } def domain URI(cask.url.to_s).host end @@ -1127,24 +1147,25 @@ module Cask host_uri.host end - return false if homepage.blank? + home = homepage + return false if home.blank? - home = homepage.downcase + home.downcase! if (split_host = T.must(host).split(".")).length >= 3 host = T.must(split_host[-2..]).join(".") end - if (split_home = homepage.split(".")).length >= 3 - home = split_home[-2..].join(".") + if (split_home = home.split(".")).length >= 3 + home = T.must(split_home[-2..]).join(".") end host == home end - # sig { params(url: String).returns(String) } + sig { params(url: String).returns(String) } def strip_url_scheme(url) url.sub(%r{^[^:/]+://(www\.)?}, "") end - # sig { returns(String) } + sig { returns(String) } def url_from_verified strip_url_scheme(T.must(cask.url).verified) end @@ -1154,8 +1175,10 @@ module Cask url_domain, url_path = strip_url_scheme(cask.url.to_s).split("/", 2) verified_domain, verified_path = url_from_verified.split("/", 2) - (url_domain == verified_domain || (verified_domain && url_domain&.end_with?(".#{verified_domain}"))) && - (!verified_path || url_path&.start_with?(verified_path)) + domains_match = (url_domain == verified_domain) || + (verified_domain && url_domain&.end_with?(".#{verified_domain}")) + paths_match = !verified_path || url_path&.start_with?(verified_path) + (domains_match && paths_match) || false end sig { returns(T::Boolean) } @@ -1177,10 +1200,10 @@ module Cask sig { returns(Tap) } def core_tap - @core_tap ||= CoreTap.instance + @core_tap ||= T.let(CoreTap.instance, T.nilable(Tap)) end - # sig { returns(T::Array[String]) } + sig { returns(T::Array[String]) } def core_formula_names core_tap.formula_names end diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb index 9d6e4b3314..7ffe3e0c72 100644 --- a/Library/Homebrew/cli/parser.rb +++ b/Library/Homebrew/cli/parser.rb @@ -445,7 +445,7 @@ module Homebrew .gsub(/`(.*?)`/m, "#{Tty.bold}\\1#{Tty.reset}") .gsub(%r{<([^\s]+?://[^\s]+?)>}) { |url| Formatter.url(url) } .gsub(/\*(.*?)\*|<(.*?)>/m) do |underlined| - underlined[1...-1].gsub(/^(\s*)(.*?)$/, "\\1#{Tty.underline}\\2#{Tty.reset}") + T.must(underlined[1...-1]).gsub(/^(\s*)(.*?)$/, "\\1#{Tty.underline}\\2#{Tty.reset}") end end diff --git a/Library/Homebrew/utils/formatter.rb b/Library/Homebrew/utils/formatter.rb index 3e7428c80e..9d24a8b78d 100644 --- a/Library/Homebrew/utils/formatter.rb +++ b/Library/Homebrew/utils/formatter.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "utils/tty" @@ -10,6 +10,7 @@ module Formatter COMMAND_DESC_WIDTH = 80 OPTION_DESC_WIDTH = 45 + sig { params(string: String, color: T.nilable(Symbol)).returns(String) } def self.arrow(string, color: nil) prefix("==>", string, color) end @@ -17,18 +18,22 @@ module Formatter # Format a string as headline. # # @api internal + sig { params(string: String, color: T.nilable(Symbol)).returns(String) } def self.headline(string, color: nil) arrow("#{Tty.bold}#{string}#{Tty.reset}", color:) end + sig { params(string: Object).returns(String) } def self.identifier(string) "#{Tty.green}#{string}#{Tty.default}" end + sig { params(string: String).returns(String) } def self.bold(string) "#{Tty.bold}#{string}#{Tty.reset}" end + sig { params(string: String).returns(String) } def self.option(string) bold(string) end @@ -36,6 +41,7 @@ module Formatter # Format a string as success, with an optional label. # # @api internal + sig { params(string: String, label: T.nilable(String)).returns(String) } def self.success(string, label: nil) label(label, string, :green) end @@ -43,6 +49,7 @@ module Formatter # Format a string as warning, with an optional label. # # @api internal + sig { params(string: T.any(String, Exception), label: T.nilable(String)).returns(String) } def self.warning(string, label: nil) label(label, string, :yellow) end @@ -50,6 +57,7 @@ module Formatter # Format a string as error, with an optional label. # # @api internal + sig { params(string: T.any(String, Exception), label: T.nilable(String)).returns(String) } def self.error(string, label: nil) label(label, string, :red) end @@ -80,6 +88,7 @@ module Formatter # so we always wrap one word before an option. # @see https://github.com/Homebrew/brew/pull/12672 # @see https://macromates.com/blog/2006/wrapping-text-with-regular-expressions/ + sig { params(string: String, width: Integer).returns(String) } def self.format_help_text(string, width: 172) desc = OPTION_DESC_WIDTH indent = width - desc @@ -90,21 +99,26 @@ module Formatter .gsub(/(.{1,#{width}})( +|$)(?!-)\n?/, "\\1\n") end + sig { params(string: T.any(NilClass, String, URI::Generic)).returns(String) } def self.url(string) "#{Tty.underline}#{string}#{Tty.no_underline}" end + sig { params(label: T.nilable(String), string: T.any(String, Exception), color: Symbol).returns(String) } def self.label(label, string, color) label = "#{label}:" unless label.nil? prefix(label, string, color) end private_class_method :label + sig { + params(prefix: T.nilable(String), string: T.any(String, Exception), color: T.nilable(Symbol)).returns(String) + } def self.prefix(prefix, string, color) if prefix.nil? && color.nil? - string + string.to_s elsif prefix.nil? - "#{Tty.send(color)}#{string}#{Tty.reset}" + "#{Tty.send(T.must(color))}#{string}#{Tty.reset}" elsif color.nil? "#{prefix} #{string}" else @@ -116,7 +130,8 @@ module Formatter # Layout objects in columns that fit the current terminal width. # # @api internal - def self.columns(*objects, gap_size: 2) + sig { params(objects: T::Array[String], gap_size: Integer).returns(String) } + def self.columns(objects, gap_size: 2) objects = objects.flatten.map(&:to_s) fallback = proc do @@ -145,7 +160,7 @@ module Formatter item_indices_for_row = T.cast(row_index.step(objects.size - 1, rows).to_a, T::Array[Integer]) first_n = T.must(item_indices_for_row[0...-1]).map do |index| - objects[index] + "".rjust(col_width - object_lengths.fetch(index)) + objects.fetch(index) + "".rjust(col_width - object_lengths.fetch(index)) end # don't add trailing whitespace to last column