From e594e7e0bb46d950c6fc400b06702bf297489862 Mon Sep 17 00:00:00 2001 From: botantony Date: Mon, 24 Mar 2025 16:23:57 +0100 Subject: [PATCH 1/3] deprecate!/disable!: allow to specify replacement type Signed-off-by: botantony --- Library/Homebrew/cask/dsl.rb | 20 ++- Library/Homebrew/deprecate_disable.rb | 8 +- Library/Homebrew/formula.rb | 164 ++++++++++++------ Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi | 6 + .../Homebrew/test/deprecate_disable_spec.rb | 57 ++++-- 5 files changed, 189 insertions(+), 66 deletions(-) diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 497e41f775..8d944dd229 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -91,11 +91,13 @@ module Cask :deprecation_date, :deprecation_reason, :deprecation_replacement, + :deprecation_replacement_type, :disable!, :disabled?, :disable_date, :disable_reason, :disable_replacement, + :disable_replacement_type, :discontinued?, # TODO: remove once discontinued? is removed (4.5.0) :livecheck, :livecheck_defined?, @@ -110,8 +112,9 @@ module Cask include OnSystem::MacOSAndLinux - attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, :disable_date, - :disable_reason, :disable_replacement, :on_system_block_min_os + attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, + :deprecation_replacement_type, :disable_date, :disable_reason, + :disable_replacement, :disable_replacement_type, :on_system_block_min_os def initialize(cask) @cask = cask @@ -526,12 +529,16 @@ module Cask # NOTE: A warning will be shown when trying to install this cask. # # @api public - def deprecate!(date:, because:, replacement: nil) + def deprecate!(date:, because:, replacement: nil, replacement_type: :cask) + raise ArgumentError, "Invalid replacement type: #{replacement_type}" if [:formula, + :cask].exclude?(replacement_type) + @deprecation_date = Date.parse(date) return if @deprecation_date > Date.today @deprecation_reason = because @deprecation_replacement = replacement + @deprecation_replacement_type = replacement_type @deprecated = true end @@ -540,18 +547,23 @@ module Cask # NOTE: An error will be thrown when trying to install this cask. # # @api public - def disable!(date:, because:, replacement: nil) + def disable!(date:, because:, replacement: nil, replacement_type: :cask) + raise ArgumentError, "Invalid replacement type: #{replacement_type}" if [:formula, + :cask].exclude?(replacement_type) + @disable_date = Date.parse(date) if @disable_date > Date.today @deprecation_reason = because @deprecation_replacement = replacement + @deprecation_replacement_type = replacement_type @deprecated = true return end @disable_reason = because @disable_replacement = replacement + @disable_replacement_type = replacement_type @disabled = true end diff --git a/Library/Homebrew/deprecate_disable.rb b/Library/Homebrew/deprecate_disable.rb index 05e85ec8ed..1022a18690 100644 --- a/Library/Homebrew/deprecate_disable.rb +++ b/Library/Homebrew/deprecate_disable.rb @@ -83,11 +83,17 @@ module DeprecateDisable formula_or_cask.disable_replacement end + replacement_type = if formula_or_cask.deprecated? + formula_or_cask.deprecation_replacement_type + elsif formula_or_cask.disabled? + formula_or_cask.disable_replacement_type + end + if replacement.present? message << "\n" message << <<~EOS Replacement: - brew install #{replacement} + brew install --#{replacement_type} #{replacement} EOS end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 7265845e66..efa165c9bb 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1484,6 +1484,17 @@ class Formula # @see .deprecate! delegate deprecation_replacement: :"self.class" + # Type of the replacement for this deprecated {Formula}. + # Returns `nil` if the formula is not deprecated. + # @!method deprecation_replacement_type + # @return [Symbol] + # @see .deprecate! + # delegate deprecation_replacement_type: :@deprecation_replacement_type + sig { returns(T.nilable(Symbol)) } + def deprecation_replacement_type + @deprecation_replacement_type ||= T.let(nil, T.nilable(Symbol)) + end + # Whether this {Formula} is disabled (i.e. cannot be installed). # Defaults to false. # @!method disabled? @@ -1512,6 +1523,17 @@ class Formula # @see .disable! delegate disable_replacement: :"self.class" + # Type of the replacement for this disabled {Formula}. + # Returns `nil` if no replacement is specified or the formula is not deprecated. + # @!method disable_replacement_type + # @return [Symbol] + # @see .disable! + # delegate disable_replacement_type: :@disable_replacement_type + sig { returns(T.nilable(Symbol)) } + def disable_replacement_type + @disable_replacement_type ||= T.let(nil, T.nilable(Symbol)) + end + sig { returns(T::Boolean) } def skip_cxxstdlib_check? = false @@ -2491,57 +2513,59 @@ class Formula sig { returns(T::Hash[String, T.untyped]) } def to_hash hsh = { - "name" => name, - "full_name" => full_name, - "tap" => tap&.name, - "oldnames" => oldnames, - "aliases" => aliases.sort, - "versioned_formulae" => versioned_formulae.map(&:name), - "desc" => desc, - "license" => SPDX.license_expression_to_string(license), - "homepage" => homepage, - "versions" => { + "name" => name, + "full_name" => full_name, + "tap" => tap&.name, + "oldnames" => oldnames, + "aliases" => aliases.sort, + "versioned_formulae" => versioned_formulae.map(&:name), + "desc" => desc, + "license" => SPDX.license_expression_to_string(license), + "homepage" => homepage, + "versions" => { "stable" => stable&.version&.to_s, "head" => head&.version&.to_s, "bottle" => bottle_defined?, }, - "urls" => urls_hash, - "revision" => revision, - "version_scheme" => version_scheme, - "bottle" => {}, - "pour_bottle_only_if" => self.class.pour_bottle_only_if&.to_s, - "keg_only" => keg_only?, - "keg_only_reason" => keg_only_reason&.to_hash, - "options" => [], - "build_dependencies" => [], - "dependencies" => [], - "test_dependencies" => [], - "recommended_dependencies" => [], - "optional_dependencies" => [], - "uses_from_macos" => [], - "uses_from_macos_bounds" => [], - "requirements" => serialized_requirements, - "conflicts_with" => conflicts.map(&:name), - "conflicts_with_reasons" => conflicts.map(&:reason), - "link_overwrite" => self.class.link_overwrite_paths.to_a, - "caveats" => caveats_with_placeholders, - "installed" => T.let([], T::Array[T::Hash[String, T.untyped]]), - "linked_keg" => linked_version&.to_s, - "pinned" => pinned?, - "outdated" => outdated?, - "deprecated" => deprecated?, - "deprecation_date" => deprecation_date, - "deprecation_reason" => deprecation_reason, - "deprecation_replacement" => deprecation_replacement, - "disabled" => disabled?, - "disable_date" => disable_date, - "disable_reason" => disable_reason, - "disable_replacement" => disable_replacement, - "post_install_defined" => post_install_defined?, - "service" => (service.to_hash if service?), - "tap_git_head" => tap_git_head, - "ruby_source_path" => ruby_source_path, - "ruby_source_checksum" => {}, + "urls" => urls_hash, + "revision" => revision, + "version_scheme" => version_scheme, + "bottle" => {}, + "pour_bottle_only_if" => self.class.pour_bottle_only_if&.to_s, + "keg_only" => keg_only?, + "keg_only_reason" => keg_only_reason&.to_hash, + "options" => [], + "build_dependencies" => [], + "dependencies" => [], + "test_dependencies" => [], + "recommended_dependencies" => [], + "optional_dependencies" => [], + "uses_from_macos" => [], + "uses_from_macos_bounds" => [], + "requirements" => serialized_requirements, + "conflicts_with" => conflicts.map(&:name), + "conflicts_with_reasons" => conflicts.map(&:reason), + "link_overwrite" => self.class.link_overwrite_paths.to_a, + "caveats" => caveats_with_placeholders, + "installed" => T.let([], T::Array[T::Hash[String, T.untyped]]), + "linked_keg" => linked_version&.to_s, + "pinned" => pinned?, + "outdated" => outdated?, + "deprecated" => deprecated?, + "deprecation_date" => deprecation_date, + "deprecation_reason" => deprecation_reason, + "deprecation_replacement" => deprecation_replacement, + "deprecation_replacement_type" => @deprecation_replacement_type, + "disabled" => disabled?, + "disable_date" => disable_date, + "disable_reason" => disable_reason, + "disable_replacement" => disable_replacement, + "disable_replacement_type" => @disable_replacement_type, + "post_install_defined" => post_install_defined?, + "service" => (service.to_hash if service?), + "tap_git_head" => tap_git_head, + "ruby_source_path" => ruby_source_path, + "ruby_source_checksum" => {}, } hsh["bottle"]["stable"] = bottle_hash if stable && bottle_defined? @@ -4320,17 +4344,29 @@ class Formula # ```ruby # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" # ``` + # ```ruby + # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo", + # replacement_type: :cask + # ``` # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public - sig { params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String)).void } - def deprecate!(date:, because:, replacement: nil) + sig { + params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String), + replacement_type: Symbol).void + } + def deprecate!(date:, because:, replacement: nil, replacement_type: :formula) + if [:formula, :cask].exclude?(replacement_type) + raise ArgumentError, "Invalid replacement type: #{replacement_type}" + end + @deprecation_date = T.let(Date.parse(date), T.nilable(Date)) return if T.must(@deprecation_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) @deprecation_replacement = T.let(replacement, T.nilable(String)) + @deprecation_replacement_type = T.let(replacement_type, T.nilable(Symbol)) T.must(@deprecated = T.let(true, T.nilable(T::Boolean))) end @@ -4363,6 +4399,13 @@ class Formula sig { returns(T.nilable(String)) } attr_reader :deprecation_replacement + # Type of the replacement for a deprecated {Formula}. + # + # @return [nil] if no replacement was provided or the formula is not deprecated. + # @see .deprecate! + sig { returns(T.nilable(Symbol)) } + attr_reader :deprecation_replacement_type + # Disables a {Formula} (on the given date) so it cannot be # installed. If the date has not yet passed the formula # will be deprecated instead of disabled. @@ -4380,23 +4423,35 @@ class Formula # ```ruby # disable! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" # ``` + # ```ruby + # disable! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo", replacement_type: :cask + # ``` # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public - sig { params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String)).void } - def disable!(date:, because:, replacement: nil) + sig { + params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String), + replacement_type: Symbol).void + } + def disable!(date:, because:, replacement: nil, replacement_type: :formula) + if [:formula, :cask].exclude?(replacement_type) + raise ArgumentError, "Invalid replacement type: #{replacement_type}" + end + @disable_date = T.let(Date.parse(date), T.nilable(Date)) if T.must(@disable_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) @deprecation_replacement = T.let(replacement, T.nilable(String)) + @deprecation_replacement_type = T.let(replacement_type, T.nilable(Symbol)) @deprecated = T.let(true, T.nilable(T::Boolean)) return end @disable_reason = T.let(because, T.nilable(T.any(String, Symbol))) @disable_replacement = T.let(replacement, T.nilable(String)) + @disable_replacement_type = T.let(replacement_type, T.nilable(Symbol)) @disabled = T.let(true, T.nilable(T::Boolean)) end @@ -4430,6 +4485,13 @@ class Formula sig { returns(T.nilable(String)) } attr_reader :disable_replacement + # Type of the replacement for a disabled {Formula}. + # Returns `nil` if the formula is not disabled. + # + # @see .disable! + sig { returns(T.nilable(Symbol)) } + attr_reader :disable_replacement_type + # Permit overwriting certain files while linking. # # ### Examples diff --git a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi index d9f42ba625..9d1dfcdc33 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi @@ -69,6 +69,9 @@ class Cask::Cask sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_replacement(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def deprecation_replacement_type(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def desc(*args, &block); end @@ -87,6 +90,9 @@ class Cask::Cask sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_replacement(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def disable_replacement_type(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) } def disabled?(*args, &block); end diff --git a/Library/Homebrew/test/deprecate_disable_spec.rb b/Library/Homebrew/test/deprecate_disable_spec.rb index 71ed29e2b3..a1304886c6 100644 --- a/Library/Homebrew/test/deprecate_disable_spec.rb +++ b/Library/Homebrew/test/deprecate_disable_spec.rb @@ -7,27 +7,33 @@ RSpec.describe DeprecateDisable do let(:disable_date) { deprecate_date >> DeprecateDisable::REMOVE_DISABLED_TIME_WINDOW } let(:deprecated_formula) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_type: nil, + deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_date) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_date: deprecate_date, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_type: nil, + deprecation_date: deprecate_date, disable_date: nil) end let(:disabled_formula) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: "is broken", - disable_replacement: nil, deprecation_date: nil, disable_date: nil) + disable_replacement: nil, disable_replacement_type: nil, + deprecation_date: nil, disable_date: nil) end let(:disabled_formula_with_date) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: :does_not_build, - disable_replacement: nil, deprecation_date: nil, disable_date: disable_date) + disable_replacement: nil, disable_replacement_type: nil, + deprecation_date: nil, disable_date: disable_date) end let(:deprecated_cask) do instance_double(Cask::Cask, deprecated?: true, disabled?: false, deprecation_reason: :discontinued, - deprecation_replacement: nil, deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_type: nil, + deprecation_date: nil, disable_date: nil) end let(:disabled_cask) do instance_double(Cask::Cask, deprecated?: false, disabled?: true, disable_reason: nil, - disable_replacement: nil, deprecation_date: nil, disable_date: nil) + disable_replacement: nil, disable_replacement_type: nil, + deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_replacement) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, @@ -125,23 +131,54 @@ RSpec.describe DeprecateDisable do end it "returns a replacement message for a deprecated formula" do + # allow(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) + expect(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) expect(described_class.message(deprecated_formula_with_replacement)) - .to eq "deprecated because it does not build!\nReplacement:\n brew install foo\n" + .to eq "deprecated because it does not build!\nReplacement:\n brew install --formula foo\n" + end + + it "returns a replacement cask message for a deprecated formula" do + allow(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) + expect(described_class.message(deprecated_formula_with_replacement)) + .to eq "deprecated because it does not build!\nReplacement:\n brew install --cask foo\n" end it "returns a replacement message for a disabled formula" do + # allow(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:formula) + expect(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:formula) expect(described_class.message(disabled_formula_with_replacement)) - .to eq "disabled because it is broken!\nReplacement:\n brew install bar\n" + .to eq "disabled because it is broken!\nReplacement:\n brew install --formula bar\n" + end + + it "returns a replacement cask message for a disabled formula" do + allow(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:cask) + expect(described_class.message(disabled_formula_with_replacement)) + .to eq "disabled because it is broken!\nReplacement:\n brew install --cask bar\n" end it "returns a replacement message for a deprecated cask" do + # allow(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) + expect(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) expect(described_class.message(deprecated_cask_with_replacement)) - .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install baz\n" + .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --cask baz\n" + end + + it "returns a replacement formula message for a deprecated cask" do + allow(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) + expect(described_class.message(deprecated_cask_with_replacement)) + .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --formula baz\n" end it "returns a replacement message for a disabled cask" do + expect(disabled_cask_with_replacement).to receive(:disable_replacement_type).and_return(:cask) expect(described_class.message(disabled_cask_with_replacement)) - .to eq "disabled!\nReplacement:\n brew install qux\n" + .to eq "disabled!\nReplacement:\n brew install --cask qux\n" + end + + it "returns a replacement formula message for a disabled cask" do + allow(disabled_cask_with_replacement).to receive(:disable_replacement_type).and_return(:formula) + expect(described_class.message(disabled_cask_with_replacement)) + .to eq "disabled!\nReplacement:\n brew install --formula qux\n" end end From ca447e1ce9a7ebf199ff753a12e83c950e38149c Mon Sep 17 00:00:00 2001 From: botantony Date: Wed, 2 Apr 2025 13:19:49 +0200 Subject: [PATCH 2/3] deprecate!/disable!: `replacement_formula`/`replacement_cask` fields Signed-off-by: botantony --- Library/Homebrew/cask/dsl.rb | 52 +++- Library/Homebrew/deprecate_disable.rb | 41 ++- Library/Homebrew/formula.rb | 238 +++++++++++------- Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi | 10 +- Library/Homebrew/sorbet/rbi/dsl/formula.rbi | 12 + .../Homebrew/test/deprecate_disable_spec.rb | 83 ++++-- 6 files changed, 296 insertions(+), 140 deletions(-) diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 8d944dd229..0947d8d0a3 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -91,13 +91,15 @@ module Cask :deprecation_date, :deprecation_reason, :deprecation_replacement, - :deprecation_replacement_type, + :deprecation_replacement_formula, + :deprecation_replacement_cask, :disable!, :disabled?, :disable_date, :disable_reason, :disable_replacement, - :disable_replacement_type, + :disable_replacement_formula, + :disable_replacement_cask, :discontinued?, # TODO: remove once discontinued? is removed (4.5.0) :livecheck, :livecheck_defined?, @@ -113,8 +115,9 @@ module Cask include OnSystem::MacOSAndLinux attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, - :deprecation_replacement_type, :disable_date, :disable_reason, - :disable_replacement, :disable_replacement_type, :on_system_block_min_os + :deprecation_replacement_formula, :deprecation_replacement_caks, :disable_date, + :disable_reason, :disable_replacement, :disable_replacement_formula, + :disable_replacement_cask, :on_system_block_min_os def initialize(cask) @cask = cask @@ -529,16 +532,27 @@ module Cask # NOTE: A warning will be shown when trying to install this cask. # # @api public - def deprecate!(date:, because:, replacement: nil, replacement_type: :cask) - raise ArgumentError, "Invalid replacement type: #{replacement_type}" if [:formula, - :cask].exclude?(replacement_type) + def deprecate!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) + if replacement_formula && replacement_cask + raise ArgumentError, "replacement_formula and replacement_cask specified!" + end + + # TODO: deprecate in >= 4.5.0 + # if replacement + # odeprecated( + # "deprecate!(:replacement)", + # "deprecate!(:replacement_formula) or deprecate!(:replacement_cask)", + # disable_on: Time.new(2025, 10, 15), + # ) + # end @deprecation_date = Date.parse(date) return if @deprecation_date > Date.today @deprecation_reason = because @deprecation_replacement = replacement - @deprecation_replacement_type = replacement_type + @deprecation_replacement_formula = replacement_formula + @deprecation_replacement_cask = replacement_cask @deprecated = true end @@ -547,23 +561,35 @@ module Cask # NOTE: An error will be thrown when trying to install this cask. # # @api public - def disable!(date:, because:, replacement: nil, replacement_type: :cask) - raise ArgumentError, "Invalid replacement type: #{replacement_type}" if [:formula, - :cask].exclude?(replacement_type) + def disable!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) + if replacement_formula && replacement_cask + raise ArgumentError, "replacement_formula and replacement_cask specified!" + end + + # TODO: deprecate in >= 4.5.0 + # if replacement + # odeprecated( + # "disable!(:replacement)", + # "disable!(:replacement_formula) or disable!(:replacement_cask)", + # disable_on: Time.new(2025, 10, 15), + # ) + # end @disable_date = Date.parse(date) if @disable_date > Date.today @deprecation_reason = because @deprecation_replacement = replacement - @deprecation_replacement_type = replacement_type + @deprecation_replacement_formula = replacement_formula + @deprecation_replacement_cask = replacement_cask @deprecated = true return end @disable_reason = because @disable_replacement = replacement - @disable_replacement_type = replacement_type + @disable_replacement_formula = replacement_formula + @disable_replacement_cask = replacement_cask @disabled = true end diff --git a/Library/Homebrew/deprecate_disable.rb b/Library/Homebrew/deprecate_disable.rb index 1022a18690..7bc9e72d6e 100644 --- a/Library/Homebrew/deprecate_disable.rb +++ b/Library/Homebrew/deprecate_disable.rb @@ -41,6 +41,23 @@ module DeprecateDisable :disabled if formula_or_cask.disabled? end + sig { + params( + formula: T.nilable(String), + cask: T.nilable(String), + not_typed: T.nilable(String), + ).returns(T.nilable(String)) + } + def replacement_with_type(formula, cask, not_typed) + if formula + "--formula #{formula}" + elsif cask + "--cask #{cask}" + else + not_typed + end + end + sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T.nilable(String)) } def message(formula_or_cask) return if type(formula_or_cask).blank? @@ -77,23 +94,25 @@ module DeprecateDisable end end - replacement = if formula_or_cask.deprecated? - formula_or_cask.deprecation_replacement - elsif formula_or_cask.disabled? - formula_or_cask.disable_replacement - end - - replacement_type = if formula_or_cask.deprecated? - formula_or_cask.deprecation_replacement_type - elsif formula_or_cask.disabled? - formula_or_cask.disable_replacement_type + replacement = if formula_or_cask.disabled? + replacement_with_type( + formula_or_cask.disable_replacement_formula, + formula_or_cask.disable_replacement_cask, + formula_or_cask.disable_replacement, + ) + elsif formula_or_cask.deprecated? + replacement_with_type( + formula_or_cask.deprecation_replacement_formula, + formula_or_cask.deprecation_replacement_cask, + formula_or_cask.deprecation_replacement, + ) end if replacement.present? message << "\n" message << <<~EOS Replacement: - brew install --#{replacement_type} #{replacement} + brew install #{replacement} EOS end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index efa165c9bb..db30b17da8 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1484,16 +1484,19 @@ class Formula # @see .deprecate! delegate deprecation_replacement: :"self.class" - # Type of the replacement for this deprecated {Formula}. - # Returns `nil` if the formula is not deprecated. - # @!method deprecation_replacement_type - # @return [Symbol] + # The replacement formula for this deprecated {Formula}. + # Returns `nil` if no replacement is specified or the formula is not deprecated. + # @!method deprecation_replacement_formula + # @return [String] # @see .deprecate! - # delegate deprecation_replacement_type: :@deprecation_replacement_type - sig { returns(T.nilable(Symbol)) } - def deprecation_replacement_type - @deprecation_replacement_type ||= T.let(nil, T.nilable(Symbol)) - end + delegate deprecation_replacement_formula: :"self.class" + + # The replacement cask for this deprecated {Formula}. + # Returns `nil` if no replacement is specified or the formula is not deprecated. + # @!method deprecation_replacement_cask + # @return [String] + # @see .deprecate! + delegate deprecation_replacement_cask: :"self.class" # Whether this {Formula} is disabled (i.e. cannot be installed). # Defaults to false. @@ -1523,16 +1526,19 @@ class Formula # @see .disable! delegate disable_replacement: :"self.class" - # Type of the replacement for this disabled {Formula}. - # Returns `nil` if no replacement is specified or the formula is not deprecated. - # @!method disable_replacement_type - # @return [Symbol] + # The replacement formula for this disabled {Formula}. + # Returns `nil` if no replacement is specified or the formula is not disabled. + # @!method disable_replacement_formula + # @return [String] # @see .disable! - # delegate disable_replacement_type: :@disable_replacement_type - sig { returns(T.nilable(Symbol)) } - def disable_replacement_type - @disable_replacement_type ||= T.let(nil, T.nilable(Symbol)) - end + delegate disable_replacement_formula: :"self.class" + + # The replacement cask for this disabled {Formula}. + # Returns `nil` if no replacement is specified or the formula is not disabled. + # @!method disable_replacement_cask + # @return [String] + # @see .disable! + delegate disable_replacement_cask: :"self.class" sig { returns(T::Boolean) } def skip_cxxstdlib_check? = false @@ -2513,59 +2519,61 @@ class Formula sig { returns(T::Hash[String, T.untyped]) } def to_hash hsh = { - "name" => name, - "full_name" => full_name, - "tap" => tap&.name, - "oldnames" => oldnames, - "aliases" => aliases.sort, - "versioned_formulae" => versioned_formulae.map(&:name), - "desc" => desc, - "license" => SPDX.license_expression_to_string(license), - "homepage" => homepage, - "versions" => { + "name" => name, + "full_name" => full_name, + "tap" => tap&.name, + "oldnames" => oldnames, + "aliases" => aliases.sort, + "versioned_formulae" => versioned_formulae.map(&:name), + "desc" => desc, + "license" => SPDX.license_expression_to_string(license), + "homepage" => homepage, + "versions" => { "stable" => stable&.version&.to_s, "head" => head&.version&.to_s, "bottle" => bottle_defined?, }, - "urls" => urls_hash, - "revision" => revision, - "version_scheme" => version_scheme, - "bottle" => {}, - "pour_bottle_only_if" => self.class.pour_bottle_only_if&.to_s, - "keg_only" => keg_only?, - "keg_only_reason" => keg_only_reason&.to_hash, - "options" => [], - "build_dependencies" => [], - "dependencies" => [], - "test_dependencies" => [], - "recommended_dependencies" => [], - "optional_dependencies" => [], - "uses_from_macos" => [], - "uses_from_macos_bounds" => [], - "requirements" => serialized_requirements, - "conflicts_with" => conflicts.map(&:name), - "conflicts_with_reasons" => conflicts.map(&:reason), - "link_overwrite" => self.class.link_overwrite_paths.to_a, - "caveats" => caveats_with_placeholders, - "installed" => T.let([], T::Array[T::Hash[String, T.untyped]]), - "linked_keg" => linked_version&.to_s, - "pinned" => pinned?, - "outdated" => outdated?, - "deprecated" => deprecated?, - "deprecation_date" => deprecation_date, - "deprecation_reason" => deprecation_reason, - "deprecation_replacement" => deprecation_replacement, - "deprecation_replacement_type" => @deprecation_replacement_type, - "disabled" => disabled?, - "disable_date" => disable_date, - "disable_reason" => disable_reason, - "disable_replacement" => disable_replacement, - "disable_replacement_type" => @disable_replacement_type, - "post_install_defined" => post_install_defined?, - "service" => (service.to_hash if service?), - "tap_git_head" => tap_git_head, - "ruby_source_path" => ruby_source_path, - "ruby_source_checksum" => {}, + "urls" => urls_hash, + "revision" => revision, + "version_scheme" => version_scheme, + "bottle" => {}, + "pour_bottle_only_if" => self.class.pour_bottle_only_if&.to_s, + "keg_only" => keg_only?, + "keg_only_reason" => keg_only_reason&.to_hash, + "options" => [], + "build_dependencies" => [], + "dependencies" => [], + "test_dependencies" => [], + "recommended_dependencies" => [], + "optional_dependencies" => [], + "uses_from_macos" => [], + "uses_from_macos_bounds" => [], + "requirements" => serialized_requirements, + "conflicts_with" => conflicts.map(&:name), + "conflicts_with_reasons" => conflicts.map(&:reason), + "link_overwrite" => self.class.link_overwrite_paths.to_a, + "caveats" => caveats_with_placeholders, + "installed" => T.let([], T::Array[T::Hash[String, T.untyped]]), + "linked_keg" => linked_version&.to_s, + "pinned" => pinned?, + "outdated" => outdated?, + "deprecated" => deprecated?, + "deprecation_date" => deprecation_date, + "deprecation_reason" => deprecation_reason, + "deprecation_replacement" => deprecation_replacement, + "deprecation_replacement_formula" => deprecation_replacement_formula, + "deprecation_replacement_cask" => deprecation_replacement_cask, + "disabled" => disabled?, + "disable_date" => disable_date, + "disable_reason" => disable_reason, + "disable_replacement" => disable_replacement, + "disable_replacement_formula" => disable_replacement_formula, + "disable_replacement_cask" => disable_replacement_cask, + "post_install_defined" => post_install_defined?, + "service" => (service.to_hash if service?), + "tap_git_head" => tap_git_head, + "ruby_source_path" => ruby_source_path, + "ruby_source_checksum" => {}, } hsh["bottle"]["stable"] = bottle_hash if stable && bottle_defined? @@ -4342,31 +4350,46 @@ class Formula # ``` # # ```ruby - # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" + # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement_formula: "foo" # ``` # ```ruby - # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo", - # replacement_type: :cask + # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement_cask: "foo" # ``` + # TODO: replace legacy `replacement` with `replacement_formula` or `replacement_cask` # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public sig { - params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String), - replacement_type: Symbol).void + params( + date: String, + because: T.any(NilClass, String, Symbol), + replacement: T.nilable(String), + replacement_formula: T.nilable(String), + replacement_cask: T.nilable(String), + ).void } - def deprecate!(date:, because:, replacement: nil, replacement_type: :formula) - if [:formula, :cask].exclude?(replacement_type) - raise ArgumentError, "Invalid replacement type: #{replacement_type}" + def deprecate!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) + if replacement_formula && replacement_cask + raise ArgumentError, "replacement_formula and replacement_cask specified!" end + # TODO: deprecate in >= 4.5.0 + # if replacement + # odeprecated( + # "deprecate!(:replacement)", + # "deprecate!(:replacement_formula) or deprecate!(:replacement_cask)", + # disable_on: Time.new(2025, 10, 15), + # ) + # end + @deprecation_date = T.let(Date.parse(date), T.nilable(Date)) return if T.must(@deprecation_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) @deprecation_replacement = T.let(replacement, T.nilable(String)) - @deprecation_replacement_type = T.let(replacement_type, T.nilable(Symbol)) + @deprecation_replacement_formula = T.let(replacement_formula, T.nilable(String)) + @deprecation_replacement_cask = T.let(replacement_cask, T.nilable(String)) T.must(@deprecated = T.let(true, T.nilable(T::Boolean))) end @@ -4399,12 +4422,19 @@ class Formula sig { returns(T.nilable(String)) } attr_reader :deprecation_replacement - # Type of the replacement for a deprecated {Formula}. + # The replacement formula for a deprecated {Formula}. # # @return [nil] if no replacement was provided or the formula is not deprecated. # @see .deprecate! - sig { returns(T.nilable(Symbol)) } - attr_reader :deprecation_replacement_type + sig { returns(T.nilable(String)) } + attr_reader :deprecation_replacement_formula + + # The replacement cask for a deprecated {Formula}. + # + # @return [nil] if no replacement was provided or the formula is not deprecated. + # @see .deprecate! + sig { returns(T.nilable(String)) } + attr_reader :deprecation_replacement_cask # Disables a {Formula} (on the given date) so it cannot be # installed. If the date has not yet passed the formula @@ -4421,37 +4451,54 @@ class Formula # ``` # # ```ruby - # disable! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" + # disable! date: "2020-08-27", because: "has been replaced by foo", replacement_formula: "foo" # ``` # ```ruby - # disable! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo", replacement_type: :cask + # disable! date: "2020-08-27", because: "has been replaced by foo", replacement_cask: "foo" # ``` + # TODO: replace legacy `replacement` with `replacement_formula` or `replacement_cask` # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public sig { - params(date: String, because: T.any(NilClass, String, Symbol), replacement: T.nilable(String), - replacement_type: Symbol).void + params( + date: String, + because: T.any(NilClass, String, Symbol), + replacement: T.nilable(String), + replacement_formula: T.nilable(String), + replacement_cask: T.nilable(String), + ).void } - def disable!(date:, because:, replacement: nil, replacement_type: :formula) - if [:formula, :cask].exclude?(replacement_type) - raise ArgumentError, "Invalid replacement type: #{replacement_type}" + def disable!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) + if replacement_formula && replacement_cask + raise ArgumentError, "replacement_formula and replacement_cask specified!" end + # TODO: deprecate in >= 4.5.0 + # if replacement + # odeprecated( + # "disable!(:replacement)", + # "disable!(:replacement_formula) or deprecate!(:replacement_cask)", + # disable_on: Time.new(2025, 10, 15), + # ) + # end + @disable_date = T.let(Date.parse(date), T.nilable(Date)) if T.must(@disable_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) @deprecation_replacement = T.let(replacement, T.nilable(String)) - @deprecation_replacement_type = T.let(replacement_type, T.nilable(Symbol)) + @deprecation_replacement_formula = T.let(replacement_formula, T.nilable(String)) + @deprecation_replacement_cask = T.let(replacement_cask, T.nilable(String)) @deprecated = T.let(true, T.nilable(T::Boolean)) return end @disable_reason = T.let(because, T.nilable(T.any(String, Symbol))) @disable_replacement = T.let(replacement, T.nilable(String)) - @disable_replacement_type = T.let(replacement_type, T.nilable(Symbol)) + @disable_replacement_formula = T.let(replacement_formula, T.nilable(String)) + @disable_replacement_cask = T.let(replacement_cask, T.nilable(String)) @disabled = T.let(true, T.nilable(T::Boolean)) end @@ -4485,12 +4532,19 @@ class Formula sig { returns(T.nilable(String)) } attr_reader :disable_replacement - # Type of the replacement for a disabled {Formula}. - # Returns `nil` if the formula is not disabled. + # The replacement formula for a disabled {Formula}. + # Returns `nil` if no reason was provided or the formula is not disabled. # # @see .disable! - sig { returns(T.nilable(Symbol)) } - attr_reader :disable_replacement_type + sig { returns(T.nilable(String)) } + attr_reader :disable_replacement_formula + + # The replacement cask for a disabled {Formula}. + # Returns `nil` if no reason was provided or the formula is not disabled. + # + # @see .disable! + sig { returns(T.nilable(String)) } + attr_reader :disable_replacement_cask # Permit overwriting certain files while linking. # diff --git a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi index 9d1dfcdc33..831a01c677 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi @@ -70,7 +70,10 @@ class Cask::Cask def deprecation_replacement(*args, &block); end sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def deprecation_replacement_type(*args, &block); end + def deprecation_replacement_formula(*args, &block); end + + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def deprecation_replacement_cask(*args, &block); end sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def desc(*args, &block); end @@ -91,7 +94,10 @@ class Cask::Cask def disable_replacement(*args, &block); end sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def disable_replacement_type(*args, &block); end + def disable_replacement_formula(*args, &block); end + + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def disable_replacement_cask(*args, &block); end sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) } def disabled?(*args, &block); end diff --git a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi index 8e7c2d0290..c0e149d99b 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi @@ -54,6 +54,12 @@ class Formula sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_replacement(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def deprecation_replacement_formula(*args, &block); end + + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def deprecation_replacement_cask(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deps(*args, &block); end @@ -69,6 +75,12 @@ class Formula sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_replacement(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def disable_replacement_formula(*args, &block); end + + sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } + def disable_replacement_cask(*args, &block); end + sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) } def disabled?(*args, &block); end diff --git a/Library/Homebrew/test/deprecate_disable_spec.rb b/Library/Homebrew/test/deprecate_disable_spec.rb index a1304886c6..819191261c 100644 --- a/Library/Homebrew/test/deprecate_disable_spec.rb +++ b/Library/Homebrew/test/deprecate_disable_spec.rb @@ -7,32 +7,38 @@ RSpec.describe DeprecateDisable do let(:disable_date) { deprecate_date >> DeprecateDisable::REMOVE_DISABLED_TIME_WINDOW } let(:deprecated_formula) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_replacement_type: nil, - deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_date) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_replacement_type: nil, - deprecation_date: deprecate_date, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, deprecation_date: deprecate_date, disable_date: nil) end let(:disabled_formula) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: "is broken", - disable_replacement: nil, disable_replacement_type: nil, + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, disable_replacement: nil, + disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:disabled_formula_with_date) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: :does_not_build, - disable_replacement: nil, disable_replacement_type: nil, + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, disable_replacement: nil, + disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: disable_date) end let(:deprecated_cask) do instance_double(Cask::Cask, deprecated?: true, disabled?: false, deprecation_reason: :discontinued, - deprecation_replacement: nil, deprecation_replacement_type: nil, - deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:disabled_cask) do instance_double(Cask::Cask, deprecated?: false, disabled?: true, disable_reason: nil, - disable_replacement: nil, disable_replacement_type: nil, + deprecation_replacement: nil, deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil, disable_replacement: nil, + disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_replacement) do @@ -131,55 +137,88 @@ RSpec.describe DeprecateDisable do end it "returns a replacement message for a deprecated formula" do - # allow(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) - expect(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) + allow(deprecated_formula_with_replacement).to receive_messages(deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil) + expect(described_class.message(deprecated_formula_with_replacement)) + .to eq "deprecated because it does not build!\nReplacement:\n brew install foo\n" + end + + it "returns a replacement formula message for a deprecated formula" do + allow(deprecated_formula_with_replacement).to receive_messages(deprecation_replacement_formula: "foo", + deprecation_replacement_cask: nil) expect(described_class.message(deprecated_formula_with_replacement)) .to eq "deprecated because it does not build!\nReplacement:\n brew install --formula foo\n" end it "returns a replacement cask message for a deprecated formula" do - allow(deprecated_formula_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) + allow(deprecated_formula_with_replacement).to receive_messages(deprecation_replacement_formula: nil, + deprecation_replacement_cask: "foo") expect(described_class.message(deprecated_formula_with_replacement)) .to eq "deprecated because it does not build!\nReplacement:\n brew install --cask foo\n" end it "returns a replacement message for a disabled formula" do - # allow(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:formula) - expect(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:formula) + allow(disabled_formula_with_replacement).to receive_messages(disable_replacement_formula: nil, + disable_replacement_cask: nil) + expect(described_class.message(disabled_formula_with_replacement)) + .to eq "disabled because it is broken!\nReplacement:\n brew install bar\n" + end + + it "returns a replacement formula message for a disabled formula" do + allow(disabled_formula_with_replacement).to receive_messages(disable_replacement_formula: "bar", + disable_replacement_cask: nil) expect(described_class.message(disabled_formula_with_replacement)) .to eq "disabled because it is broken!\nReplacement:\n brew install --formula bar\n" end it "returns a replacement cask message for a disabled formula" do - allow(disabled_formula_with_replacement).to receive(:disable_replacement_type).and_return(:cask) + allow(disabled_formula_with_replacement).to receive_messages(disable_replacement_formula: nil, + disable_replacement_cask: "bar") expect(described_class.message(disabled_formula_with_replacement)) .to eq "disabled because it is broken!\nReplacement:\n brew install --cask bar\n" end it "returns a replacement message for a deprecated cask" do - # allow(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) - expect(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:cask) + allow(deprecated_cask_with_replacement).to receive_messages(deprecation_replacement_formula: nil, + deprecation_replacement_cask: nil) expect(described_class.message(deprecated_cask_with_replacement)) - .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --cask baz\n" + .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install baz\n" end it "returns a replacement formula message for a deprecated cask" do - allow(deprecated_cask_with_replacement).to receive(:deprecation_replacement_type).and_return(:formula) + allow(deprecated_cask_with_replacement).to receive_messages(deprecation_replacement_formula: "baz", + deprecation_replacement_cask: nil) expect(described_class.message(deprecated_cask_with_replacement)) .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --formula baz\n" end + it "returns a replacement cask message for a deprecated cask" do + allow(deprecated_cask_with_replacement).to receive_messages(deprecation_replacement_formula: nil, + deprecation_replacement_cask: "baz") + expect(described_class.message(deprecated_cask_with_replacement)) + .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --cask baz\n" + end + it "returns a replacement message for a disabled cask" do - expect(disabled_cask_with_replacement).to receive(:disable_replacement_type).and_return(:cask) + allow(disabled_cask_with_replacement).to receive_messages(disable_replacement_formula: nil, + disable_replacement_cask: nil) expect(described_class.message(disabled_cask_with_replacement)) - .to eq "disabled!\nReplacement:\n brew install --cask qux\n" + .to eq "disabled!\nReplacement:\n brew install qux\n" end it "returns a replacement formula message for a disabled cask" do - allow(disabled_cask_with_replacement).to receive(:disable_replacement_type).and_return(:formula) + allow(disabled_cask_with_replacement).to receive_messages(disable_replacement_formula: "qux", + disable_replacement_cask: nil) expect(described_class.message(disabled_cask_with_replacement)) .to eq "disabled!\nReplacement:\n brew install --formula qux\n" end + + it "returns a replacement cask message for a disabled cask" do + allow(disabled_cask_with_replacement).to receive_messages(disable_replacement_formula: nil, + disable_replacement_cask: "qux") + expect(described_class.message(disabled_cask_with_replacement)) + .to eq "disabled!\nReplacement:\n brew install --cask qux\n" + end end describe "::to_reason_string_or_symbol" do From 4fa1355be0bbf0de469925fce9aeff9ce841cd82 Mon Sep 17 00:00:00 2001 From: botantony Date: Mon, 14 Apr 2025 13:46:05 +0200 Subject: [PATCH 3/3] deprecate!/disable!: remove non-typed `replacement` field Signed-off-by: botantony Co-authored-by: Mike McQuaid --- Library/Homebrew/cask/cask.rb | 70 ++++++++++--------- Library/Homebrew/cask/dsl.rb | 30 ++++---- Library/Homebrew/deprecate_disable.rb | 15 ++-- Library/Homebrew/formula.rb | 53 +++----------- Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi | 6 -- Library/Homebrew/sorbet/rbi/dsl/formula.rbi | 6 -- .../Homebrew/test/deprecate_disable_spec.rb | 57 ++++----------- .../support/fixtures/cask/everything.json | 6 +- 8 files changed, 81 insertions(+), 162 deletions(-) diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index b57147a8b7..a48af3acb2 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -361,40 +361,42 @@ module Cask def to_h { - "token" => token, - "full_token" => full_name, - "old_tokens" => old_tokens, - "tap" => tap&.name, - "name" => name, - "desc" => desc, - "homepage" => homepage, - "url" => url, - "url_specs" => url_specs, - "version" => version, - "installed" => installed_version, - "installed_time" => install_time&.to_i, - "bundle_version" => bundle_long_version, - "bundle_short_version" => bundle_short_version, - "outdated" => outdated?, - "sha256" => sha256, - "artifacts" => artifacts_list, - "caveats" => (Tty.strip_ansi(caveats) unless caveats.empty?), - "depends_on" => depends_on, - "conflicts_with" => conflicts_with, - "container" => container&.pairs, - "auto_updates" => auto_updates, - "deprecated" => deprecated?, - "deprecation_date" => deprecation_date, - "deprecation_reason" => deprecation_reason, - "deprecation_replacement" => deprecation_replacement, - "disabled" => disabled?, - "disable_date" => disable_date, - "disable_reason" => disable_reason, - "disable_replacement" => disable_replacement, - "tap_git_head" => tap_git_head, - "languages" => languages, - "ruby_source_path" => ruby_source_path, - "ruby_source_checksum" => ruby_source_checksum, + "token" => token, + "full_token" => full_name, + "old_tokens" => old_tokens, + "tap" => tap&.name, + "name" => name, + "desc" => desc, + "homepage" => homepage, + "url" => url, + "url_specs" => url_specs, + "version" => version, + "installed" => installed_version, + "installed_time" => install_time&.to_i, + "bundle_version" => bundle_long_version, + "bundle_short_version" => bundle_short_version, + "outdated" => outdated?, + "sha256" => sha256, + "artifacts" => artifacts_list, + "caveats" => (Tty.strip_ansi(caveats) unless caveats.empty?), + "depends_on" => depends_on, + "conflicts_with" => conflicts_with, + "container" => container&.pairs, + "auto_updates" => auto_updates, + "deprecated" => deprecated?, + "deprecation_date" => deprecation_date, + "deprecation_reason" => deprecation_reason, + "deprecation_replacement_formula" => deprecation_replacement_formula, + "deprecation_replacement_cask" => deprecation_replacement_cask, + "disabled" => disabled?, + "disable_date" => disable_date, + "disable_reason" => disable_reason, + "disable_replacement_formula" => disable_replacement_formula, + "disable_replacement_cask" => disable_replacement_cask, + "tap_git_head" => tap_git_head, + "languages" => languages, + "ruby_source_path" => ruby_source_path, + "ruby_source_checksum" => ruby_source_checksum, } end diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 0947d8d0a3..4de0e32c32 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -90,14 +90,12 @@ module Cask :deprecated?, :deprecation_date, :deprecation_reason, - :deprecation_replacement, :deprecation_replacement_formula, :deprecation_replacement_cask, :disable!, :disabled?, :disable_date, :disable_reason, - :disable_replacement, :disable_replacement_formula, :disable_replacement_cask, :discontinued?, # TODO: remove once discontinued? is removed (4.5.0) @@ -114,9 +112,8 @@ module Cask include OnSystem::MacOSAndLinux - attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, - :deprecation_replacement_formula, :deprecation_replacement_caks, :disable_date, - :disable_reason, :disable_replacement, :disable_replacement_formula, + attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement_formula, + :deprecation_replacement_cask, :disable_date, :disable_reason, :disable_replacement_formula, :disable_replacement_cask, :on_system_block_min_os def initialize(cask) @@ -533,8 +530,8 @@ module Cask # # @api public def deprecate!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) - if replacement_formula && replacement_cask - raise ArgumentError, "replacement_formula and replacement_cask specified!" + if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1 + raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!" end # TODO: deprecate in >= 4.5.0 @@ -550,9 +547,8 @@ module Cask return if @deprecation_date > Date.today @deprecation_reason = because - @deprecation_replacement = replacement - @deprecation_replacement_formula = replacement_formula - @deprecation_replacement_cask = replacement_cask + @deprecation_replacement_formula = replacement_formula.presence || replacement + @deprecation_replacement_cask = replacement_cask.presence || replacement @deprecated = true end @@ -562,8 +558,8 @@ module Cask # # @api public def disable!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) - if replacement_formula && replacement_cask - raise ArgumentError, "replacement_formula and replacement_cask specified!" + if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1 + raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!" end # TODO: deprecate in >= 4.5.0 @@ -579,17 +575,15 @@ module Cask if @disable_date > Date.today @deprecation_reason = because - @deprecation_replacement = replacement - @deprecation_replacement_formula = replacement_formula - @deprecation_replacement_cask = replacement_cask + @deprecation_replacement_formula = replacement_formula.presence || replacement + @deprecation_replacement_cask = replacement_cask.presence || replacement @deprecated = true return end @disable_reason = because - @disable_replacement = replacement - @disable_replacement_formula = replacement_formula - @disable_replacement_cask = replacement_cask + @disable_replacement_formula = replacement_formula.presence || replacement + @disable_replacement_cask = replacement_cask.presence || replacement @disabled = true end diff --git a/Library/Homebrew/deprecate_disable.rb b/Library/Homebrew/deprecate_disable.rb index 7bc9e72d6e..c09e95f30c 100644 --- a/Library/Homebrew/deprecate_disable.rb +++ b/Library/Homebrew/deprecate_disable.rb @@ -43,18 +43,17 @@ module DeprecateDisable sig { params( - formula: T.nilable(String), - cask: T.nilable(String), - not_typed: T.nilable(String), + formula: T.nilable(String), + cask: T.nilable(String), ).returns(T.nilable(String)) } - def replacement_with_type(formula, cask, not_typed) - if formula + def replacement_with_type(formula, cask) + if formula && formula == cask + formula + elsif formula "--formula #{formula}" elsif cask "--cask #{cask}" - else - not_typed end end @@ -98,13 +97,11 @@ module DeprecateDisable replacement_with_type( formula_or_cask.disable_replacement_formula, formula_or_cask.disable_replacement_cask, - formula_or_cask.disable_replacement, ) elsif formula_or_cask.deprecated? replacement_with_type( formula_or_cask.deprecation_replacement_formula, formula_or_cask.deprecation_replacement_cask, - formula_or_cask.deprecation_replacement, ) end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index db30b17da8..1eeed5aa13 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1477,13 +1477,6 @@ class Formula # @see .deprecate! delegate deprecation_reason: :"self.class" - # The replacement for this deprecated {Formula}. - # Returns `nil` if no replacement is specified or the formula is not deprecated. - # @!method deprecation_replacement - # @return [String] - # @see .deprecate! - delegate deprecation_replacement: :"self.class" - # The replacement formula for this deprecated {Formula}. # Returns `nil` if no replacement is specified or the formula is not deprecated. # @!method deprecation_replacement_formula @@ -1519,13 +1512,6 @@ class Formula # @see .disable! delegate disable_reason: :"self.class" - # The replacement for this disabled {Formula}. - # Returns `nil` if no replacement is specified or the formula is not disabled. - # @!method disable_replacement - # @return [String] - # @see .disable! - delegate disable_replacement: :"self.class" - # The replacement formula for this disabled {Formula}. # Returns `nil` if no replacement is specified or the formula is not disabled. # @!method disable_replacement_formula @@ -2560,13 +2546,11 @@ class Formula "deprecated" => deprecated?, "deprecation_date" => deprecation_date, "deprecation_reason" => deprecation_reason, - "deprecation_replacement" => deprecation_replacement, "deprecation_replacement_formula" => deprecation_replacement_formula, "deprecation_replacement_cask" => deprecation_replacement_cask, "disabled" => disabled?, "disable_date" => disable_date, "disable_reason" => disable_reason, - "disable_replacement" => disable_replacement, "disable_replacement_formula" => disable_replacement_formula, "disable_replacement_cask" => disable_replacement_cask, "post_install_defined" => post_install_defined?, @@ -4370,8 +4354,8 @@ class Formula ).void } def deprecate!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) - if replacement_formula && replacement_cask - raise ArgumentError, "replacement_formula and replacement_cask specified!" + if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1 + raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!" end # TODO: deprecate in >= 4.5.0 @@ -4387,9 +4371,8 @@ class Formula return if T.must(@deprecation_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) - @deprecation_replacement = T.let(replacement, T.nilable(String)) - @deprecation_replacement_formula = T.let(replacement_formula, T.nilable(String)) - @deprecation_replacement_cask = T.let(replacement_cask, T.nilable(String)) + @deprecation_replacement_formula = T.let(replacement_formula.presence || replacement, T.nilable(String)) + @deprecation_replacement_cask = T.let(replacement_cask.presence || replacement, T.nilable(String)) T.must(@deprecated = T.let(true, T.nilable(T::Boolean))) end @@ -4415,13 +4398,6 @@ class Formula sig { returns(T.any(NilClass, String, Symbol)) } attr_reader :deprecation_reason - # The replacement for a deprecated {Formula}. - # - # @return [nil] if no replacement was provided or the formula is not deprecated. - # @see .deprecate! - sig { returns(T.nilable(String)) } - attr_reader :deprecation_replacement - # The replacement formula for a deprecated {Formula}. # # @return [nil] if no replacement was provided or the formula is not deprecated. @@ -4471,8 +4447,8 @@ class Formula ).void } def disable!(date:, because:, replacement: nil, replacement_formula: nil, replacement_cask: nil) - if replacement_formula && replacement_cask - raise ArgumentError, "replacement_formula and replacement_cask specified!" + if [replacement, replacement_formula, replacement_cask].filter_map(&:presence).length > 1 + raise ArgumentError, "more than one of replacement, replacement_formula and/or replacement_cask specified!" end # TODO: deprecate in >= 4.5.0 @@ -4488,17 +4464,15 @@ class Formula if T.must(@disable_date) > Date.today @deprecation_reason = T.let(because, T.any(NilClass, String, Symbol)) - @deprecation_replacement = T.let(replacement, T.nilable(String)) - @deprecation_replacement_formula = T.let(replacement_formula, T.nilable(String)) - @deprecation_replacement_cask = T.let(replacement_cask, T.nilable(String)) + @deprecation_replacement_formula = T.let(replacement_formula.presence || replacement, T.nilable(String)) + @deprecation_replacement_cask = T.let(replacement_cask.presence || replacement, T.nilable(String)) @deprecated = T.let(true, T.nilable(T::Boolean)) return end @disable_reason = T.let(because, T.nilable(T.any(String, Symbol))) - @disable_replacement = T.let(replacement, T.nilable(String)) - @disable_replacement_formula = T.let(replacement_formula, T.nilable(String)) - @disable_replacement_cask = T.let(replacement_cask, T.nilable(String)) + @disable_replacement_formula = T.let(replacement_formula.presence || replacement, T.nilable(String)) + @disable_replacement_cask = T.let(replacement_cask.presence || replacement, T.nilable(String)) @disabled = T.let(true, T.nilable(T::Boolean)) end @@ -4525,13 +4499,6 @@ class Formula sig { returns(T.any(NilClass, String, Symbol)) } attr_reader :disable_reason - # The replacement for a disabled {Formula}. - # Returns `nil` if no reason was provided or the formula is not disabled. - # - # @see .disable! - sig { returns(T.nilable(String)) } - attr_reader :disable_replacement - # The replacement formula for a disabled {Formula}. # Returns `nil` if no reason was provided or the formula is not disabled. # diff --git a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi index 831a01c677..78c80320e5 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/cask/cask.rbi @@ -66,9 +66,6 @@ class Cask::Cask sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_reason(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def deprecation_replacement(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_replacement_formula(*args, &block); end @@ -90,9 +87,6 @@ class Cask::Cask sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_reason(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def disable_replacement(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_replacement_formula(*args, &block); end diff --git a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi index c0e149d99b..86118a7373 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi @@ -51,9 +51,6 @@ class Formula sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_reason(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def deprecation_replacement(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def deprecation_replacement_formula(*args, &block); end @@ -72,9 +69,6 @@ class Formula sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_reason(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } - def disable_replacement(*args, &block); end - sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } def disable_replacement_formula(*args, &block); end diff --git a/Library/Homebrew/test/deprecate_disable_spec.rb b/Library/Homebrew/test/deprecate_disable_spec.rb index 819191261c..0110e6e209 100644 --- a/Library/Homebrew/test/deprecate_disable_spec.rb +++ b/Library/Homebrew/test/deprecate_disable_spec.rb @@ -7,55 +7,52 @@ RSpec.describe DeprecateDisable do let(:disable_date) { deprecate_date >> DeprecateDisable::REMOVE_DISABLED_TIME_WINDOW } let(:deprecated_formula) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, deprecation_date: nil, disable_date: nil) + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, + deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_date) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, deprecation_date: deprecate_date, disable_date: nil) + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, + deprecation_date: deprecate_date, disable_date: nil) end let(:disabled_formula) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: "is broken", - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, disable_replacement: nil, + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:disabled_formula_with_date) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: :does_not_build, - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, disable_replacement: nil, + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: disable_date) end let(:deprecated_cask) do instance_double(Cask::Cask, deprecated?: true, disabled?: false, deprecation_reason: :discontinued, - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, deprecation_date: nil, disable_date: nil) + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, + deprecation_date: nil, disable_date: nil) end let(:disabled_cask) do instance_double(Cask::Cask, deprecated?: false, disabled?: true, disable_reason: nil, - deprecation_replacement: nil, deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil, disable_replacement: nil, + deprecation_replacement_formula: nil, deprecation_replacement_cask: nil, disable_replacement_formula: nil, disable_replacement_cask: nil, deprecation_date: nil, disable_date: nil) end let(:deprecated_formula_with_replacement) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_replacement: "foo", deprecation_date: nil, disable_date: nil) + deprecation_date: nil, disable_date: nil) end let(:disabled_formula_with_replacement) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: "is broken", - disable_replacement: "bar", deprecation_date: nil, disable_date: nil) + deprecation_date: nil, disable_date: nil) end let(:deprecated_cask_with_replacement) do instance_double(Cask::Cask, deprecated?: true, disabled?: false, deprecation_reason: :discontinued, - deprecation_replacement: "baz", deprecation_date: nil, disable_date: nil) + deprecation_date: nil, disable_date: nil) end let(:disabled_cask_with_replacement) do instance_double(Cask::Cask, deprecated?: false, disabled?: true, disable_reason: nil, - disable_replacement: "qux", deprecation_date: nil, disable_date: nil) + deprecation_date: nil, disable_date: nil) end before do @@ -136,13 +133,6 @@ RSpec.describe DeprecateDisable do .to eq "disabled!" end - it "returns a replacement message for a deprecated formula" do - allow(deprecated_formula_with_replacement).to receive_messages(deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil) - expect(described_class.message(deprecated_formula_with_replacement)) - .to eq "deprecated because it does not build!\nReplacement:\n brew install foo\n" - end - it "returns a replacement formula message for a deprecated formula" do allow(deprecated_formula_with_replacement).to receive_messages(deprecation_replacement_formula: "foo", deprecation_replacement_cask: nil) @@ -157,13 +147,6 @@ RSpec.describe DeprecateDisable do .to eq "deprecated because it does not build!\nReplacement:\n brew install --cask foo\n" end - it "returns a replacement message for a disabled formula" do - allow(disabled_formula_with_replacement).to receive_messages(disable_replacement_formula: nil, - disable_replacement_cask: nil) - expect(described_class.message(disabled_formula_with_replacement)) - .to eq "disabled because it is broken!\nReplacement:\n brew install bar\n" - end - it "returns a replacement formula message for a disabled formula" do allow(disabled_formula_with_replacement).to receive_messages(disable_replacement_formula: "bar", disable_replacement_cask: nil) @@ -178,13 +161,6 @@ RSpec.describe DeprecateDisable do .to eq "disabled because it is broken!\nReplacement:\n brew install --cask bar\n" end - it "returns a replacement message for a deprecated cask" do - allow(deprecated_cask_with_replacement).to receive_messages(deprecation_replacement_formula: nil, - deprecation_replacement_cask: nil) - expect(described_class.message(deprecated_cask_with_replacement)) - .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install baz\n" - end - it "returns a replacement formula message for a deprecated cask" do allow(deprecated_cask_with_replacement).to receive_messages(deprecation_replacement_formula: "baz", deprecation_replacement_cask: nil) @@ -199,13 +175,6 @@ RSpec.describe DeprecateDisable do .to eq "deprecated because it is discontinued upstream!\nReplacement:\n brew install --cask baz\n" end - it "returns a replacement message for a disabled cask" do - allow(disabled_cask_with_replacement).to receive_messages(disable_replacement_formula: nil, - disable_replacement_cask: nil) - expect(described_class.message(disabled_cask_with_replacement)) - .to eq "disabled!\nReplacement:\n brew install qux\n" - end - it "returns a replacement formula message for a disabled cask" do allow(disabled_cask_with_replacement).to receive_messages(disable_replacement_formula: "qux", disable_replacement_cask: nil) diff --git a/Library/Homebrew/test/support/fixtures/cask/everything.json b/Library/Homebrew/test/support/fixtures/cask/everything.json index b247e82a05..d3d981258f 100644 --- a/Library/Homebrew/test/support/fixtures/cask/everything.json +++ b/Library/Homebrew/test/support/fixtures/cask/everything.json @@ -95,11 +95,13 @@ "deprecated": false, "deprecation_date": null, "deprecation_reason": null, - "deprecation_replacement": null, + "deprecation_replacement_formula": null, + "deprecation_replacement_cask": null, "disabled": false, "disable_date": null, "disable_reason": null, - "disable_replacement": null, + "disable_replacement_formula": null, + "disable_replacement_cask": null, "tap_git_head": "abcdef1234567890abcdef1234567890abcdef12", "languages": [ "en",