diff --git a/Library/Homebrew/bundle/dumper.rb b/Library/Homebrew/bundle/dumper.rb index c46fb7ea52..218b297890 100644 --- a/Library/Homebrew/bundle/dumper.rb +++ b/Library/Homebrew/bundle/dumper.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "fileutils" @@ -7,12 +7,25 @@ require "pathname" module Homebrew module Bundle module Dumper + sig { params(brewfile_path: Pathname, force: T::Boolean).returns(T::Boolean) } private_class_method def self.can_write_to_brewfile?(brewfile_path, force: false) raise "#{brewfile_path} already exists" if should_not_write_file?(brewfile_path, overwrite: force) true end + sig { + params( + describe: T::Boolean, + no_restart: T::Boolean, + formulae: T::Boolean, + taps: T::Boolean, + casks: T::Boolean, + mas: T::Boolean, + whalebrew: T::Boolean, + vscode: T::Boolean, + ).returns(String) + } def self.build_brewfile(describe:, no_restart:, formulae:, taps:, casks:, mas:, whalebrew:, vscode:) require "bundle/tap_dumper" require "bundle/formula_dumper" @@ -31,6 +44,21 @@ module Homebrew "#{content.reject(&:empty?).join("\n")}\n" end + sig { + params( + global: T::Boolean, + file: T.nilable(String), + describe: T::Boolean, + force: T::Boolean, + no_restart: T::Boolean, + formulae: T::Boolean, + taps: T::Boolean, + casks: T::Boolean, + mas: T::Boolean, + whalebrew: T::Boolean, + vscode: T::Boolean, + ).void + } def self.dump_brewfile(global:, file:, describe:, force:, no_restart:, formulae:, taps:, casks:, mas:, whalebrew:, vscode:) path = brewfile_path(global:, file:) @@ -39,15 +67,18 @@ module Homebrew write_file path, content end + sig { params(global: T::Boolean, file: T.nilable(String)).returns(Pathname) } def self.brewfile_path(global: false, file: nil) require "bundle/brewfile" Brewfile.path(dash_writes_to_stdout: true, global:, file:) end + sig { params(file: Pathname, overwrite: T::Boolean).returns(T::Boolean) } private_class_method def self.should_not_write_file?(file, overwrite: false) file.exist? && !overwrite && file.to_s != "/dev/stdout" end + sig { params(file: Pathname, content: String).void } def self.write_file(file, content) Bundle.exchange_uid_if_needed! do file.open("w") { |io| io.write content } diff --git a/Library/Homebrew/bundle/mac_app_store_dumper.rb b/Library/Homebrew/bundle/mac_app_store_dumper.rb index 390b10d386..1c2d228e65 100644 --- a/Library/Homebrew/bundle/mac_app_store_dumper.rb +++ b/Library/Homebrew/bundle/mac_app_store_dumper.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "json" @@ -6,11 +6,14 @@ require "json" module Homebrew module Bundle module MacAppStoreDumper + sig { void } def self.reset! @apps = nil end + sig { returns(T::Array[[String, String]]) } def self.apps + @apps ||= T.let(nil, T.nilable(T::Array[[String, String]])) @apps ||= if Bundle.mas_installed? `mas list 2>/dev/null`.split("\n").map do |app| app_details = app.match(/\A(?\d+)\s+(?.*?)\s+\((?[\d.]*)\)\Z/) @@ -19,7 +22,7 @@ module Homebrew # Strip unprintable characters if app_details name = T.must(app_details[:name]) - [app_details[:id], name.gsub(/[[:cntrl:]]|[\p{C}]/, "")] + [T.must(app_details[:id]), name.gsub(/[[:cntrl:]]|[\p{C}]/, "")] end end else @@ -27,10 +30,12 @@ module Homebrew end.compact end + sig { returns(T::Array[Integer]) } def self.app_ids apps.map { |id, _| id.to_i } end + sig { returns(String) } def self.dump apps.sort_by { |_, name| name.downcase }.map { |id, name| "mas \"#{name}\", id: #{id}" }.join("\n") end diff --git a/Library/Homebrew/bundle/tap_dumper.rb b/Library/Homebrew/bundle/tap_dumper.rb index 94d042aa44..1118742876 100644 --- a/Library/Homebrew/bundle/tap_dumper.rb +++ b/Library/Homebrew/bundle/tap_dumper.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "json" @@ -6,10 +6,12 @@ require "json" module Homebrew module Bundle module TapDumper + sig { void } def self.reset! @taps = nil end + sig { returns(String) } def self.dump taps.map do |tap| remote = if tap.custom_remote? && (tap_remote = tap.remote) @@ -27,11 +29,14 @@ module Homebrew end.sort.uniq.join("\n") end + sig { returns(T::Array[String]) } def self.tap_names taps.map(&:name) end + sig { returns(T::Array[Tap]) } private_class_method def self.taps + @taps ||= T.let(nil, T.nilable(T::Array[Tap])) @taps ||= begin require "tap" Tap.select(&:installed?).to_a diff --git a/Library/Homebrew/bundle/vscode_extension_dumper.rb b/Library/Homebrew/bundle/vscode_extension_dumper.rb index 285102b1cb..68bf9616ff 100644 --- a/Library/Homebrew/bundle/vscode_extension_dumper.rb +++ b/Library/Homebrew/bundle/vscode_extension_dumper.rb @@ -1,14 +1,17 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true module Homebrew module Bundle module VscodeExtensionDumper + sig { void } def self.reset! @extensions = nil end + sig { returns(T::Array[String]) } def self.extensions + @extensions ||= T.let(nil, T.nilable(T::Array[String])) @extensions ||= if Bundle.vscode_installed? Bundle.exchange_uid_if_needed! do `"#{Bundle.which_vscode}" --list-extensions 2>/dev/null` @@ -18,6 +21,7 @@ module Homebrew end end + sig { returns(String) } def self.dump extensions.map { |name| "vscode \"#{name}\"" }.join("\n") end diff --git a/Library/Homebrew/manpages.rb b/Library/Homebrew/manpages.rb index e2d79bf1f8..f2ccd27852 100644 --- a/Library/Homebrew/manpages.rb +++ b/Library/Homebrew/manpages.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "cli/parser" @@ -22,10 +22,11 @@ module Homebrew keyword_init: true, ) - SOURCE_PATH = (HOMEBREW_LIBRARY_PATH/"manpages").freeze - TARGET_MAN_PATH = (HOMEBREW_REPOSITORY/"manpages").freeze - TARGET_DOC_PATH = (HOMEBREW_REPOSITORY/"docs").freeze + SOURCE_PATH = T.let((HOMEBREW_LIBRARY_PATH/"manpages").freeze, Pathname) + TARGET_MAN_PATH = T.let((HOMEBREW_REPOSITORY/"manpages").freeze, Pathname) + TARGET_DOC_PATH = T.let((HOMEBREW_REPOSITORY/"docs").freeze, Pathname) + sig { params(quiet: T::Boolean).void } def self.regenerate_man_pages(quiet:) require "kramdown" require "manpages/parser/ronn" @@ -45,6 +46,7 @@ module Homebrew File.write(TARGET_MAN_PATH/"brew.1", roff) end + sig { params(quiet: T::Boolean).void } def self.build_man_page(quiet:) template = (SOURCE_PATH/"brew.1.md.erb").read readme = HOMEBREW_REPOSITORY/"README.md" @@ -70,11 +72,13 @@ module Homebrew ERB.new(template, trim_mode: ">").result(variables.instance_eval { binding }) end + sig { params(path: Pathname).returns(String) } def self.sort_key_for_path(path) # Options after regular commands (`~` comes after `z` in ASCII table). path.basename.to_s.sub(/\.(rb|sh)$/, "").sub(/^--/, "~~") end + sig { params(cmd_paths: T::Array[Pathname]).returns(String) } def self.generate_cmd_manpages(cmd_paths) man_page_lines = [] @@ -99,7 +103,9 @@ module Homebrew sig { params(cmd_parser: CLI::Parser).returns(T::Array[String]) } def self.cmd_parser_manpage_lines(cmd_parser) - lines = [format_usage_banner(cmd_parser.usage_banner_text)] + lines = [] + usage_banner_text = cmd_parser.usage_banner_text + lines << format_usage_banner(usage_banner_text) if usage_banner_text lines += cmd_parser.processed_options.filter_map do |short, long, desc, hidden| next if hidden @@ -115,14 +121,21 @@ module Homebrew lines end + sig { params(cmd_path: Pathname).returns(T.nilable(T::Array[String])) } def self.cmd_comment_manpage_lines(cmd_path) comment_lines = cmd_path.read.lines.grep(/^#:/) return if comment_lines.empty? - return if comment_lines.first.include?("@hide_from_man_page") - lines = [format_usage_banner(comment_lines.first).chomp] - comment_lines.slice(1..-1) - .each do |line| + first_comment_line = comment_lines.first + return unless first_comment_line + return if first_comment_line.include?("@hide_from_man_page") + + lines = [format_usage_banner(first_comment_line).chomp] + all_but_first_comment_lines = comment_lines.slice(1..-1) + return unless all_but_first_comment_lines + return if all_but_first_comment_lines.empty? + + all_but_first_comment_lines.each do |line| line = line.slice(4..-2) unless line lines.last << "\n" @@ -174,10 +187,18 @@ module Homebrew lines.join("\n") end + sig { params(opt: T.nilable(String)).returns(T.nilable(String)) } def self.format_opt(opt) "`#{opt}`" unless opt.nil? end + sig { + params( + short: T.nilable(String), + long: T.nilable(String), + desc: String, + ).returns(String) + } def self.generate_option_doc(short, long, desc) comma = (short && long) ? ", " : "" <<~EOS @@ -188,9 +209,10 @@ module Homebrew EOS end + sig { params(usage_banner: String).returns(String) } def self.format_usage_banner(usage_banner) - usage_banner&.sub(/^(#: *\* )?/, "### ") - &.gsub(/(?