diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 96d599fc2e..753b55289e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Cache Bundler RubyGems - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} @@ -73,7 +73,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Cache Bundler RubyGems - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} @@ -231,7 +231,7 @@ jobs: uses: Homebrew/actions/setup-homebrew@master - name: Cache Bundler RubyGems - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} @@ -276,7 +276,7 @@ jobs: HOMEBREW_NO_INSTALL_FROM_API: 1 - name: Cache Bundler RubyGems - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} @@ -289,7 +289,7 @@ jobs: run: mkdir tests - name: Cache parallel tests log - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: tests key: ${{ runner.os }}-${{ matrix.test-flags }}-parallel_runtime_rspec-${{ github.sha }} diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 75c60d1c42..1c9d6ca93e 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Re-run this workflow if: github.event_name == 'schedule' || github.event.action == 'closed' - uses: reitermarkus/rerun-workflow@1a15891377b6667377207cdd85b628b79ebd6f81 + uses: reitermarkus/rerun-workflow@7381e98aa2bc4464acef4b60ade8d1d1d90e6e65 with: token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} continuous-label: waiting for feedback diff --git a/Library/Homebrew/Gemfile b/Library/Homebrew/Gemfile index 1a5f5d8383..79cb2bcdaa 100644 --- a/Library/Homebrew/Gemfile +++ b/Library/Homebrew/Gemfile @@ -28,6 +28,7 @@ gem "rubocop", require: false gem "rubocop-ast", require: false gem "simplecov", require: false gem "simplecov-cobertura", require: false +gem "stackprof", require: false gem "warning", require: false group :sorbet, optional: true do diff --git a/Library/Homebrew/Gemfile.lock b/Library/Homebrew/Gemfile.lock index 37c6a02ec4..fd3156a293 100644 --- a/Library/Homebrew/Gemfile.lock +++ b/Library/Homebrew/Gemfile.lock @@ -188,6 +188,7 @@ GEM sorbet (>= 0.5.9204) sorbet-runtime (>= 0.5.9204) thor (>= 0.19.2) + stackprof (0.2.24) tapioca (0.7.3) bundler (>= 1.17.3) pry (>= 0.12.2) @@ -257,6 +258,7 @@ DEPENDENCIES sorbet-runtime sorbet-static-and-runtime spoom + stackprof tapioca warning diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index 76f653aad4..6f185bc216 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -1,6 +1,7 @@ -# typed: false +# typed: true # frozen_string_literal: true +# `HOMEBREW_STACKPROF` should be set via `brew prof --stackprof`, not manually. if ENV["HOMEBREW_STACKPROF"] require "rubygems" require "stackprof" @@ -35,8 +36,8 @@ begin empty_argv = ARGV.empty? help_flag_list = %w[-h --help --usage -?] help_flag = !ENV["HOMEBREW_HELP"].nil? - help_cmd_index = nil - cmd = nil + help_cmd_index = T.let(nil, T.nilable(Integer)) + cmd = T.let(nil, T.nilable(String)) ARGV.each_with_index do |arg, i| break if help_flag && cmd @@ -64,7 +65,7 @@ begin path.prepend(HOMEBREW_SHIMS_PATH/"shared") homebrew_path.prepend(HOMEBREW_SHIMS_PATH/"shared") - ENV["PATH"] = path + ENV["PATH"] = path.to_s require "commands" require "settings" @@ -76,7 +77,7 @@ begin homebrew_path.append(Tap.cmd_directories) # External commands expect a normal PATH - ENV["PATH"] = homebrew_path + ENV["PATH"] = homebrew_path.to_s end # Usage instructions should be displayed if and only if one of: diff --git a/Library/Homebrew/cask/artifact/abstract_uninstall.rb b/Library/Homebrew/cask/artifact/abstract_uninstall.rb index 2635bd79d1..7b4d522a7a 100644 --- a/Library/Homebrew/cask/artifact/abstract_uninstall.rb +++ b/Library/Homebrew/cask/artifact/abstract_uninstall.rb @@ -6,8 +6,6 @@ require "timeout" require "utils/user" require "cask/artifact/abstract_artifact" require "cask/pkg" -require "extend/hash_validator" -using HashValidator module Cask module Artifact @@ -38,7 +36,7 @@ module Cask attr_reader :directives def initialize(cask, directives) - directives.assert_valid_keys!(*ORDERED_DIRECTIVES) + directives.assert_valid_keys(*ORDERED_DIRECTIVES) super(cask, **directives) directives[:signal] = Array(directives[:signal]).flatten.each_slice(2).to_a diff --git a/Library/Homebrew/cask/artifact/artifact.rb b/Library/Homebrew/cask/artifact/artifact.rb index 705cc4d18a..a3b25a5972 100644 --- a/Library/Homebrew/cask/artifact/artifact.rb +++ b/Library/Homebrew/cask/artifact/artifact.rb @@ -3,9 +3,6 @@ require "cask/artifact/moved" -require "extend/hash_validator" -using HashValidator - module Cask module Artifact # Generic artifact corresponding to the `artifact` stanza. diff --git a/Library/Homebrew/cask/artifact/installer.rb b/Library/Homebrew/cask/artifact/installer.rb index ef20cd0bd1..735a74688f 100644 --- a/Library/Homebrew/cask/artifact/installer.rb +++ b/Library/Homebrew/cask/artifact/installer.rb @@ -3,9 +3,6 @@ require "cask/artifact/abstract_artifact" -require "extend/hash_validator" -using HashValidator - module Cask module Artifact # Artifact corresponding to the `installer` stanza. @@ -67,7 +64,7 @@ module Cask ) end - args.assert_valid_keys!(*VALID_KEYS) + args.assert_valid_keys(*VALID_KEYS) new(cask, **args) end diff --git a/Library/Homebrew/cask/artifact/pkg.rb b/Library/Homebrew/cask/artifact/pkg.rb index 924f731ae4..7a22057909 100644 --- a/Library/Homebrew/cask/artifact/pkg.rb +++ b/Library/Homebrew/cask/artifact/pkg.rb @@ -6,9 +6,6 @@ require "plist" require "utils/user" require "cask/artifact/abstract_artifact" -require "extend/hash_validator" -using HashValidator - module Cask module Artifact # Artifact corresponding to the `pkg` stanza. @@ -18,7 +15,7 @@ module Cask attr_reader :path, :stanza_options def self.from_args(cask, path, **stanza_options) - stanza_options.assert_valid_keys!(:allow_untrusted, :choices) + stanza_options.assert_valid_keys(:allow_untrusted, :choices) new(cask, path, **stanza_options) end diff --git a/Library/Homebrew/cask/artifact/relocated.rb b/Library/Homebrew/cask/artifact/relocated.rb index ee5ca48d73..80d2e727b1 100644 --- a/Library/Homebrew/cask/artifact/relocated.rb +++ b/Library/Homebrew/cask/artifact/relocated.rb @@ -3,9 +3,6 @@ require "cask/artifact/abstract_artifact" -require "extend/hash_validator" -using HashValidator - module Cask module Artifact # Superclass for all artifacts which have a source and a target location. @@ -20,7 +17,7 @@ module Cask if target_hash raise CaskInvalidError unless target_hash.respond_to?(:keys) - target_hash.assert_valid_keys!(:target) + target_hash.assert_valid_keys(:target) end target_hash ||= {} diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index e654f3260d..5aad041fad 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -20,9 +20,6 @@ module Cask extend APIHashable include Metadata - # TODO: can be removed when API JSON is regenerated with HOMEBREW_PREFIX_PLACEHOLDER. - HOMEBREW_OLD_PREFIX_PLACEHOLDER = "$(brew --prefix)" - attr_reader :token, :sourcefile_path, :source, :config, :default_config, :loader attr_accessor :download, :allow_reassignment diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index a47a6464a3..7d20ee0af0 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -326,13 +326,10 @@ module Cask end def from_h_string_gsubs(string, appdir) - # TODO: HOMEBREW_OLD_PREFIX_PLACEHOLDER can be removed when API JSON is - # regenerated with HOMEBREW_PREFIX_PLACEHOLDER. string.to_s .gsub(HOMEBREW_HOME_PLACEHOLDER, Dir.home) .gsub(HOMEBREW_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) .gsub(HOMEBREW_CASK_APPDIR_PLACEHOLDER, appdir) - .gsub(Cask::HOMEBREW_OLD_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) end def from_h_array_gsubs(array, appdir) diff --git a/Library/Homebrew/cask/cmd.rb b/Library/Homebrew/cask/cmd.rb index acb6de20e7..127995b692 100644 --- a/Library/Homebrew/cask/cmd.rb +++ b/Library/Homebrew/cask/cmd.rb @@ -11,7 +11,6 @@ require "cask/config" require "cask/cmd/abstract_command" require "cask/cmd/audit" -require "cask/cmd/fetch" require "cask/cmd/install" require "cask/cmd/reinstall" diff --git a/Library/Homebrew/cask/cmd/fetch.rb b/Library/Homebrew/cask/cmd/fetch.rb deleted file mode 100644 index da0eb66295..0000000000 --- a/Library/Homebrew/cask/cmd/fetch.rb +++ /dev/null @@ -1,41 +0,0 @@ -# typed: false -# frozen_string_literal: true - -module Cask - class Cmd - # Cask implementation of the `brew fetch` command. - # - # @api private - class Fetch < AbstractCommand - extend T::Sig - - def self.parser - super do - switch "--force", - description: "Force redownloading even if files already exist in local cache." - end - end - - sig { void } - def run - require "cask/download" - require "cask/installer" - - options = { - quarantine: args.quarantine?, - }.compact - - options[:quarantine] = true if options[:quarantine].nil? - - casks.each do |cask| - puts Installer.caveats(cask) - ohai "Downloading external files for Cask #{cask}" - download = Download.new(cask, **options) - download.clear_cache if args.force? - downloaded_path = download.fetch - ohai "Success! Downloaded to: #{downloaded_path}" - end - end - end - end -end diff --git a/Library/Homebrew/cask/cmd/install.rb b/Library/Homebrew/cask/cmd/install.rb index efd84d9365..c4cbab03ef 100644 --- a/Library/Homebrew/cask/cmd/install.rb +++ b/Library/Homebrew/cask/cmd/install.rb @@ -85,8 +85,7 @@ module Cask if dry_run if (casks_to_install = casks.reject(&:installed?).presence) - plural = ::Utils.pluralize("cask", casks_to_install.count) - ohai "Would install #{casks_to_install.count} #{plural}:" + ohai "Would install #{::Utils.pluralize("cask", casks_to_install.count, include_count: true)}:" puts casks_to_install.map(&:full_name).join(" ") end casks.each do |cask| @@ -97,8 +96,8 @@ module Cask .map(&:name) next if dep_names.blank? - plural = ::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y") - ohai "Would install #{dep_names.count} #{plural} for #{cask.full_name}:" + ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y", + include_count: true)} for #{cask.full_name}:" puts dep_names.join(" ") end return diff --git a/Library/Homebrew/cask/config.rb b/Library/Homebrew/cask/config.rb index c99ac319da..ae083bf292 100644 --- a/Library/Homebrew/cask/config.rb +++ b/Library/Homebrew/cask/config.rb @@ -6,9 +6,6 @@ require "json" require "lazy_object" require "locale" -require "extend/hash_validator" -using HashValidator - module Cask # Configuration for installing casks. # @@ -110,8 +107,8 @@ module Cask return end - @env&.assert_valid_keys!(*self.class.defaults.keys) - @explicit.assert_valid_keys!(*self.class.defaults.keys) + @env&.assert_valid_keys(*self.class.defaults.keys) + @explicit.assert_valid_keys(*self.class.defaults.keys) end sig { returns(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]) } @@ -169,10 +166,12 @@ module Cask DEFAULT_DIRS.each_key do |dir| define_method(dir) do + T.bind(self, Config) explicit.fetch(dir, env.fetch(dir, default.fetch(dir))) end define_method(:"#{dir}=") do |path| + T.bind(self, Config) explicit[dir] = Pathname(path).expand_path end end diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 7976100525..ab5a87f6b3 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -339,6 +339,7 @@ module Cask ORDINARY_ARTIFACT_CLASSES.each do |klass| define_method(klass.dsl_key) do |*args, **kwargs| + T.bind(self, DSL) if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) && (artifacts.map(&:class) & ACTIVATABLE_ARTIFACT_CLASSES).any? raise CaskInvalidError.new(cask, "'stage_only' must be the only activatable artifact.") @@ -355,6 +356,7 @@ module Cask ARTIFACT_BLOCK_CLASSES.each do |klass| [klass.dsl_key, klass.uninstall_dsl_key].each do |dsl_key| define_method(dsl_key) do |&block| + T.bind(self, DSL) artifacts.add(klass.new(cask, dsl_key => block)) end end diff --git a/Library/Homebrew/cask/dsl/conflicts_with.rb b/Library/Homebrew/cask/dsl/conflicts_with.rb index da47113ab5..40b19f6056 100644 --- a/Library/Homebrew/cask/dsl/conflicts_with.rb +++ b/Library/Homebrew/cask/dsl/conflicts_with.rb @@ -1,11 +1,8 @@ -# typed: false +# typed: true # frozen_string_literal: true require "delegate" -require "extend/hash_validator" -using HashValidator - module Cask class DSL # Class corresponding to the `conflicts_with` stanza. @@ -22,16 +19,16 @@ module Cask ].freeze def initialize(**options) - options.assert_valid_keys!(*VALID_KEYS) + options.assert_valid_keys(*VALID_KEYS) - conflicts = options.transform_values { |v| Set.new(Array(v)) } + conflicts = options.transform_values { |v| Set.new(Kernel.Array(v)) } conflicts.default = Set.new super(conflicts) end def to_json(generator) - transform_values(&:to_a).to_json(generator) + __getobj__.transform_values(&:to_a).to_json(generator) end end end diff --git a/Library/Homebrew/cask/dsl/depends_on.rb b/Library/Homebrew/cask/dsl/depends_on.rb index 48c96b893f..9522f0553e 100644 --- a/Library/Homebrew/cask/dsl/depends_on.rb +++ b/Library/Homebrew/cask/dsl/depends_on.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "delegate" @@ -38,7 +38,7 @@ module Cask pairs.each do |key, value| raise "invalid depends_on key: '#{key.inspect}'" unless VALID_KEYS.include?(key) - self[key] = send(:"#{key}=", *value) + __getobj__[key] = send(:"#{key}=", *value) end end @@ -54,15 +54,18 @@ module Cask def macos=(*args) raise "Only a single 'depends_on macos' is allowed." if defined?(@macos) + # workaround for https://github.com/sorbet/sorbet/issues/6860 + first_arg = args.first&.to_s + begin @macos = if args.count > 1 MacOSRequirement.new([args], comparator: "==") elsif MacOSVersions::SYMBOLS.key?(args.first) MacOSRequirement.new([args.first], comparator: "==") - elsif /^\s*(?<|>|[=<>]=)\s*:(?\S+)\s*$/ =~ args.first - MacOSRequirement.new([version.to_sym], comparator: comparator) - elsif /^\s*(?<|>|[=<>]=)\s*(?\S+)\s*$/ =~ args.first - MacOSRequirement.new([version], comparator: comparator) + elsif (md = /^\s*(?<|>|[=<>]=)\s*:(?\S+)\s*$/.match(first_arg)) + MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator]) + elsif (md = /^\s*(?<|>|[=<>]=)\s*(?\S+)\s*$/.match(first_arg)) + MacOSRequirement.new([md[:version]], comparator: md[:comparator]) else # rubocop:disable Lint/DuplicateBranch MacOSRequirement.new([args.first], comparator: "==") end diff --git a/Library/Homebrew/cask/dsl/depends_on.rbi b/Library/Homebrew/cask/dsl/depends_on.rbi new file mode 100644 index 0000000000..92d6827c64 --- /dev/null +++ b/Library/Homebrew/cask/dsl/depends_on.rbi @@ -0,0 +1,5 @@ +# typed: strict + +class Cask::DSL::DependsOn + include Kernel +end diff --git a/Library/Homebrew/cask/dsl/version.rb b/Library/Homebrew/cask/dsl/version.rb index bb02439dbf..2b5170406b 100644 --- a/Library/Homebrew/cask/dsl/version.rb +++ b/Library/Homebrew/cask/dsl/version.rb @@ -32,6 +32,7 @@ module Cask def define_divider_deletion_method(divider) method_name = deletion_method_name(divider) define_method(method_name) do + T.bind(self, Version) version { delete(divider) } end end @@ -49,6 +50,7 @@ module Cask def define_divider_conversion_method(left_divider, right_divider) method_name = conversion_method_name(left_divider, right_divider) define_method(method_name) do + T.bind(self, Version) version { gsub(left_divider, right_divider) } end end diff --git a/Library/Homebrew/cask/installer.rb b/Library/Homebrew/cask/installer.rb index 8f9f756912..e5d7ea2559 100644 --- a/Library/Homebrew/cask/installer.rb +++ b/Library/Homebrew/cask/installer.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "formula_installer" @@ -112,8 +112,9 @@ module Cask install_artifacts - if @cask.tap&.should_report_analytics? - ::Utils::Analytics.report_event(:cask_install, @cask.token, on_request: true) + if (tap = @cask.tap) && tap.should_report_analytics? + ::Utils::Analytics.report_event(:cask_install, package_name: @cask.token, tap_name: tap.name, +on_request: true) end purge_backed_up_versioned_files @@ -241,7 +242,7 @@ module Cask save_download_sha if @cask.version.latest? rescue => e begin - already_installed_artifacts.each do |artifact| + already_installed_artifacts&.each do |artifact| if artifact.respond_to?(:uninstall_phase) odebug "Reverting installation of artifact of class #{artifact.class}" artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?) @@ -296,7 +297,7 @@ module Cask graph = ::Utils::TopologicalHash.graph_package_dependencies(@cask) - raise CaskSelfReferencingDependencyError, cask.token if graph[@cask].include?(@cask) + raise CaskSelfReferencingDependencyError, @cask.token if graph[@cask].include?(@cask) ::Utils::TopologicalHash.graph_package_dependencies(primary_container.dependencies, graph) @@ -454,7 +455,7 @@ module Cask artifacts = @cask.artifacts odebug "Uninstalling artifacts" - odebug "#{artifacts.length} #{::Utils.pluralize("artifact", artifacts.length)} defined", artifacts + odebug "#{::Utils.pluralize("artifact", artifacts.length, include_count: true)} defined", artifacts artifacts.each do |artifact| if artifact.respond_to?(:uninstall_phase) diff --git a/Library/Homebrew/cask/quarantine.rb b/Library/Homebrew/cask/quarantine.rb index 2b9ec87058..7333b4e231 100644 --- a/Library/Homebrew/cask/quarantine.rb +++ b/Library/Homebrew/cask/quarantine.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "development_tools" @@ -11,29 +11,27 @@ module Cask module Quarantine extend T::Sig - module_function - QUARANTINE_ATTRIBUTE = "com.apple.quarantine" QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze - def swift + def self.swift @swift ||= DevelopmentTools.locate("swift") end - private :swift + private_class_method :swift - def xattr + def self.xattr @xattr ||= DevelopmentTools.locate("xattr") end - private :xattr + private_class_method :xattr - def swift_target_args + def self.swift_target_args ["-target", "#{Hardware::CPU.arch}-apple-macosx#{MacOS.version}"] end - private :swift_target_args + private_class_method :swift_target_args sig { returns(Symbol) } - def check_quarantine_support + def self.check_quarantine_support odebug "Checking quarantine support" if !system_command(xattr, args: ["-h"], print_stderr: false).success? @@ -58,13 +56,13 @@ module Cask end end - def available? + def self.available? @status ||= check_quarantine_support @status == :quarantine_available end - def detect(file) + def self.detect(file) return if file.nil? odebug "Verifying Gatekeeper status of #{file}" @@ -76,13 +74,13 @@ module Cask quarantine_status end - def status(file) + def self.status(file) system_command(xattr, args: ["-p", QUARANTINE_ATTRIBUTE, file], print_stderr: false).stdout.rstrip end - def toggle_no_translocation_bit(attribute) + def self.toggle_no_translocation_bit(attribute) fields = attribute.split(";") # Fields: status, epoch, download agent, event ID @@ -94,7 +92,7 @@ module Cask fields.join(";") end - def release!(download_path: nil) + def self.release!(download_path: nil) return unless detect(download_path) odebug "Releasing #{download_path} from quarantine" @@ -112,7 +110,7 @@ module Cask raise CaskQuarantineReleaseError.new(download_path, quarantiner.stderr) end - def cask!(cask: nil, download_path: nil, action: true) + def self.cask!(cask: nil, download_path: nil, action: true) return if cask.nil? || download_path.nil? return if detect(download_path) @@ -139,7 +137,7 @@ module Cask end end - def propagate(from: nil, to: nil) + def self.propagate(from: nil, to: nil) return if from.nil? || to.nil? raise CaskError, "#{from} was not quarantined properly." unless detect(from) diff --git a/Library/Homebrew/cask/upgrade.rb b/Library/Homebrew/cask/upgrade.rb index a27925b921..17c38f43be 100644 --- a/Library/Homebrew/cask/upgrade.rb +++ b/Library/Homebrew/cask/upgrade.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "env_config" @@ -124,7 +124,9 @@ module Cask return true if caught_exceptions.empty? raise MultipleCaskErrors, caught_exceptions if caught_exceptions.count > 1 - raise caught_exceptions.first if caught_exceptions.count == 1 + raise caught_exceptions.fetch(0) if caught_exceptions.count == 1 + + false end def self.upgrade_cask( diff --git a/Library/Homebrew/caveats.rb b/Library/Homebrew/caveats.rb index 78f607adce..ebd3ff5736 100644 --- a/Library/Homebrew/caveats.rb +++ b/Library/Homebrew/caveats.rb @@ -94,7 +94,8 @@ class Caveats end end end - s << "\n" + s << "\n" unless s.end_with?("\n") + s end private diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index 11fd1469cd..0d0580471b 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -124,7 +124,7 @@ module Homebrew return unless HOMEBREW_CELLAR.exist? count = Formula.racks.length - puts "#{count} #{Utils.pluralize("keg", count)}, #{HOMEBREW_CELLAR.dup.abv}" + puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}" end sig { params(args: CLI::Args).void } diff --git a/Library/Homebrew/cmd/postgresql-upgrade-database.rb b/Library/Homebrew/cmd/postgresql-upgrade-database.rb new file mode 100755 index 0000000000..50421460d9 --- /dev/null +++ b/Library/Homebrew/cmd/postgresql-upgrade-database.rb @@ -0,0 +1,166 @@ +# typed: false +# frozen_string_literal: true + +require "cli/parser" +require "formula" + +module Homebrew + module_function + + sig { returns(CLI::Parser) } + def postgresql_upgrade_database_args + Homebrew::CLI::Parser.new do + description <<~EOS + Upgrades the database for the `postgresql` formula. + EOS + + named_args :none + end + end + + sig { void } + def postgresql_upgrade_database + postgresql_upgrade_database_args.parse + + name = "postgresql" + pg = Formula[name] + bin = pg.bin + var = pg.var + version = pg.version + pg_version_file = var/"postgres/PG_VERSION" + + pg_version_installed = version.to_s[/^\d+/] + pg_version_data = pg_version_file.read.chomp + if pg_version_installed == pg_version_data + odie <<~EOS + #{name} data already upgraded! + EOS + end + + datadir = var/"postgres" + old_datadir = var/"postgres.old" + if old_datadir.exist? + odie <<~EOS + #{old_datadir} already exists! + Remove it if you want to upgrade data automatically. + EOS + end + + old_pg_name = "#{name}@#{pg_version_data}" + old_pg_glob = "#{HOMEBREW_CELLAR}/#{old_pg_name}/#{pg_version_data}.*/bin" + old_bin = Pathname.glob(old_pg_glob).first + old_bin ||= begin + Formula[old_pg_name] + ohai "brew install #{old_pg_name}" + system "brew", "install", old_pg_name + Pathname.glob(old_pg_glob).first + rescue FormulaUnavailableError + nil + end + + odie "No #{name} #{pg_version_data}.* version installed!" unless old_bin + + server_stopped = false + moved_data = false + initdb_run = false + upgraded = false + + begin + # Following instructions from: + # https://www.postgresql.org/docs/10/static/pgupgrade.html + ohai "Upgrading #{name} data from #{pg_version_data} to #{pg_version_installed}..." + services_json_output = Utils.popen_read("brew", "services", "info", "--all", "--json") + services_json = JSON.parse(services_json_output) + loaded_service_names = services_json.select { |sj| sj[:loaded] }.map { |sj| sj[:name] } + if loaded_service_names.include?(name) + system "brew", "services", "stop", name + service_stopped = true + elsif quiet_system "#{bin}/pg_ctl", "-D", datadir, "status" + system "#{bin}/pg_ctl", "-D", datadir, "stop" + server_stopped = true + end + + # Shut down old server if it is up via brew services + system "brew", "services", "stop", old_pg_name if loaded_service_names.include?(old_pg_name) + + # get 'lc_collate' from old DB" + unless quiet_system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "status" + system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "start" + end + + initdb_args = [] + locale_settings = %w[ + lc_collate + lc_ctype + lc_messages + lc_monetary + lc_numeric + lc_time + server_encoding + ] + locale_settings.each do |setting| + sql = "SELECT setting FROM pg_settings WHERE name LIKE '#{setting}';" + value = Utils.popen_read("#{old_bin}/psql", "postgres", "-qtAX", "-U", ENV.fetch("USER"), "-c", sql).strip + + next if value.empty? + + initdb_args += if setting == "server_encoding" + ["-E #{value}"] + else + ["--#{setting.tr("_", "-")}=#{value}"] + end + end + + if quiet_system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "status" + system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "stop" + end + + ohai "Moving #{name} data from #{datadir} to #{old_datadir}..." + FileUtils.mv datadir, old_datadir + moved_data = true + + (var/"postgres").mkpath + ohai "Creating database..." + safe_system "#{bin}/initdb", *initdb_args, "#{var}/postgres" + initdb_run = true + + ohai "Migrating and upgrading data..." + (var/"log").cd do + safe_system "#{bin}/pg_upgrade", + "-r", + "-b", old_bin, + "-B", bin, + "-d", old_datadir, + "-D", datadir, + "-j", Hardware::CPU.cores.to_s + end + upgraded = true + + ohai "Upgraded #{name} data from #{pg_version_data} to #{pg_version_installed}!" + ohai "Your #{name} #{pg_version_data} data remains at #{old_datadir}" + ensure + if upgraded + if server_stopped + safe_system "#{bin}/pg_ctl", "-D", datadir, "start" + elsif service_stopped + safe_system "brew", "services", "start", name + end + else + onoe "Upgrading #{name} data from #{pg_version_data} to #{pg_version_installed} failed!" + if initdb_run + ohai "Removing empty #{name} initdb database..." + FileUtils.rm_r datadir + end + if moved_data + ohai "Moving #{name} data back from #{old_datadir} to #{datadir}..." + FileUtils.mv old_datadir, datadir + end + if server_stopped + system "#{bin}/pg_ctl", "-D", datadir, "start" + elsif service_stopped + system "brew", "services", "start", name + end + end + end + end +end diff --git a/Library/Homebrew/cmd/tap-info.rb b/Library/Homebrew/cmd/tap-info.rb index 039c861dca..cea6eb2528 100644 --- a/Library/Homebrew/cmd/tap-info.rb +++ b/Library/Homebrew/cmd/tap-info.rb @@ -57,10 +57,10 @@ module Homebrew command_count += tap.command_files.size private_count += 1 if tap.private? end - info = "#{tap_count} #{Utils.pluralize("tap", tap_count)}" + info = Utils.pluralize("tap", tap_count, include_count: true) info += ", #{private_count} private" - info += ", #{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}" - info += ", #{command_count} #{Utils.pluralize("command", command_count)}" + info += ", #{Utils.pluralize("formula", formula_count, plural: "e", include_count: true)}" + info += ", #{Utils.pluralize("command", command_count, include_count: true)}" info += ", #{Tap::TAP_DIRECTORY.dup.abv}" if Tap::TAP_DIRECTORY.directory? puts info else diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 3e0df64c35..a31d149cf5 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -233,8 +233,7 @@ module Homebrew unless updated_taps.empty? auto_update_header args: args - noun = Utils.pluralize("tap", updated_taps.count) - puts "Updated #{updated_taps.count} #{noun} (#{updated_taps.to_sentence})." + puts "Updated #{Utils.pluralize("tap", updated_taps.count, include_count: true)} (#{updated_taps.to_sentence})." updated = true end @@ -656,12 +655,12 @@ class ReporterHub output_dump_formula_or_cask_report "Outdated Casks", outdated_casks elsif report_all if (changed_formulae = select_formula_or_cask(:M).count) && changed_formulae.positive? - noun = Utils.pluralize("formula", changed_formulae, plural: "e") - ohai "Modified Formulae", "Modified #{changed_formulae} #{noun}." + ohai "Modified Formulae", + "Modified #{Utils.pluralize("formula", changed_formulae, plural: "e", include_count: true)}." end if (changed_casks = select_formula_or_cask(:MC).count) && changed_casks.positive? - ohai "Modified Casks", "Modified #{changed_casks} #{Utils.pluralize("cask", changed_casks)}." + ohai "Modified Casks", "Modified #{Utils.pluralize("cask", changed_casks, include_count: true)}." end else outdated_formulae = Formula.installed.select(&:outdated?).map(&:name) diff --git a/Library/Homebrew/cmd/update-reset.sh b/Library/Homebrew/cmd/update-reset.sh index a7b597afa5..f87d623b25 100644 --- a/Library/Homebrew/cmd/update-reset.sh +++ b/Library/Homebrew/cmd/update-reset.sh @@ -12,6 +12,10 @@ git() { # HOMEBREW_LIBRARY is set by bin/brew # shellcheck disable=SC2154 GIT_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/git" --homebrew=print-path)" + if [[ -z "${GIT_EXECUTABLE}" ]] + then + odie "Can't find a working Git!" + fi fi "${GIT_EXECUTABLE}" "$@" } diff --git a/Library/Homebrew/cmd/update.sh b/Library/Homebrew/cmd/update.sh index cf096c46f8..2bcee9996e 100644 --- a/Library/Homebrew/cmd/update.sh +++ b/Library/Homebrew/cmd/update.sh @@ -25,6 +25,10 @@ curl() { if [[ -z "${CURL_EXECUTABLE}" ]] then CURL_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/curl" --homebrew=print-path)" + if [[ -z "${CURL_EXECUTABLE}" ]] + then + odie "Can't find a working Curl!" + fi fi "${CURL_EXECUTABLE}" "$@" } @@ -33,6 +37,10 @@ git() { if [[ -z "${GIT_EXECUTABLE}" ]] then GIT_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/git" --homebrew=print-path)" + if [[ -z "${GIT_EXECUTABLE}" ]] + then + odie "Can't find a working Git!" + fi fi "${GIT_EXECUTABLE}" "$@" } diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 15ddc1f72f..407f7ba295 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -275,22 +275,21 @@ module Homebrew if total_problems_count.positive? puts new_formula_problem_lines.map { |s| " #{s}" } - errors_summary = "#{total_problems_count} #{Utils.pluralize("problem", total_problems_count)}" + errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true) error_sources = [] if formula_count.positive? - error_sources << "#{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}" + error_sources << Utils.pluralize("formula", formula_count, plural: "e", include_count: true) end - error_sources << "#{cask_count} #{Utils.pluralize("cask", cask_count)}" if cask_count.positive? - error_sources << "#{tap_count} #{Utils.pluralize("tap", tap_count)}" if tap_count.positive? + error_sources << Utils.pluralize("cask", cask_count, include_count: true) if cask_count.positive? + error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_count.positive? errors_summary += " in #{error_sources.to_sentence}" if error_sources.any? errors_summary += " detected" if corrected_problem_count.positive? - noun = Utils.pluralize("problem", corrected_problem_count) - errors_summary += ", #{corrected_problem_count} #{noun} corrected" + errors_summary += ", #{Utils.pluralize("problem", corrected_problem_count, include_count: true)} corrected" end ofail errors_summary diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index ebad7138c9..3abc22f96e 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -64,7 +64,8 @@ module Homebrew results[user] = scan_repositories(repos, user, args) grand_totals[user] = total(results[user]) - puts "#{user} contributed #{grand_totals[user].values.sum} times #{time_period(args)}." + puts "#{user} contributed #{Utils.pluralize("time", grand_totals[user].values.sum, + include_count: true)} #{time_period(args)}." puts generate_csv(T.must(user), results[user], grand_totals[user]) if args.csv? return end @@ -79,7 +80,8 @@ module Homebrew results[username] = scan_repositories(repos, username, args) grand_totals[username] = total(results[username]) - puts "#{username} contributed #{grand_totals[username].values.sum} times #{time_period(args)}." + puts "#{username} contributed #{Utils.pluralize("time", grand_totals[username].values.sum, + include_count: true)} #{time_period(args)}." end puts generate_maintainers_csv(grand_totals) if args.csv? diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index 9a1a1618b9..e8ec2195db 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -22,6 +22,8 @@ module Homebrew description: "Download the bottles but don't upload them." switch "--no-commit", description: "Do not generate a new commit before uploading." + switch "--no-cherry-pick", + description: "Do not cherry-pick commits from the pull request branch." switch "-n", "--dry-run", description: "Print what would be done rather than doing it." switch "--clean", @@ -447,10 +449,16 @@ module Homebrew ohai "Fetching #{tap} pull request ##{pr}" Dir.mktmpdir pr do |dir| cd dir do - original_commit = ENV["GITHUB_SHA"].presence || tap.path.git_head + current_branch_head = ENV["GITHUB_SHA"] || tap.git_head + original_commit = if args.no_cherry_pick? + # TODO: Handle the case where `merge-base` returns multiple commits. + Utils.safe_popen_read("git", "-C", tap.path, "merge-base", "origin/HEAD", current_branch_head).strip + else + current_branch_head + end unless args.no_commit? - cherry_pick_pr!(user, repo, pr, path: tap.path, args: args) + cherry_pick_pr!(user, repo, pr, path: tap.path, args: args) unless args.no_cherry_pick? if !args.no_autosquash? && !args.dry_run? autosquash!(original_commit, tap: tap, verbose: args.verbose?, resolve: args.resolve?, reason: args.message) diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index 39ccbc58fc..9b88e6ef7b 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -908,7 +908,7 @@ module Homebrew 0 end - "#{tap.path} (#{cask_count} #{Utils.pluralize("cask", cask_count)})" + "#{tap.path} (#{Utils.pluralize("cask", cask_count, include_count: true)})" end end) diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 0186a3aca8..0074b62ec7 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -491,10 +491,13 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy end end + filename = content_disposition.filename if filename.blank? + next if filename.blank? + # Servers may include '/' in their Content-Disposition filename header. Take only the basename of this, because: # - Unpacking code assumes this is a single file - not something living in a subdirectory. # - Directory traversal attacks are possible without limiting this to just the basename. - File.basename(filename || content_disposition.filename) + File.basename(filename) end filenames = lines.map(&parse_content_disposition).compact diff --git a/Library/Homebrew/extend/git_repository.rbi b/Library/Homebrew/extend/git_repository.rbi index 534b936b96..9ffee241cf 100644 --- a/Library/Homebrew/extend/git_repository.rbi +++ b/Library/Homebrew/extend/git_repository.rbi @@ -1,8 +1,5 @@ # typed: strict module GitRepositoryExtension - include Kernel - - sig { params(args: T.any(String, Pathname)).returns(Pathname) } - def join(*args); end + requires_ancestor { Pathname } end diff --git a/Library/Homebrew/extend/hash_validator.rb b/Library/Homebrew/extend/hash_validator.rb deleted file mode 100644 index 09cfbe68a3..0000000000 --- a/Library/Homebrew/extend/hash_validator.rb +++ /dev/null @@ -1,13 +0,0 @@ -# typed: false -# frozen_string_literal: true - -module HashValidator - refine Hash do - def assert_valid_keys!(*valid_keys) - unknown_keys = keys - valid_keys - return if unknown_keys.empty? - - raise ArgumentError, "invalid keys: #{unknown_keys.map(&:inspect).join(", ")}" - end - end -end diff --git a/Library/Homebrew/extend/hash_validator.rbi b/Library/Homebrew/extend/hash_validator.rbi deleted file mode 100644 index 715b783813..0000000000 --- a/Library/Homebrew/extend/hash_validator.rbi +++ /dev/null @@ -1,6 +0,0 @@ -# typed: strict - -class Hash - sig { params(valid_keys: T.untyped).void } - def assert_valid_keys!(*valid_keys); end -end diff --git a/Library/Homebrew/extend/kernel.rb b/Library/Homebrew/extend/kernel.rb index 77c7547376..8c9672ec4a 100644 --- a/Library/Homebrew/extend/kernel.rb +++ b/Library/Homebrew/extend/kernel.rb @@ -199,13 +199,13 @@ module Kernel if seconds > 59 minutes = seconds / 60 seconds %= 60 - res = +"#{minutes} #{Utils.pluralize("minute", minutes)}" + res = +Utils.pluralize("minute", minutes, include_count: true) return res.freeze if seconds.zero? res << " " end - res << "#{seconds} #{Utils.pluralize("second", seconds)}" + res << Utils.pluralize("second", seconds, include_count: true) res.freeze end diff --git a/Library/Homebrew/extend/object.rbi b/Library/Homebrew/extend/object.rbi new file mode 100644 index 0000000000..cc39405568 --- /dev/null +++ b/Library/Homebrew/extend/object.rbi @@ -0,0 +1,6 @@ +# typed: strict + +class Object + sig { returns(T::Boolean) } + def present?; end +end diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/extend/on_system.rb index ccd923df4d..5d4c581f44 100644 --- a/Library/Homebrew/extend/on_system.rb +++ b/Library/Homebrew/extend/on_system.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "simulate_system" @@ -9,17 +9,15 @@ module OnSystem ARCH_OPTIONS = [:intel, :arm].freeze BASE_OS_OPTIONS = [:macos, :linux].freeze - module_function - sig { params(arch: Symbol).returns(T::Boolean) } - def arch_condition_met?(arch) + def self.arch_condition_met?(arch) raise ArgumentError, "Invalid arch condition: #{arch.inspect}" if ARCH_OPTIONS.exclude?(arch) arch == Homebrew::SimulateSystem.current_arch end sig { params(os_name: Symbol, or_condition: T.nilable(Symbol)).returns(T::Boolean) } - def os_condition_met?(os_name, or_condition = nil) + def self.os_condition_met?(os_name, or_condition = nil) return Homebrew::SimulateSystem.send("simulating_or_running_on_#{os_name}?") if BASE_OS_OPTIONS.include?(os_name) raise ArgumentError, "Invalid OS condition: #{os_name.inspect}" unless MacOSVersions::SYMBOLS.key?(os_name) @@ -46,17 +44,17 @@ module OnSystem end sig { params(method_name: Symbol).returns(Symbol) } - def condition_from_method_name(method_name) + def self.condition_from_method_name(method_name) method_name.to_s.sub(/^on_/, "").to_sym end sig { params(base: Class).void } - def setup_arch_methods(base) + def self.setup_arch_methods(base) ARCH_OPTIONS.each do |arch| base.define_method("on_#{arch}") do |&block| @on_system_blocks_exist = true - return unless OnSystem.arch_condition_met? OnSystem.condition_from_method_name(__method__) + return unless OnSystem.arch_condition_met? OnSystem.condition_from_method_name(T.must(__method__)) @called_in_on_system_block = true result = block.call @@ -75,12 +73,12 @@ module OnSystem end sig { params(base: Class).void } - def setup_base_os_methods(base) + def self.setup_base_os_methods(base) BASE_OS_OPTIONS.each do |base_os| base.define_method("on_#{base_os}") do |&block| @on_system_blocks_exist = true - return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(__method__) + return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(T.must(__method__)) @called_in_on_system_block = true result = block.call @@ -118,12 +116,12 @@ module OnSystem end sig { params(base: Class).void } - def setup_macos_methods(base) + def self.setup_macos_methods(base) MacOSVersions::SYMBOLS.each_key do |os_name| base.define_method("on_#{os_name}") do |or_condition = nil, &block| @on_system_blocks_exist = true - os_condition = OnSystem.condition_from_method_name __method__ + os_condition = OnSystem.condition_from_method_name T.must(__method__) return unless OnSystem.os_condition_met? os_condition, or_condition @called_in_on_system_block = true diff --git a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb index 65585d3cd0..cff64f5802 100644 --- a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb +++ b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb @@ -11,8 +11,6 @@ module UnpackStrategy include UnpackStrategy include SystemCommand::Mixin - using Magic - sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) } def extract_to_dir(unpack_dir, basename:, verbose:) with_env(TZ: "UTC") do diff --git a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rbi b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rbi index ee40416382..43597431d9 100644 --- a/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rbi +++ b/Library/Homebrew/extend/os/mac/unpack_strategy/zip.rbi @@ -1,9 +1,5 @@ # typed: strict -module UnpackStrategy - class Zip - module MacOSZipExtension - include Kernel - end - end +module UnpackStrategy::Zip::MacOSZipExtension + include Kernel end diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index a929f153b8..ca9144b27a 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -343,14 +343,24 @@ class Pathname chmod saved_perms if saved_perms end + # @private + def which_install_info + @which_install_info ||= + if File.executable?("/usr/bin/install-info") + "/usr/bin/install-info" + elsif Formula["texinfo"].any_version_installed? + Formula["texinfo"].opt_bin/"install-info" + end + end + # @private def install_info - quiet_system "/usr/bin/install-info", "--quiet", to_s, "#{dirname}/dir" + quiet_system(which_install_info, "--quiet", to_s, "#{dirname}/dir") end # @private def uninstall_info - quiet_system "/usr/bin/install-info", "--delete", "--quiet", to_s, "#{dirname}/dir" + quiet_system(which_install_info, "--delete", "--quiet", to_s, "#{dirname}/dir") end # Writes an exec script in this folder for each target pathname. @@ -456,6 +466,32 @@ class Pathname def rpaths [] end + + sig { returns(String) } + def magic_number + @magic_number ||= if directory? + "" + else + # Length of the longest regex (currently Tar). + max_magic_number_length = 262 + # FIXME: The `T.let` is a workaround until we have https://github.com/sorbet/sorbet/pull/6865 + T.let(binread(max_magic_number_length), T.nilable(String)) || "" + end + end + + sig { returns(String) } + def file_type + @file_type ||= system_command("file", args: ["-b", self], print_stderr: false) + .stdout.chomp + end + + sig { returns(T::Array[String]) } + def zipinfo + @zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false) + .stdout + .encode(Encoding::UTF_8, invalid: :replace) + .split("\n") + end end require "extend/os/pathname" diff --git a/Library/Homebrew/extend/pathname.rbi b/Library/Homebrew/extend/pathname.rbi index 12a94cd3eb..a682b461ee 100644 --- a/Library/Homebrew/extend/pathname.rbi +++ b/Library/Homebrew/extend/pathname.rbi @@ -1,19 +1,9 @@ # typed: strict module DiskUsageExtension - include Kernel - - def exist?; end - - def symlink?; end - - def resolved_path; end + requires_ancestor { Pathname } end module ObserverPathnameExtension - include Kernel - - def dirname; end - - def basename; end + requires_ancestor { Pathname } end diff --git a/Library/Homebrew/formula_auditor.rb b/Library/Homebrew/formula_auditor.rb index 40530ef8e1..99aa219f5b 100644 --- a/Library/Homebrew/formula_auditor.rb +++ b/Library/Homebrew/formula_auditor.rb @@ -466,6 +466,16 @@ module Homebrew "They must not be upgraded to version 7.11 or newer." end + def audit_keg_only_reason + return unless @core_tap + return unless formula.keg_only? + + keg_only_message = text.to_s.match(/keg_only\s+["'](.*)["']/)&.captures&.first + return unless keg_only_message&.include?("HOMEBREW_PREFIX") + + problem "`keg_only` reason should not include `HOMEBREW_PREFIX` as it creates confusing `brew info` output." + end + def audit_versioned_keg_only return unless @versioned_formula return unless @core_tap diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 12eb8ec499..1cbec1c9ca 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -414,9 +414,9 @@ class FormulaInstaller options = display_options(formula).join(" ") oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip if show_header? - if formula.tap&.should_report_analytics? - action = "#{formula.full_name} #{options}".strip - Utils::Analytics.report_event(:formula_install, action, on_request: installed_on_request?) + if (tap = formula.tap) && tap.should_report_analytics? + Utils::Analytics.report_event(:formula_install, package_name: formula.name, tap_name: tap.name, +on_request: installed_on_request?, options: options) end self.class.attempted << formula diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index 19a09b219e..973f9a1d03 100644 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -326,8 +326,8 @@ module Homebrew if dry_run if (formulae_name_to_install = formulae_to_install.map(&:name)) - plural = Utils.pluralize("formula", formulae_name_to_install.count, plural: "e") - ohai "Would install #{formulae_name_to_install.count} #{plural}:" + ohai "Would install #{Utils.pluralize("formula", formulae_name_to_install.count, + plural: "e", include_count: true)}:" puts formulae_name_to_install.join(" ") formula_installers.each do |fi| @@ -355,8 +355,8 @@ module Homebrew def print_dry_run_dependencies(formula, dependencies, &block) return if dependencies.empty? - plural = Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y") - ohai "Would install #{dependencies.count} #{plural} for #{formula.name}:" + ohai "Would install #{Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y", + include_count: true)} for #{formula.name}:" formula_names = dependencies.map(&:first).map(&:to_formula).map(&block) puts formula_names.join(" ") end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index f878f60f83..8ea2c98f85 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "keg_relocate" @@ -524,8 +524,8 @@ class Keg end def delete_pyc_files! - find { |pn| pn.delete if PYC_EXTENSIONS.include?(pn.extname) } - find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" } + path.find { |pn| pn.delete if PYC_EXTENSIONS.include?(pn.extname) } + path.find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" } end def binary_executable_or_library_files diff --git a/Library/Homebrew/mktemp.rb b/Library/Homebrew/mktemp.rb index 8e6e9b0883..159c0b88df 100644 --- a/Library/Homebrew/mktemp.rb +++ b/Library/Homebrew/mktemp.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true # Performs {Formula#mktemp}'s functionality, and tracks the results. @@ -70,7 +70,7 @@ class Mktemp begin chown(nil, group_id, @tmpdir) rescue Errno::EPERM - opoo "Failed setting group \"#{Etc.getgrgid(group_id).name}\" on #{@tmpdir}" + opoo "Failed setting group \"#{T.must(Etc.getgrgid(group_id)).name}\" on #{@tmpdir}" end begin diff --git a/Library/Homebrew/options.rb b/Library/Homebrew/options.rb index 727a72cfa5..e1877729a0 100644 --- a/Library/Homebrew/options.rb +++ b/Library/Homebrew/options.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true # A formula option. diff --git a/Library/Homebrew/options.rbi b/Library/Homebrew/options.rbi new file mode 100644 index 0000000000..b11d6cd088 --- /dev/null +++ b/Library/Homebrew/options.rbi @@ -0,0 +1,8 @@ +# typed: strict + +class Options + # This is a workaround to enable `alias to_ary to_a` + # @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238 + sig { returns(T::Array[Option]) } + def to_a; end +end diff --git a/Library/Homebrew/os/linux/elf.rb b/Library/Homebrew/os/linux/elf.rb index cdfc602fba..ca7d078b89 100644 --- a/Library/Homebrew/os/linux/elf.rb +++ b/Library/Homebrew/os/linux/elf.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true # {Pathname} extension for dealing with ELF files. diff --git a/Library/Homebrew/os/linux/elf.rbi b/Library/Homebrew/os/linux/elf.rbi new file mode 100644 index 0000000000..2c99175895 --- /dev/null +++ b/Library/Homebrew/os/linux/elf.rbi @@ -0,0 +1,5 @@ +# typed: strict + +module ELFShim + requires_ancestor { Pathname } +end diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index abadcf8fd6..b421103de7 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "macho" diff --git a/Library/Homebrew/os/mac/mach.rbi b/Library/Homebrew/os/mac/mach.rbi new file mode 100644 index 0000000000..54bb844d78 --- /dev/null +++ b/Library/Homebrew/os/mac/mach.rbi @@ -0,0 +1,5 @@ +# typed: strict + +module MachOShim + include Kernel +end diff --git a/Library/Homebrew/os/mac/version.rb b/Library/Homebrew/os/mac/version.rb index fd709f72f2..d407e882ac 100644 --- a/Library/Homebrew/os/mac/version.rb +++ b/Library/Homebrew/os/mac/version.rb @@ -45,7 +45,7 @@ module OS sig { returns(T.self_type) } def strip_patch # Big Sur is 11.x but Catalina is 10.15.x. - if major >= 11 + if T.must(major) >= 11 self.class.new(major.to_s) else major_minor diff --git a/Library/Homebrew/os/mac/version.rbi b/Library/Homebrew/os/mac/version.rbi deleted file mode 100644 index dc7c90848a..0000000000 --- a/Library/Homebrew/os/mac/version.rbi +++ /dev/null @@ -1,10 +0,0 @@ -# typed: strict - -module OS - module Mac - class Version - sig { returns(Token) } - def major; end - end - end -end diff --git a/Library/Homebrew/rubocops/cask/ast/cask_block.rb b/Library/Homebrew/rubocops/cask/ast/cask_block.rb index 4f45f6771c..ac0dca7592 100644 --- a/Library/Homebrew/rubocops/cask/ast/cask_block.rb +++ b/Library/Homebrew/rubocops/cask/ast/cask_block.rb @@ -36,7 +36,15 @@ module RuboCop end def toplevel_stanzas - @toplevel_stanzas ||= stanzas.select(&:toplevel_stanza?) + # If a `cask` block only contains one stanza, it is that stanza's direct parent, + # otherwise stanzas are grouped in a block and `cask` is that block's parent. + is_toplevel_stanza = if cask_body.begin_block? + ->(stanza) { stanza.parent_node.parent.cask_block? } + else + ->(stanza) { stanza.parent_node.cask_block? } + end + + @toplevel_stanzas ||= stanzas.select(&is_toplevel_stanza) end def sorted_toplevel_stanzas diff --git a/Library/Homebrew/rubocops/cask/ast/stanza.rb b/Library/Homebrew/rubocops/cask/ast/stanza.rb index 2088a43655..88badac8cd 100644 --- a/Library/Homebrew/rubocops/cask/ast/stanza.rb +++ b/Library/Homebrew/rubocops/cask/ast/stanza.rb @@ -52,10 +52,6 @@ module RuboCop stanza_group == other.stanza_group end - def toplevel_stanza? - parent_node.cask_block? || parent_node.parent.cask_block? - end - def ==(other) self.class == other.class && stanza_node == other.stanza_node end diff --git a/Library/Homebrew/rubocops/cask/extend/node.rb b/Library/Homebrew/rubocops/cask/extend/node.rb index 20e1e4b7cc..ec79aa4f3f 100644 --- a/Library/Homebrew/rubocops/cask/extend/node.rb +++ b/Library/Homebrew/rubocops/cask/extend/node.rb @@ -17,6 +17,8 @@ module RuboCop def_node_matcher :cask_block?, "(block (send nil? :cask _) args ...)" def_node_matcher :arch_variable?, "(lvasgn _ (send nil? :on_arch_conditional ...))" + def_node_matcher :begin_block?, "(begin ...)" + def stanza? return true if arch_variable? diff --git a/Library/Homebrew/sorbet/rbi/gems/stackprof@0.2.24.rbi b/Library/Homebrew/sorbet/rbi/gems/stackprof@0.2.24.rbi new file mode 100644 index 0000000000..c60dde1735 --- /dev/null +++ b/Library/Homebrew/sorbet/rbi/gems/stackprof@0.2.24.rbi @@ -0,0 +1,88 @@ +# typed: true + +# DO NOT EDIT MANUALLY +# This is an autogenerated file for types exported from the `stackprof` gem. +# Please instead update this file by running `bin/tapioca gem stackprof`. + +module StackProf + class << self + def results(*_arg0); end + def run(*_arg0); end + def running?; end + def sample; end + def start(*_arg0); end + def stop; end + def use_postponed_job!; end + end +end + +class StackProf::Middleware + def initialize(app, options = T.unsafe(nil)); end + + def call(env); end + + class << self + def enabled; end + def enabled=(_arg0); end + def enabled?(env); end + def interval; end + def interval=(_arg0); end + def metadata; end + def metadata=(_arg0); end + def mode; end + def mode=(_arg0); end + def path; end + def path=(_arg0); end + def raw; end + def raw=(_arg0); end + def save; end + end +end + +class StackProf::Report + def initialize(data); end + + def +(other); end + def add_lines(a, b); end + def convert_to_d3_flame_graph_format(name, stacks, depth); end + def data; end + def files; end + def flamegraph_row(f, x, y, weight, addr); end + def flamegraph_stacks(raw); end + def frames(sort_by_total = T.unsafe(nil)); end + def max_samples; end + def modeline; end + def normalized_frames; end + def overall_samples; end + def print_alphabetical_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end + def print_callgrind(f = T.unsafe(nil)); end + def print_d3_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end + def print_debug; end + def print_dump(f = T.unsafe(nil)); end + def print_file(filter, f = T.unsafe(nil)); end + def print_files(sort_by_total = T.unsafe(nil), limit = T.unsafe(nil), f = T.unsafe(nil)); end + def print_flamegraph(f, skip_common, alphabetical = T.unsafe(nil)); end + def print_graphviz(options = T.unsafe(nil), f = T.unsafe(nil)); end + def print_json(f = T.unsafe(nil)); end + def print_method(name, f = T.unsafe(nil)); end + def print_stackcollapse; end + def print_text(sort_by_total = T.unsafe(nil), limit = T.unsafe(nil), select_files = T.unsafe(nil), reject_files = T.unsafe(nil), select_names = T.unsafe(nil), reject_names = T.unsafe(nil), f = T.unsafe(nil)); end + def print_timeline_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end + def version; end + def walk_method(name); end + + private + + def callers_for(addr); end + def root_frames; end + def source_display(f, file, lines, range = T.unsafe(nil)); end + + class << self + def from_file(file); end + def from_json(json); end + def parse_json(json); end + end +end + +StackProf::Report::MARSHAL_SIGNATURE = T.let(T.unsafe(nil), String) +StackProf::VERSION = T.let(T.unsafe(nil), String) diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index e3dcef310c..9f9e78ea77 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -6469,6 +6469,8 @@ end class RuboCop::AST::Node def arch_variable?(param0=T.unsafe(nil)); end + def begin_block?(param0=T.unsafe(nil)); end + def block_args(param0=T.unsafe(nil)); end def block_body(param0=T.unsafe(nil)); end diff --git a/Library/Homebrew/sorbet/rbi/todo.rbi b/Library/Homebrew/sorbet/rbi/todo.rbi index 866476b0d6..73f2bbd607 100644 --- a/Library/Homebrew/sorbet/rbi/todo.rbi +++ b/Library/Homebrew/sorbet/rbi/todo.rbi @@ -4,7 +4,6 @@ # typed: false -module ::StackProf; end module T::InterfaceWrapper::Helpers; end module T::Private::Abstract::Hooks; end module T::Private::Methods::MethodHooks; end diff --git a/Library/Homebrew/sorbet/rbi/upstream.rbi b/Library/Homebrew/sorbet/rbi/upstream.rbi deleted file mode 100644 index dbade1d0dc..0000000000 --- a/Library/Homebrew/sorbet/rbi/upstream.rbi +++ /dev/null @@ -1,23 +0,0 @@ -# typed: strict - -# This file contains temporary definitions for fixes that have -# been submitted upstream to https://github.com/sorbet/sorbet. - -class Module - # https://github.com/sorbet/sorbet/pull/3732 - sig do - params( - arg0: T.any(Symbol, String), - arg1: T.any(Proc, Method, UnboundMethod) - ) - .returns(Symbol) - end - sig do - params( - arg0: T.any(Symbol, String), - blk: T.proc.bind(T.untyped).returns(T.untyped), - ) - .returns(Symbol) - end - def define_method(arg0, arg1=T.unsafe(nil), &blk); end -end diff --git a/Library/Homebrew/system_command.rb b/Library/Homebrew/system_command.rb index e069a4e27f..af201706d4 100644 --- a/Library/Homebrew/system_command.rb +++ b/Library/Homebrew/system_command.rb @@ -7,7 +7,6 @@ require "shellwords" require "extend/io" require "extend/predicable" -require "extend/hash_validator" require "extend/time" diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index 457fe67726..3439ec0c92 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -475,15 +475,15 @@ class Tap contents = [] if (command_count = command_files.count).positive? - contents << "#{command_count} #{Utils.pluralize("command", command_count)}" + contents << Utils.pluralize("command", command_count, include_count: true) end if (cask_count = cask_files.count).positive? - contents << "#{cask_count} #{Utils.pluralize("cask", cask_count)}" + contents << Utils.pluralize("cask", cask_count, include_count: true) end if (formula_count = formula_files.count).positive? - contents << "#{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}" + contents << Utils.pluralize("formula", formula_count, plural: "e", include_count: true) end contents diff --git a/Library/Homebrew/test/cask/cmd/fetch_spec.rb b/Library/Homebrew/test/cask/cmd/fetch_spec.rb deleted file mode 100644 index ad443c43db..0000000000 --- a/Library/Homebrew/test/cask/cmd/fetch_spec.rb +++ /dev/null @@ -1,60 +0,0 @@ -# typed: false -# frozen_string_literal: true - -describe Cask::Cmd::Fetch, :cask do - let(:local_transmission) do - Cask::CaskLoader.load(cask_path("local-transmission")) - end - - let(:local_caffeine) do - Cask::CaskLoader.load(cask_path("local-caffeine")) - end - - it "allows downloading the installer of a Cask" do - transmission_location = CurlDownloadStrategy.new( - local_transmission.url.to_s, local_transmission.token, local_transmission.version, - cache: Cask::Cache.path, **local_transmission.url.specs - ).cached_location - caffeine_location = CurlDownloadStrategy.new( - local_caffeine.url.to_s, local_caffeine.token, local_caffeine.version, - cache: Cask::Cache.path, **local_caffeine.url.specs - ).cached_location - - expect(transmission_location).not_to exist - expect(caffeine_location).not_to exist - - described_class.run("local-transmission", "local-caffeine") - - expect(transmission_location).to exist - expect(caffeine_location).to exist - end - - it "prevents double fetch (without nuking existing installation)" do - cached_location = Cask::Download.new(local_transmission).fetch - - old_ctime = File.stat(cached_location).ctime - - described_class.run("local-transmission", "--no-quarantine") - new_ctime = File.stat(cached_location).ctime - - expect(old_ctime.to_i).to eq(new_ctime.to_i) - end - - it "allows double fetch with --force" do - cached_location = Cask::Download.new(local_transmission).fetch - - old_ctime = File.stat(cached_location).ctime - sleep(1) - - described_class.run("local-transmission", "--force", "--no-quarantine") - new_ctime = File.stat(cached_location).ctime - - expect(new_ctime.to_i).to be > old_ctime.to_i - end - - it "properly handles Casks that are not present" do - expect do - described_class.run("notacask") - end.to raise_error(Cask::CaskUnavailableError) - end -end diff --git a/Library/Homebrew/test/cask/quarantine_spec.rb b/Library/Homebrew/test/cask/quarantine_spec.rb index 7ab7b7f2d5..f14d4bd70b 100644 --- a/Library/Homebrew/test/cask/quarantine_spec.rb +++ b/Library/Homebrew/test/cask/quarantine_spec.rb @@ -2,7 +2,6 @@ # frozen_string_literal: true require "cask/cmd/audit" -require "cask/cmd/fetch" require "cask/cmd/install" require "cask/cask_loader" require "cask/download" @@ -29,7 +28,8 @@ describe Cask::Quarantine, :cask do end it "quarantines Cask fetches" do - Cask::Cmd::Fetch.run("local-transmission") + download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: true) + download.fetch local_transmission = Cask::CaskLoader.load(cask_path("local-transmission")) cached_location = Cask::Download.new(local_transmission).fetch @@ -50,7 +50,8 @@ describe Cask::Quarantine, :cask do end it "quarantines Cask installs even if the fetch was not" do - Cask::Cmd::Fetch.run("local-transmission", "--no-quarantine") + download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: false) + download.fetch Cask::Cmd::Install.run("local-transmission") @@ -144,7 +145,8 @@ describe Cask::Quarantine, :cask do end it "does not quarantine Cask fetches" do - Cask::Cmd::Fetch.run("local-transmission", "--no-quarantine") + download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: false) + download.fetch local_transmission = Cask::CaskLoader.load(cask_path("local-transmission")) cached_location = Cask::Download.new(local_transmission).fetch @@ -165,7 +167,8 @@ describe Cask::Quarantine, :cask do end it "does not quarantine Cask installs even if the fetch was" do - Cask::Cmd::Fetch.run("local-transmission") + download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: true) + download.fetch Cask::Cmd::Install.run("local-transmission", "--no-quarantine") diff --git a/Library/Homebrew/test/caveats_spec.rb b/Library/Homebrew/test/caveats_spec.rb index fe50877834..e8b394683c 100644 --- a/Library/Homebrew/test/caveats_spec.rb +++ b/Library/Homebrew/test/caveats_spec.rb @@ -249,6 +249,32 @@ describe Caveats do expect(caveats).to include("#{f.opt_share}/pkgconfig") end end + + context "when joining different caveat types together" do + let(:f) do + formula do + url "foo-1.0" + keg_only "some reason" + + def caveats + "something else" + end + + service do + run [bin/"cmd"] + end + end + end + + let(:caveats) { described_class.new(f).caveats } + + it "adds the correct amount of new lines to the output" do + expect(caveats).to include("something else") + expect(caveats).to include("keg-only") + expect(caveats).to include("if you don't want/need a background service") + expect(caveats.count("\n")).to eq(9) + end + end end describe "shell completions" do diff --git a/Library/Homebrew/test/utils/analytics_spec.rb b/Library/Homebrew/test/utils/analytics_spec.rb index c78ccf68a5..93b69a7dd3 100644 --- a/Library/Homebrew/test/utils/analytics_spec.rb +++ b/Library/Homebrew/test/utils/analytics_spec.rb @@ -6,7 +6,7 @@ require "formula_installer" describe Utils::Analytics do before do - described_class.clear_additional_tags_cache + described_class.clear_cache end describe "::label_google" do @@ -45,64 +45,68 @@ describe Utils::Analytics do end end - describe "::additional_tags_influx" do + describe "::default_tags_influx" do let(:ci) { ", CI" if ENV["CI"] } it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is a custom prefix on intel" do expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once) - expect(described_class.additional_tags_influx).to have_key(:prefix) - expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + expect(described_class.default_tags_influx).to have_key(:prefix) + expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix" end it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once) - expect(described_class.additional_tags_influx).to have_key(:arch) - expect(described_class.additional_tags_influx[:arch]).to eq HOMEBREW_PHYSICAL_PROCESSOR - expect(described_class.additional_tags_influx).to have_key(:prefix) - expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + expect(described_class.default_tags_influx).to have_key(:arch) + expect(described_class.default_tags_influx[:arch]).to eq HOMEBREW_PHYSICAL_PROCESSOR + expect(described_class.default_tags_influx).to have_key(:prefix) + expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix" end it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once) - expect(described_class.additional_tags_influx).to have_key(:prefix) - expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix" + expect(described_class.default_tags_influx).to have_key(:prefix) + expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix" end it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do expect(Homebrew).to receive(:default_prefix?).and_return(true).at_least(:once) - expect(described_class.additional_tags_influx).to have_key(:prefix) - expect(described_class.additional_tags_influx[:prefix]).to eq HOMEBREW_PREFIX.to_s + expect(described_class.default_tags_influx).to have_key(:prefix) + expect(described_class.default_tags_influx[:prefix]).to eq HOMEBREW_PREFIX.to_s end it "includes CI when ENV['CI'] is set" do ENV["CI"] = "1" - expect(described_class.additional_tags_influx).to have_key(:ci) + expect(described_class.default_tags_influx).to have_key(:ci) end it "includes developer when ENV['HOMEBREW_DEVELOPER'] is set" do expect(Homebrew::EnvConfig).to receive(:developer?).and_return(true) - expect(described_class.additional_tags_influx).to have_key(:developer) + expect(described_class.default_tags_influx).to have_key(:developer) end end describe "::report_event" do let(:f) { formula { url "foo-1.0" } } - let(:options) { ["--head"].join } - let(:action) { "#{f.full_name} #{options}".strip } + let(:package_name) { f.name } + let(:tap_name) { f.tap.name } + let(:on_request) { false } + let(:options) { "--HEAD" } context "when ENV vars is set" do it "returns nil when HOMEBREW_NO_ANALYTICS is true" do ENV["HOMEBREW_NO_ANALYTICS"] = "true" expect(described_class).not_to receive(:report_google) expect(described_class).not_to receive(:report_influx) - described_class.report_event(:install, action) + described_class.report_event(:install, package_name: package_name, tap_name: tap_name, + on_request: on_request, options: options) end it "returns nil when HOMEBREW_NO_ANALYTICS_THIS_RUN is true" do ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "true" expect(described_class).not_to receive(:report_google) expect(described_class).not_to receive(:report_influx) - described_class.report_event(:install, action) + described_class.report_event(:install, package_name: package_name, tap_name: tap_name, + on_request: on_request, options: options) end it "returns nil when HOMEBREW_ANALYTICS_DEBUG is true" do @@ -112,7 +116,8 @@ describe Utils::Analytics do expect(described_class).to receive(:report_google) expect(described_class).to receive(:report_influx) - described_class.report_event(:install, action) + described_class.report_event(:install, package_name: package_name, tap_name: tap_name, + on_request: on_request, options: options) end end @@ -120,40 +125,47 @@ describe Utils::Analytics do ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") ENV.delete("HOMEBREW_NO_ANALYTICS") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" - expect(Homebrew::EnvConfig).to receive(:developer?).and_return(false) - expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action)).once - expect(described_class).to receive(:report_influx).with(:install, "formula_name --head", false, - hash_including(developer: false)).once - described_class.report_event(:install, action) + expect(described_class).to receive(:report_google).with(:event, + hash_including(ea: "#{package_name} #{options}")).once + expect(described_class).to receive(:report_influx).with(:install, hash_including(package_name: package_name, + on_request: on_request)).once + described_class.report_event(:install, package_name: package_name, tap_name: tap_name, + on_request: on_request, options: options) end it "sends to google twice on request" do ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") ENV.delete("HOMEBREW_NO_ANALYTICS") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" - expect(Homebrew::EnvConfig).to receive(:developer?).and_return(false) - expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action, ec: :install)).once expect(described_class).to receive(:report_google).with(:event, - hash_including(ea: action, + hash_including(ea: "#{package_name} #{options}", + ec: :install)).once + expect(described_class).to receive(:report_google).with(:event, + hash_including(ea: "#{package_name} #{options}", ec: :install_on_request)).once - expect(described_class).to receive(:report_influx).with(:install, "formula_name --head", true, - hash_including(developer: false)).once + expect(described_class).to receive(:report_influx).with(:install, + hash_including(package_name: package_name, + on_request: true)).once - described_class.report_event(:install, action, on_request: true) + described_class.report_event(:install, package_name: package_name, tap_name: tap_name, + on_request: true, options: options) end end describe "::report_influx" do let(:f) { formula { url "foo-1.0" } } - let(:options) { ["--head"].join } - let(:action) { "#{f.full_name} #{options}".strip } + let(:package_name) { f.name } + let(:tap_name) { f.tap.name } + let(:on_request) { false } + let(:options) { "--HEAD" } it "outputs in debug mode" do ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") ENV.delete("HOMEBREW_NO_ANALYTICS") ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" expect(described_class).to receive(:deferred_curl).once - described_class.report_influx(:install, action, true, developer: true, CI: true) + described_class.report_influx(:install, package_name: package_name, tap_name: tap_name, on_request: on_request, +options: options) end end diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb index 8b9b5e89e8..744014ad94 100644 --- a/Library/Homebrew/test/utils_spec.rb +++ b/Library/Homebrew/test/utils_spec.rb @@ -70,6 +70,12 @@ describe Utils do expect(described_class.pluralize("foo", 1, singular: "o", plural: "es")).to eq("fooo") expect(described_class.pluralize("foo", 2, singular: "o", plural: "es")).to eq("fooes") end + + it "includes the count when requested" do + expect(described_class.pluralize("foo", 0, include_count: true)).to eq("0 foos") + expect(described_class.pluralize("foo", 1, include_count: true)).to eq("1 foo") + expect(described_class.pluralize("foo", 2, include_count: true)).to eq("2 foos") + end end describe ".underscore" do diff --git a/Library/Homebrew/unpack_strategy.rb b/Library/Homebrew/unpack_strategy.rb index b7572d18e7..8a29604b93 100644 --- a/Library/Homebrew/unpack_strategy.rb +++ b/Library/Homebrew/unpack_strategy.rb @@ -1,29 +1,8 @@ -# typed: false +# typed: true # frozen_string_literal: true require "system_command" -# Helper module for iterating over directory trees. -# -# @api private -module PathnameEachDirectory - refine Pathname do - extend T::Sig - - sig { - type_parameters(:T) - .params( - _block: T.proc.params(path: Pathname).returns(T.type_parameter(:T)), - ).returns(T.type_parameter(:T)) - } - def each_directory(&_block) - find do |path| - yield path if path.directory? - end - end - end -end - # Module containing all available strategies for unpacking archives. # # @api private @@ -33,38 +12,6 @@ module UnpackStrategy include SystemCommand::Mixin - using PathnameEachDirectory - - # Helper module for identifying the file type. - module Magic - # Length of the longest regex (currently Tar). - MAX_MAGIC_NUMBER_LENGTH = 262 - private_constant :MAX_MAGIC_NUMBER_LENGTH - - refine Pathname do - def magic_number - @magic_number ||= if directory? - "" - else - binread(MAX_MAGIC_NUMBER_LENGTH) || "" - end - end - - def file_type - @file_type ||= system_command("file", args: ["-b", self], print_stderr: false) - .stdout.chomp - end - - def zipinfo - @zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false) - .stdout - .encode(Encoding::UTF_8, invalid: :replace) - .split("\n") - end - end - end - private_constant :Magic - def self.strategies @strategies ||= [ Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd. @@ -195,7 +142,7 @@ module UnpackStrategy end # Ensure all extracted directories are writable. - tmp_unpack_dir.each_directory do |path| + each_directory(tmp_unpack_dir) do |path| next if path.writable? FileUtils.chmod "u+w", path, verbose: verbose @@ -208,6 +155,19 @@ module UnpackStrategy def dependencies [] end + + # Helper method for iterating over directory trees. + sig { + params( + pathname: Pathname, + _block: T.proc.params(path: Pathname).void, + ).returns(T.nilable(Pathname)) + } + def each_directory(pathname, &_block) + pathname.find do |path| + yield path if path.directory? + end + end end require "unpack_strategy/air" diff --git a/Library/Homebrew/unpack_strategy.rbi b/Library/Homebrew/unpack_strategy.rbi index 55284f6668..d7550c56b2 100644 --- a/Library/Homebrew/unpack_strategy.rbi +++ b/Library/Homebrew/unpack_strategy.rbi @@ -3,14 +3,3 @@ module UnpackStrategy include Kernel end - -class Pathname - sig { returns(String) } - def magic_number; end - - sig { returns(String) } - def file_type; end - - sig { returns(T::Array[String]) } - def zipinfo; end -end diff --git a/Library/Homebrew/unpack_strategy/air.rb b/Library/Homebrew/unpack_strategy/air.rb index 9f8bd8c473..aac0881fa8 100644 --- a/Library/Homebrew/unpack_strategy/air.rb +++ b/Library/Homebrew/unpack_strategy/air.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".air"] diff --git a/Library/Homebrew/unpack_strategy/bazaar.rb b/Library/Homebrew/unpack_strategy/bazaar.rb index 12fbd503d0..62eb0c4946 100644 --- a/Library/Homebrew/unpack_strategy/bazaar.rb +++ b/Library/Homebrew/unpack_strategy/bazaar.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Bazaar < Directory extend T::Sig - using Magic - def self.can_extract?(path) super && (path/".bzr").directory? end diff --git a/Library/Homebrew/unpack_strategy/bzip2.rb b/Library/Homebrew/unpack_strategy/bzip2.rb index 6b05e4dc15..82e9501b6a 100644 --- a/Library/Homebrew/unpack_strategy/bzip2.rb +++ b/Library/Homebrew/unpack_strategy/bzip2.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".bz2"] diff --git a/Library/Homebrew/unpack_strategy/cab.rb b/Library/Homebrew/unpack_strategy/cab.rb index 82265231d5..de2258af88 100644 --- a/Library/Homebrew/unpack_strategy/cab.rb +++ b/Library/Homebrew/unpack_strategy/cab.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".cab"] diff --git a/Library/Homebrew/unpack_strategy/compress.rb b/Library/Homebrew/unpack_strategy/compress.rb index 65f9ea8728..0cc11abbd7 100644 --- a/Library/Homebrew/unpack_strategy/compress.rb +++ b/Library/Homebrew/unpack_strategy/compress.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Compress < Tar extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".Z"] diff --git a/Library/Homebrew/unpack_strategy/cvs.rb b/Library/Homebrew/unpack_strategy/cvs.rb index 66c2f17f8f..af6f0a3a77 100644 --- a/Library/Homebrew/unpack_strategy/cvs.rb +++ b/Library/Homebrew/unpack_strategy/cvs.rb @@ -6,8 +6,6 @@ require_relative "directory" module UnpackStrategy # Strategy for unpacking CVS repositories. class Cvs < Directory - using Magic - def self.can_extract?(path) super && (path/"CVS").directory? end diff --git a/Library/Homebrew/unpack_strategy/directory.rb b/Library/Homebrew/unpack_strategy/directory.rb index 02772dcc68..f20b9a566e 100644 --- a/Library/Homebrew/unpack_strategy/directory.rb +++ b/Library/Homebrew/unpack_strategy/directory.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [] diff --git a/Library/Homebrew/unpack_strategy/dmg.rb b/Library/Homebrew/unpack_strategy/dmg.rb index ffc13ae44f..cb3ee47d12 100644 --- a/Library/Homebrew/unpack_strategy/dmg.rb +++ b/Library/Homebrew/unpack_strategy/dmg.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "tempfile" @@ -12,6 +12,8 @@ module UnpackStrategy # Helper module for listing the contents of a volume mounted from a disk image. module Bom + extend T::Sig + DMG_METADATA = Set.new(%w[ .background .com.apple.timemachine.donotpresent @@ -35,117 +37,107 @@ module UnpackStrategy end end - refine Pathname do - extend T::Sig + # Check if path is considered disk image metadata. + sig { params(pathname: Pathname).returns(T::Boolean) } + def self.dmg_metadata?(pathname) + DMG_METADATA.include?(pathname.cleanpath.ascend.to_a.last.to_s) + end - # Check if path is considered disk image metadata. - sig { returns(T::Boolean) } - def dmg_metadata? - DMG_METADATA.include?(cleanpath.ascend.to_a.last.to_s) + # Check if path is a symlink to a system directory (commonly to /Applications). + sig { params(pathname: Pathname).returns(T::Boolean) } + def self.system_dir_symlink?(pathname) + pathname.symlink? && MacOS.system_dir?(pathname.dirname.join(pathname.readlink)) + end + + sig { params(pathname: Pathname).returns(String) } + def self.bom(pathname) + tries = 0 + result = loop do + # We need to use `find` here instead of Ruby in order to properly handle + # file names containing special characters, such as “e” + “´” vs. “é”. + r = system_command("find", args: [".", "-print0"], chdir: pathname, print_stderr: false) + tries += 1 + + # Spurious bug on CI, which in most cases can be worked around by retrying. + break r unless r.stderr.match?(/Interrupted system call/i) + + raise "Command `#{r.command.shelljoin}` was interrupted." if tries >= 3 end - # Check if path is a symlink to a system directory (commonly to /Applications). - sig { returns(T::Boolean) } - def system_dir_symlink? - symlink? && MacOS.system_dir?(dirname.join(readlink)) - end + odebug "Command `#{result.command.shelljoin}` in '#{pathname}' took #{tries} tries." if tries > 1 - sig { returns(String) } - def bom - tries = 0 - result = loop do - # We need to use `find` here instead of Ruby in order to properly handle - # file names containing special characters, such as “e” + “´” vs. “é”. - r = system_command("find", args: [".", "-print0"], chdir: self, print_stderr: false) - tries += 1 + bom_paths = result.stdout.split("\0") - # Spurious bug on CI, which in most cases can be worked around by retrying. - break r unless r.stderr.match?(/Interrupted system call/i) + raise EmptyError, pathname if bom_paths.empty? - raise "Command `#{r.command.shelljoin}` was interrupted." if tries >= 3 - end - - odebug "Command `#{result.command.shelljoin}` in '#{self}' took #{tries} tries." if tries > 1 - - bom_paths = result.stdout.split("\0") - - raise EmptyError, self if bom_paths.empty? - - bom_paths - .reject { |path| Pathname(path).dmg_metadata? } - .reject { |path| (self/path).system_dir_symlink? } - .join("\n") - end + bom_paths + .reject { |path| dmg_metadata?(Pathname(path)) } + .reject { |path| system_dir_symlink?(pathname/path) } + .join("\n") end end - private_constant :Bom # Strategy for unpacking a volume mounted from a disk image. class Mount extend T::Sig - using Bom include UnpackStrategy def eject(verbose: false) - tries ||= 3 + tries = 3 + begin + return unless path.exist? - return unless path.exist? + if tries > 1 + disk_info = system_command!( + "diskutil", + args: ["info", "-plist", path], + print_stderr: false, + verbose: verbose, + ) - if tries > 1 - disk_info = system_command!( - "diskutil", - args: ["info", "-plist", path], - print_stderr: false, - verbose: verbose, - ) + # For HFS, just use + # For APFS, find the corresponding to + eject_paths = disk_info.plist + .fetch("APFSPhysicalStores", []) + .map { |store| store["APFSPhysicalStore"] } + .compact + .presence || [path] - # For HFS, just use - # For APFS, find the corresponding to - eject_paths = disk_info.plist - .fetch("APFSPhysicalStores", []) - .map { |store| store["APFSPhysicalStore"] } - .compact - .presence || [path] - - eject_paths.each do |eject_path| + eject_paths.each do |eject_path| + system_command! "diskutil", + args: ["eject", eject_path], + print_stderr: false, + verbose: verbose + end + else system_command! "diskutil", - args: ["eject", eject_path], + args: ["unmount", "force", path], print_stderr: false, verbose: verbose end - else - system_command! "diskutil", - args: ["unmount", "force", path], - print_stderr: false, - verbose: verbose - end - rescue ErrorDuringExecution => e - raise e if (tries -= 1).zero? + rescue ErrorDuringExecution => e + raise e if (tries -= 1).zero? - sleep 1 - retry + sleep 1 + retry + end end private sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) } def extract_to_dir(unpack_dir, basename:, verbose:) + tries = 3 bom = begin - tries ||= 10 - - path.bom + Bom.bom(path) rescue Bom::EmptyError => e - raise "#{e} No retries left." if (tries -= 1).zero? + raise e if (tries -= 1).zero? sleep 1 retry end - # TODO: Remove this if we actually ever hit this, i.e. if we actually found - # some files after waiting longer for the DMG to be mounted. - raise "BOM for path '#{path}' was empty but retrying for #{10 - tries} seconds helped." if tries != 10 - Tempfile.open(["", ".bom"]) do |bomfile| bomfile.close diff --git a/Library/Homebrew/unpack_strategy/executable.rb b/Library/Homebrew/unpack_strategy/executable.rb index 1daeb7d0f7..6bf45599ed 100644 --- a/Library/Homebrew/unpack_strategy/executable.rb +++ b/Library/Homebrew/unpack_strategy/executable.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Executable < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".sh", ".bash"] diff --git a/Library/Homebrew/unpack_strategy/fossil.rb b/Library/Homebrew/unpack_strategy/fossil.rb index ddaf147795..a09218cf77 100644 --- a/Library/Homebrew/unpack_strategy/fossil.rb +++ b/Library/Homebrew/unpack_strategy/fossil.rb @@ -11,8 +11,6 @@ module UnpackStrategy include UnpackStrategy extend SystemCommand::Mixin - using Magic - sig { returns(T::Array[String]) } def self.extensions [] diff --git a/Library/Homebrew/unpack_strategy/generic_unar.rb b/Library/Homebrew/unpack_strategy/generic_unar.rb index 0322d5c9ef..a860f8ec6b 100644 --- a/Library/Homebrew/unpack_strategy/generic_unar.rb +++ b/Library/Homebrew/unpack_strategy/generic_unar.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [] diff --git a/Library/Homebrew/unpack_strategy/git.rb b/Library/Homebrew/unpack_strategy/git.rb index 23257c0502..ab39f3c4e0 100644 --- a/Library/Homebrew/unpack_strategy/git.rb +++ b/Library/Homebrew/unpack_strategy/git.rb @@ -6,8 +6,6 @@ require_relative "directory" module UnpackStrategy # Strategy for unpacking Git repositories. class Git < Directory - using Magic - def self.can_extract?(path) super && (path/".git").directory? end diff --git a/Library/Homebrew/unpack_strategy/gzip.rb b/Library/Homebrew/unpack_strategy/gzip.rb index 983d995a98..dbc70d2766 100644 --- a/Library/Homebrew/unpack_strategy/gzip.rb +++ b/Library/Homebrew/unpack_strategy/gzip.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".gz"] diff --git a/Library/Homebrew/unpack_strategy/jar.rb b/Library/Homebrew/unpack_strategy/jar.rb index d4170a8689..21153d1c2b 100644 --- a/Library/Homebrew/unpack_strategy/jar.rb +++ b/Library/Homebrew/unpack_strategy/jar.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Jar < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".apk", ".jar"] diff --git a/Library/Homebrew/unpack_strategy/lha.rb b/Library/Homebrew/unpack_strategy/lha.rb index 543f4e4540..a8cdfdb4cf 100644 --- a/Library/Homebrew/unpack_strategy/lha.rb +++ b/Library/Homebrew/unpack_strategy/lha.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".lha", ".lzh"] diff --git a/Library/Homebrew/unpack_strategy/lua_rock.rb b/Library/Homebrew/unpack_strategy/lua_rock.rb index 6150e2788e..d5ce7de33e 100644 --- a/Library/Homebrew/unpack_strategy/lua_rock.rb +++ b/Library/Homebrew/unpack_strategy/lua_rock.rb @@ -8,8 +8,6 @@ module UnpackStrategy class LuaRock < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".rock"] diff --git a/Library/Homebrew/unpack_strategy/lzip.rb b/Library/Homebrew/unpack_strategy/lzip.rb index da8a991b9e..d1f2c432af 100644 --- a/Library/Homebrew/unpack_strategy/lzip.rb +++ b/Library/Homebrew/unpack_strategy/lzip.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".lz"] diff --git a/Library/Homebrew/unpack_strategy/lzma.rb b/Library/Homebrew/unpack_strategy/lzma.rb index 7ef147b61b..bfe04be2c9 100644 --- a/Library/Homebrew/unpack_strategy/lzma.rb +++ b/Library/Homebrew/unpack_strategy/lzma.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".lzma"] diff --git a/Library/Homebrew/unpack_strategy/mercurial.rb b/Library/Homebrew/unpack_strategy/mercurial.rb index cc27a11395..72d9547933 100644 --- a/Library/Homebrew/unpack_strategy/mercurial.rb +++ b/Library/Homebrew/unpack_strategy/mercurial.rb @@ -6,8 +6,6 @@ require_relative "directory" module UnpackStrategy # Strategy for unpacking Mercurial repositories. class Mercurial < Directory - using Magic - def self.can_extract?(path) super && (path/".hg").directory? end diff --git a/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb b/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb index 616d78bc06..ad3940d86e 100644 --- a/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb +++ b/Library/Homebrew/unpack_strategy/microsoft_office_xml.rb @@ -8,8 +8,6 @@ module UnpackStrategy class MicrosoftOfficeXml < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [ diff --git a/Library/Homebrew/unpack_strategy/otf.rb b/Library/Homebrew/unpack_strategy/otf.rb index e94eb15029..61ac40a249 100644 --- a/Library/Homebrew/unpack_strategy/otf.rb +++ b/Library/Homebrew/unpack_strategy/otf.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Otf < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".otf"] diff --git a/Library/Homebrew/unpack_strategy/p7zip.rb b/Library/Homebrew/unpack_strategy/p7zip.rb index 618ea06f23..715c816076 100644 --- a/Library/Homebrew/unpack_strategy/p7zip.rb +++ b/Library/Homebrew/unpack_strategy/p7zip.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".7z"] diff --git a/Library/Homebrew/unpack_strategy/pax.rb b/Library/Homebrew/unpack_strategy/pax.rb index 4067132c80..c454b57ab4 100644 --- a/Library/Homebrew/unpack_strategy/pax.rb +++ b/Library/Homebrew/unpack_strategy/pax.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".pax"] diff --git a/Library/Homebrew/unpack_strategy/pkg.rb b/Library/Homebrew/unpack_strategy/pkg.rb index 220b86a58c..7151ba6078 100644 --- a/Library/Homebrew/unpack_strategy/pkg.rb +++ b/Library/Homebrew/unpack_strategy/pkg.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Pkg < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".pkg", ".mkpg"] diff --git a/Library/Homebrew/unpack_strategy/rar.rb b/Library/Homebrew/unpack_strategy/rar.rb index bd2b8042bb..9a2be11326 100644 --- a/Library/Homebrew/unpack_strategy/rar.rb +++ b/Library/Homebrew/unpack_strategy/rar.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".rar"] diff --git a/Library/Homebrew/unpack_strategy/self_extracting_executable.rb b/Library/Homebrew/unpack_strategy/self_extracting_executable.rb index 0e99278711..a3ea1e8de1 100644 --- a/Library/Homebrew/unpack_strategy/self_extracting_executable.rb +++ b/Library/Homebrew/unpack_strategy/self_extracting_executable.rb @@ -8,8 +8,6 @@ module UnpackStrategy class SelfExtractingExecutable < GenericUnar extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [] diff --git a/Library/Homebrew/unpack_strategy/sit.rb b/Library/Homebrew/unpack_strategy/sit.rb index 687e7d0e64..d747cb3178 100644 --- a/Library/Homebrew/unpack_strategy/sit.rb +++ b/Library/Homebrew/unpack_strategy/sit.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Sit < GenericUnar extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".sit"] diff --git a/Library/Homebrew/unpack_strategy/subversion.rb b/Library/Homebrew/unpack_strategy/subversion.rb index dd3355825b..4097f13610 100644 --- a/Library/Homebrew/unpack_strategy/subversion.rb +++ b/Library/Homebrew/unpack_strategy/subversion.rb @@ -6,8 +6,6 @@ require_relative "directory" module UnpackStrategy # Strategy for unpacking Subversion repositories. class Subversion < Directory - using Magic - def self.can_extract?(path) super && (path/".svn").directory? end diff --git a/Library/Homebrew/unpack_strategy/tar.rb b/Library/Homebrew/unpack_strategy/tar.rb index 9b0797cfa1..42ca323d33 100644 --- a/Library/Homebrew/unpack_strategy/tar.rb +++ b/Library/Homebrew/unpack_strategy/tar.rb @@ -11,8 +11,6 @@ module UnpackStrategy include UnpackStrategy extend SystemCommand::Mixin - using Magic - sig { returns(T::Array[String]) } def self.extensions [ diff --git a/Library/Homebrew/unpack_strategy/ttf.rb b/Library/Homebrew/unpack_strategy/ttf.rb index 38b44879de..bd0da5ea76 100644 --- a/Library/Homebrew/unpack_strategy/ttf.rb +++ b/Library/Homebrew/unpack_strategy/ttf.rb @@ -8,8 +8,6 @@ module UnpackStrategy class Ttf < Uncompressed extend T::Sig - using Magic - sig { returns(T::Array[String]) } def self.extensions [".ttc", ".ttf"] diff --git a/Library/Homebrew/unpack_strategy/xar.rb b/Library/Homebrew/unpack_strategy/xar.rb index ce35a0683a..4707a602d5 100644 --- a/Library/Homebrew/unpack_strategy/xar.rb +++ b/Library/Homebrew/unpack_strategy/xar.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".xar"] diff --git a/Library/Homebrew/unpack_strategy/xz.rb b/Library/Homebrew/unpack_strategy/xz.rb index 61e1b2c3ab..cc450d51d6 100644 --- a/Library/Homebrew/unpack_strategy/xz.rb +++ b/Library/Homebrew/unpack_strategy/xz.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".xz"] diff --git a/Library/Homebrew/unpack_strategy/zip.rb b/Library/Homebrew/unpack_strategy/zip.rb index c8925c96c6..85ef33cbbb 100644 --- a/Library/Homebrew/unpack_strategy/zip.rb +++ b/Library/Homebrew/unpack_strategy/zip.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".zip"] diff --git a/Library/Homebrew/unpack_strategy/zstd.rb b/Library/Homebrew/unpack_strategy/zstd.rb index 4e77184977..c5f0b2a6b9 100644 --- a/Library/Homebrew/unpack_strategy/zstd.rb +++ b/Library/Homebrew/unpack_strategy/zstd.rb @@ -8,8 +8,6 @@ module UnpackStrategy include UnpackStrategy - using Magic - sig { returns(T::Array[String]) } def self.extensions [".zst"] diff --git a/Library/Homebrew/upgrade.rb b/Library/Homebrew/upgrade.rb index 83fe6329e2..314adea386 100644 --- a/Library/Homebrew/upgrade.rb +++ b/Library/Homebrew/upgrade.rb @@ -310,10 +310,10 @@ module Homebrew if upgradeable_dependents.blank? ohai "No outdated dependents to upgrade!" unless dry_run else - dependent_plural = Utils.pluralize("dependent", upgradeable_dependents.count) formula_plural = Utils.pluralize("formula", installed_formulae.count, plural: "e") upgrade_verb = dry_run ? "Would upgrade" : "Upgrading" - ohai "#{upgrade_verb} #{upgradeable_dependents.count} #{dependent_plural} of upgraded #{formula_plural}:" + ohai "#{upgrade_verb} #{Utils.pluralize("dependent", upgradeable_dependents.count, + include_count: true)} of upgraded #{formula_plural}:" Upgrade.puts_no_installed_dependents_check_disable_message_if_not_already! formulae_upgrades = upgradeable_dependents.map do |f| name = f.full_specified_name @@ -386,9 +386,8 @@ module Homebrew if reinstallable_broken_dependents.blank? ohai "No broken dependents to reinstall!" else - count = reinstallable_broken_dependents.count - plural = Utils.pluralize("dependent", reinstallable_broken_dependents.count) - ohai "Reinstalling #{count} #{plural} with broken linkage from source:" + ohai "Reinstalling #{Utils.pluralize("dependent", reinstallable_broken_dependents.count, + include_count: true)} with broken linkage from source:" Upgrade.puts_no_installed_dependents_check_disable_message_if_not_already! puts reinstallable_broken_dependents.map(&:full_specified_name) .join(", ") diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index a0bf5fdcec..a620e38c69 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -126,10 +126,14 @@ module Utils # A lightweight alternative to `ActiveSupport::Inflector.pluralize`: # Combines `stem` with the `singular` or `plural` suffix based on `count`. - sig { params(stem: String, count: Integer, plural: String, singular: String).returns(String) } - def self.pluralize(stem, count, plural: "s", singular: "") + # Adds a prefix of the count value if `include_count` is set to true. + sig { + params(stem: String, count: Integer, plural: String, singular: String, include_count: T::Boolean).returns(String) + } + def self.pluralize(stem, count, plural: "s", singular: "", include_count: false) + prefix = include_count ? "#{count} " : "" suffix = (count == 1) ? singular : plural - "#{stem}#{suffix}" + "#{prefix}#{stem}#{suffix}" end sig { params(author: String).returns({ email: String, name: String }) } diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index edaaf7662b..4ce8599335 100644 --- a/Library/Homebrew/utils/analytics.rb +++ b/Library/Homebrew/utils/analytics.rb @@ -12,7 +12,7 @@ module Utils # @api private module Analytics INFLUX_BUCKET = "analytics" - INFLUX_TOKEN = "9eMkCRwRWS7xjPR_HbF5tBffKmnyRFSup7rq41tHZLOpnBsjVtRFd-y9R_P9OCcB3kr1ftDEzxcxTehcufy1SQ==" + INFLUX_TOKEN = "sSE5_ENBUUhuh3vL3QDi6Rqo96DDZznBYoBT_TEdYnjj8IH2H_1PQD2qkAP0nnSwEIKvfQvW3Sb24GWYT35jqg==" INFLUX_HOST = "https://europe-west1-1.gcp.cloud2.influxdata.com" INFLUX_ORG = "9a707721bb47fc02" @@ -72,27 +72,36 @@ module Utils end sig { - params(measurement: Symbol, package_and_options: String, on_request: T::Boolean, - additional_tags: T::Hash[Symbol, T.untyped]).void + params(measurement: Symbol, package_name: String, tap_name: String, on_request: T::Boolean, + options: String).void } - def report_influx(measurement, package_and_options, on_request, additional_tags = {}) - # convert on_request to a boolean + def report_influx(measurement, package_name:, tap_name:, on_request:, options:) + # ensure on_request is a boolean on_request = on_request ? true : false - # Append general information to device information - tags = additional_tags.merge(package_and_options: package_and_options, on_request: on_request) - .compact - .map { |k, v| "#{k}=#{v.to_s.sub(" ", "\\ ")}" } # convert to key/value parameters - .join(",") + # ensure options are removed (by `.compact` below) if empty + options = nil if options.blank? + + # Tags are always implicitly strings and must have low cardinality. + tags = default_tags_influx.merge(on_request: on_request) + .map { |k, v| "#{k}=#{v}" } + .join(",") + + # Fields need explicitly wrapped with quotes and can have high cardinality. + fields = default_fields_influx.merge(package: package_name, tap_name: tap_name, options: options) + .compact + .map { |k, v| %Q(#{k}="#{v}") } + .join(",") args = [ "--max-time", "3", + "--header", "Authorization: Token #{INFLUX_TOKEN}", "--header", "Content-Type: text/plain; charset=utf-8", "--header", "Accept: application/json", - "--header", "Authorization: Token #{INFLUX_TOKEN}", - "--data-raw", "#{measurement},#{tags} count=1i #{Time.now.to_i}" + "--data-binary", "#{measurement},#{tags} #{fields} #{Time.now.to_i}" ] + # Second precision is highest we can do and has the lowest performance cost. url = "#{INFLUX_HOST}/api/v2/write?bucket=#{INFLUX_BUCKET}&precision=s" deferred_curl(url, args) end @@ -111,10 +120,20 @@ module Utils end end - sig { params(measurement: Symbol, package_and_options: String, on_request: T::Boolean).void } - def report_event(measurement, package_and_options, on_request: false) + sig { + params(measurement: Symbol, package_name: String, tap_name: String, + on_request: T::Boolean, options: String).void + } + def report_event(measurement, package_name:, tap_name:, on_request:, options: "") + report_influx_event(measurement, package_name: package_name, tap_name: tap_name, on_request: on_request, +options: options) + + package_and_options = package_name + if tap_name.present? && tap_name != "homebrew/core" && tap_name != "homebrew/cask" + package_and_options = "#{tap_name}/#{package_and_options}" + end + package_and_options = "#{package_and_options} #{options}" if options.present? report_google_event(measurement, package_and_options, on_request: on_request) - report_influx_event(measurement, package_and_options, on_request: on_request) end sig { params(category: Symbol, action: String, on_request: T::Boolean).void } @@ -138,11 +157,15 @@ module Utils ev: nil) end - sig { params(measurement: Symbol, package_and_options: String, on_request: T::Boolean).void } - def report_influx_event(measurement, package_and_options, on_request: false) + sig { + params(measurement: Symbol, package_name: String, tap_name: String, on_request: T::Boolean, + options: String).void + } + def report_influx_event(measurement, package_name:, tap_name:, on_request: false, options: "") return if not_this_run? || disabled? - report_influx(measurement, package_and_options, on_request, additional_tags_influx) + report_influx(measurement, package_name: package_name, tap_name: tap_name, on_request: on_request, +options: options) end sig { params(exception: BuildError).void } @@ -171,16 +194,15 @@ module Utils def report_influx_error(exception) return if not_this_run? || disabled? - return unless exception.formula.tap - return unless exception.formula.tap.should_report_analytics? + formula = exception.formula + return unless formula - formula_full_name = exception.formula.full_name - package_and_options = if (options = exception.options.to_a.map(&:to_s).join(" ").presence) - "#{formula_full_name} #{options}".strip - else - formula_full_name - end - report_influx_event(:build_error, package_and_options) + tap = formula.tap + return unless tap + return unless tap.should_report_analytics? + + options = exception.options.to_a.map(&:to_s).join(" ") + report_influx_event(:build_error, package_name: formula.name, tap_name: tap.name, options: options) end def messages_displayed? @@ -322,9 +344,10 @@ module Utils end alias generic_arch_label_google arch_label_google - def clear_additional_tags_cache + def clear_cache remove_instance_variable(:@label_google) if instance_variable_defined?(:@label_google) - remove_instance_variable(:@additional_tags_influx) if instance_variable_defined?(:@additional_tags_influx) + remove_instance_variable(:@default_tags_influx) if instance_variable_defined?(:@default_tags_influx) + remove_instance_variable(:@default_fields_influx) if instance_variable_defined?(:@default_fields_influx) end sig { returns(String) } @@ -339,25 +362,40 @@ module Utils end sig { returns(T::Hash[Symbol, String]) } - def additional_tags_influx - @additional_tags_influx ||= begin - version = HOMEBREW_VERSION.match(/^[\d.]+/)[0] - version = "#{version}-dev" if HOMEBREW_VERSION.include?("-") + def default_tags_influx + @default_tags_influx ||= begin + # Only display default prefixes to reduce cardinality and improve privacy prefix = Homebrew.default_prefix? ? HOMEBREW_PREFIX.to_s : "custom-prefix" - # Cleanup quotes and patch/patchset versions to reduce cardinality. - os_version = OS_VERSION.tr('"', "") - .gsub(/(\d+\.\d+)(\.\d+)?(-\d)?/, '\1') + # Tags are always strings and must have low cardinality. + { + ci: ENV["CI"].present?, + prefix: prefix, + default_prefix: Homebrew.default_prefix?, + developer: Homebrew::EnvConfig.developer?, + devcmdrun: config_true?(:devcmdrun), + arch: HOMEBREW_PHYSICAL_PROCESSOR, + os: HOMEBREW_SYSTEM, + } + end + end + + # remove os_version starting with " or number + # remove macOS patch release + sig { returns(T::Hash[Symbol, String]) } + def default_fields_influx + @default_fields_influx ||= begin + version = HOMEBREW_VERSION.match(/^[\d.]+/)[0] + version = "#{version}-dev" if HOMEBREW_VERSION.include?("-") + + # Only include OS versions with an actual name. + os_name_and_version = if (os_version = OS_VERSION.presence) && os_version.downcase.match?(/^[a-z]/) + os_version + end { version: version, - prefix: prefix, - default_prefix: Homebrew.default_prefix?, - ci: ENV["CI"].present?, - developer: Homebrew::EnvConfig.developer?, - arch: HOMEBREW_PHYSICAL_PROCESSOR, - os: HOMEBREW_SYSTEM, - os_name_and_version: os_version, + os_name_and_version: os_name_and_version, } end end diff --git a/Library/Homebrew/utils/github/actions.rb b/Library/Homebrew/utils/github/actions.rb index c255dec4a0..ed27283bb3 100644 --- a/Library/Homebrew/utils/github/actions.rb +++ b/Library/Homebrew/utils/github/actions.rb @@ -1,6 +1,7 @@ # typed: true # frozen_string_literal: true +require "securerandom" require "utils/tty" module GitHub @@ -18,6 +19,24 @@ module GitHub .gsub("\r", "%0D") end + sig { params(name: String, value: String).returns(String) } + def self.format_multiline_string(name, value) + # Format multiline strings for environment files + # See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings + + delimiter = "ghadelimiter_#{SecureRandom.uuid}" + + if name.include?(delimiter) || value.include?(delimiter) + raise "`name` and `value` must not contain the delimiter" + end + + <<~EOS + #{name}<<#{delimiter} + #{value} + #{delimiter} + EOS + end + # Helper class for formatting annotations on GitHub Actions. class Annotation extend T::Sig diff --git a/Library/Homebrew/vendor/bundle/bundler/setup.rb b/Library/Homebrew/vendor/bundle/bundler/setup.rb index c504edc585..dab3881c96 100644 --- a/Library/Homebrew/vendor/bundle/bundler/setup.rb +++ b/Library/Homebrew/vendor/bundle/bundler/setup.rb @@ -119,6 +119,8 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-static-and-runtime-0.5.10461/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/thor-1.2.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/spoom-1.1.11/lib") +$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/stackprof-0.2.24") +$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/stackprof-0.2.24/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-0.9.26/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/yard-sorbet-0.6.1/lib") $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/tapioca-0.7.3/lib") diff --git a/completions/bash/brew b/completions/bash/brew index 599dd1f915..cad55301f8 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -1619,6 +1619,22 @@ _brew_pin() { __brew_complete_installed_formulae } +_brew_postgresql_upgrade_database() { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${cur}" in + -*) + __brewcomp " + --debug + --help + --quiet + --verbose + " + return + ;; + *) ;; + esac +} + _brew_postinstall() { local cur="${COMP_WORDS[COMP_CWORD]}" case "${cur}" in @@ -1697,6 +1713,7 @@ _brew_pr_pull() { --keep-old --message --no-autosquash + --no-cherry-pick --no-commit --no-upload --quiet @@ -2639,6 +2656,7 @@ _brew() { options) _brew_options ;; outdated) _brew_outdated ;; pin) _brew_pin ;; + postgresql-upgrade-database) _brew_postgresql_upgrade_database ;; postinstall) _brew_postinstall ;; pr-automerge) _brew_pr_automerge ;; pr-publish) _brew_pr_publish ;; diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 644584a378..d2f9c719b2 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -1122,6 +1122,13 @@ __fish_brew_complete_arg 'pin' -l verbose -d 'Make some output more verbose' __fish_brew_complete_arg 'pin' -a '(__fish_brew_suggest_formulae_installed)' +__fish_brew_complete_cmd 'postgresql-upgrade-database' 'Upgrades the database for the `postgresql` formula' +__fish_brew_complete_arg 'postgresql-upgrade-database' -l debug -d 'Display any debugging information' +__fish_brew_complete_arg 'postgresql-upgrade-database' -l help -d 'Show this message' +__fish_brew_complete_arg 'postgresql-upgrade-database' -l quiet -d 'Make some output more quiet' +__fish_brew_complete_arg 'postgresql-upgrade-database' -l verbose -d 'Make some output more verbose' + + __fish_brew_complete_cmd 'postinstall' 'Rerun the post-install steps for formula' __fish_brew_complete_arg 'postinstall' -l debug -d 'Display any debugging information' __fish_brew_complete_arg 'postinstall' -l help -d 'Show this message' @@ -1169,6 +1176,7 @@ __fish_brew_complete_arg 'pr-pull' -l ignore-missing-artifacts -d 'Comma-separat __fish_brew_complete_arg 'pr-pull' -l keep-old -d 'If the formula specifies a rebuild version, attempt to preserve its value in the generated DSL' __fish_brew_complete_arg 'pr-pull' -l message -d 'Message to include when autosquashing revision bumps, deletions, and rebuilds' __fish_brew_complete_arg 'pr-pull' -l no-autosquash -d 'Skip automatically reformatting and rewording commits in the pull request to our preferred format' +__fish_brew_complete_arg 'pr-pull' -l no-cherry-pick -d 'Do not cherry-pick commits from the pull request branch' __fish_brew_complete_arg 'pr-pull' -l no-commit -d 'Do not generate a new commit before uploading' __fish_brew_complete_arg 'pr-pull' -l no-upload -d 'Download the bottles but don\'t upload them' __fish_brew_complete_arg 'pr-pull' -l quiet -d 'Make some output more quiet' diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt index 15d6a01c08..ce9b70c9c6 100644 --- a/completions/internal_commands_list.txt +++ b/completions/internal_commands_list.txt @@ -67,6 +67,7 @@ nodenv-sync options outdated pin +postgresql-upgrade-database postinstall pr-automerge pr-publish diff --git a/completions/zsh/_brew b/completions/zsh/_brew index c3723ddf3e..a0df052392 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -188,6 +188,7 @@ __brew_internal_commands() { 'options:Show install options specific to formula' 'outdated:List installed casks and formulae that have an updated version available' 'pin:Pin the specified formula, preventing them from being upgraded when issuing the `brew upgrade` formula command' + 'postgresql-upgrade-database:Upgrades the database for the `postgresql` formula' 'postinstall:Rerun the post-install steps for formula' 'pr-automerge:Find pull requests that can be automatically merged using `brew pr-publish`' 'pr-publish:Publish bottles for a pull request with GitHub Actions' @@ -1375,6 +1376,15 @@ _brew_pin() { '*::installed_formula:__brew_installed_formulae' } +# brew postgresql-upgrade-database +_brew_postgresql_upgrade_database() { + _arguments \ + '--debug[Display any debugging information]' \ + '--help[Show this message]' \ + '--quiet[Make some output more quiet]' \ + '--verbose[Make some output more verbose]' +} + # brew postinstall _brew_postinstall() { _arguments \ @@ -1431,6 +1441,7 @@ _brew_pr_pull() { '--keep-old[If the formula specifies a rebuild version, attempt to preserve its value in the generated DSL]' \ '(--no-autosquash)--message[Message to include when autosquashing revision bumps, deletions, and rebuilds]' \ '(--message)--no-autosquash[Skip automatically reformatting and rewording commits in the pull request to our preferred format]' \ + '--no-cherry-pick[Do not cherry-pick commits from the pull request branch]' \ '--no-commit[Do not generate a new commit before uploading]' \ '--no-upload[Download the bottles but don'\''t upload them]' \ '--quiet[Make some output more quiet]' \ diff --git a/docs/Manpage.md b/docs/Manpage.md index 652e3bb504..6da9b531f4 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -517,6 +517,10 @@ information is displayed in interactive shells, and suppressed otherwise. Pin the specified *`formula`*, preventing them from being upgraded when issuing the `brew upgrade` *`formula`* command. See also `unpin`. +### `postgresql-upgrade-database` + +Upgrades the database for the `postgresql` formula. + ### `postinstall` *`installed_formula`* [...] Rerun the post-install steps for *`formula`*. @@ -1360,6 +1364,8 @@ Requires write access to the repository. Download the bottles but don't upload them. * `--no-commit`: Do not generate a new commit before uploading. +* `--no-cherry-pick`: + Do not cherry-pick commits from the pull request branch. * `-n`, `--dry-run`: Print what would be done rather than doing it. * `--clean`: diff --git a/docs/README.md b/docs/README.md index dad84f9f5a..d112ff825e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -55,6 +55,8 @@ - [Prose Style Guidelines](Prose-Style-Guidelines.md) - [Type Checking with Sorbet](Typechecking.md) +- [Ruby API Documentation](https://rubydoc.brew.sh) (e.g. for `Formula` etc.) + ## Maintainers - [New Maintainer Checklist](New-Maintainer-Checklist.md) @@ -68,7 +70,6 @@ - [Brew Test Bot for Maintainers](Brew-Test-Bot-For-Core-Contributors.md) - [Common Issues for Maintainers](Common-Issues-for-Core-Contributors.md) - [Releases](Releases.md) -- [Developer/Internal API Documentation](https://rubydoc.brew.sh) ## Governance diff --git a/manpages/brew.1 b/manpages/brew.1 index b26da27a09..ae9803b988 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -719,6 +719,9 @@ Also include outdated casks including those with \fBauto_updates true\fR\. .SS "\fBpin\fR \fIinstalled_formula\fR [\.\.\.]" Pin the specified \fIformula\fR, preventing them from being upgraded when issuing the \fBbrew upgrade\fR \fIformula\fR command\. See also \fBunpin\fR\. . +.SS "\fBpostgresql\-upgrade\-database\fR" +Upgrades the database for the \fBpostgresql\fR formula\. +. .SS "\fBpostinstall\fR \fIinstalled_formula\fR [\.\.\.]" Rerun the post\-install steps for \fIformula\fR\. . @@ -1944,6 +1947,10 @@ Download the bottles but don\'t upload them\. Do not generate a new commit before uploading\. . .TP +\fB\-\-no\-cherry\-pick\fR +Do not cherry\-pick commits from the pull request branch\. +. +.TP \fB\-n\fR, \fB\-\-dry\-run\fR Print what would be done rather than doing it\. .