diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index 08ac1b3340d0c..75a9e00b4280d 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -363,38 +363,40 @@ def eql?(other) 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" => (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, - "disabled" => disabled?, - "disable_date" => disable_date, - "disable_reason" => disable_reason, - "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" => (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, } end @@ -415,11 +417,13 @@ def to_internal_api_hash if deprecation_date api_hash["deprecation_date"] = deprecation_date api_hash["deprecation_reason"] = deprecation_reason + api_hash["deprecation_replacement"] = deprecation_replacement end if disable_date api_hash["disable_date"] = disable_date api_hash["disable_reason"] = disable_reason + api_hash["disable_replacement"] = disable_replacement end if (url_specs_hash = url_specs).present? diff --git a/Library/Homebrew/cask/cask.rbi b/Library/Homebrew/cask/cask.rbi index 9ffa1b2428119..9605255e043c0 100644 --- a/Library/Homebrew/cask/cask.rbi +++ b/Library/Homebrew/cask/cask.rbi @@ -26,12 +26,16 @@ module Cask def deprecation_reason; end + def deprecation_replacement; end + def disabled?; end def disable_date; end def disable_reason; end + def disable_replacement; end + def homepage; end def language; end diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index bb5d36cf7ec44..0b6a63a0d3a6e 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -87,10 +87,12 @@ class DSL :deprecated?, :deprecation_date, :deprecation_reason, + :deprecation_replacement, :disable!, :disabled?, :disable_date, :disable_reason, + :disable_replacement, :discontinued?, # TODO: remove once discontinued? is removed (4.5.0) :livecheck, :livecheckable?, @@ -105,8 +107,8 @@ class DSL extend Attrable include OnSystem::MacOSOnly - attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :disable_date, :disable_reason, - :on_system_block_min_os + attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, :disable_date, + :disable_reason, :disable_replacement, :on_system_block_min_os attr_predicate :deprecated?, :disabled?, :livecheckable?, :on_system_blocks_exist?, :depends_on_set_in_block? @@ -442,11 +444,12 @@ def livecheck(&block) # NOTE: A warning will be shown when trying to install this cask. # # @api public - def deprecate!(date:, because:) + def deprecate!(date:, because:, replacement: nil) @deprecation_date = Date.parse(date) return if @deprecation_date > Date.today @deprecation_reason = because + @deprecation_replacement = replacement @deprecated = true end @@ -455,16 +458,18 @@ def deprecate!(date:, because:) # NOTE: An error will be thrown when trying to install this cask. # # @api public - def disable!(date:, because:) + def disable!(date:, because:, replacement: nil) @disable_date = Date.parse(date) if @disable_date > Date.today @deprecation_reason = because + @deprecation_replacement = replacement @deprecated = true return end @disable_reason = because + @disable_replacement = replacement @disabled = true end diff --git a/Library/Homebrew/cask/info.rb b/Library/Homebrew/cask/info.rb index e2e6b3f4de74d..16515d665944d 100644 --- a/Library/Homebrew/cask/info.rb +++ b/Library/Homebrew/cask/info.rb @@ -12,7 +12,10 @@ def self.get_info(cask) output = "#{title_info(cask)}\n" output << "#{Formatter.url(cask.homepage)}\n" if cask.homepage deprecate_disable = DeprecateDisable.message(cask) - output << "#{deprecate_disable.capitalize}\n" if deprecate_disable + if deprecate_disable.present? + deprecate_disable.tap { |message| message[0] = message[0].upcase } + output << "#{deprecate_disable}\n" + end output << "#{installation_info(cask)}\n" repo = repo_info(cask) output << "#{repo}\n" if repo diff --git a/Library/Homebrew/deprecate_disable.rb b/Library/Homebrew/deprecate_disable.rb index c83f26ee74c3b..18c8054383f97 100644 --- a/Library/Homebrew/deprecate_disable.rb +++ b/Library/Homebrew/deprecate_disable.rb @@ -75,6 +75,14 @@ def message(formula_or_cask) end end + replacement = if formula_or_cask.deprecated? + formula_or_cask.deprecation_replacement + elsif formula_or_cask.disabled? + formula_or_cask.disable_replacement + end + + message += "\nConsider replacing it with:\n brew install #{replacement}" if replacement.present? + message end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index ee612294fcbea..52ce200e47a77 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1540,6 +1540,13 @@ def link_overwrite?(path) # @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" + # Whether this {Formula} is disabled (i.e. cannot be installed). # Defaults to false. # @!method disabled? @@ -1561,6 +1568,13 @@ def link_overwrite?(path) # @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" + sig { returns(T::Boolean) } def skip_cxxstdlib_check? false @@ -2447,9 +2461,11 @@ def to_hash "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, @@ -2540,11 +2556,13 @@ def to_internal_api_hash if deprecation_date api_hash["deprecation_date"] = deprecation_date api_hash["deprecation_reason"] = deprecation_reason + api_hash["deprecation_replacement"] = deprecation_replacement end if disable_date api_hash["disable_date"] = disable_date api_hash["disable_reason"] = disable_reason + api_hash["disable_replacement"] = disable_replacement end api_hash @@ -4238,14 +4256,19 @@ def pour_bottle?(only_if: nil, &block) # deprecate! date: "2020-08-27", because: "has been replaced by foo" # ``` # + # ```ruby + # deprecate! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" + # ``` + # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public - def deprecate!(date:, because:) + def deprecate!(date:, because:, replacement: nil) @deprecation_date = Date.parse(date) return if @deprecation_date > Date.today @deprecation_reason = because + @deprecation_replacement = replacement @deprecated = true end @@ -4271,6 +4294,13 @@ def deprecated? # @see .deprecate! attr_reader :deprecation_reason + # The replacement for a deprecated {Formula}. + # + # @return [nil] if no replacement was provided or the formula is not deprecated. + # @return [String] + # @see .deprecate! + attr_reader :deprecation_replacement + # 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. @@ -4285,19 +4315,25 @@ def deprecated? # disable! date: "2020-08-27", because: "has been replaced by foo" # ``` # + # ```ruby + # disable! date: "2020-08-27", because: "has been replaced by foo", replacement: "foo" + # ``` + # # @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae # @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS # @api public - def disable!(date:, because:) + def disable!(date:, because:, replacement: nil) @disable_date = Date.parse(date) if @disable_date > Date.today @deprecation_reason = because + @deprecation_replacement = replacement @deprecated = true return end @disable_reason = because + @disable_replacement = replacement @disabled = true end @@ -4324,6 +4360,13 @@ def disabled? # @see .disable! attr_reader :disable_reason + # The replacement for a disabled {Formula}. + # Returns `nil` if no reason was provided or the formula is not disabled. + # + # @return [String] + # @see .disable! + attr_reader :disable_replacement + # Permit overwriting certain files while linking. # # ### Examples diff --git a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi index 06810994c33f3..c8ebc68cbd840 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/formula.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/formula.rbi @@ -51,6 +51,9 @@ 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 deps(*args, &block); end @@ -63,6 +66,9 @@ 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::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 9f20602391630..4eb7aab0edd17 100644 --- a/Library/Homebrew/test/deprecate_disable_spec.rb +++ b/Library/Homebrew/test/deprecate_disable_spec.rb @@ -5,30 +5,61 @@ RSpec.describe DeprecateDisable do let(:deprecated_formula) do instance_double(Formula, deprecated?: true, disabled?: false, deprecation_reason: :does_not_build, - deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_date: nil, disable_date: nil) end let(:disabled_formula) do instance_double(Formula, deprecated?: false, disabled?: true, disable_reason: "is broken", - deprecation_date: nil, disable_date: nil) + disable_replacement: nil, deprecation_date: nil, disable_date: nil) end let(:deprecated_cask) do instance_double(Cask::Cask, deprecated?: true, disabled?: false, deprecation_reason: :discontinued, - deprecation_date: nil, disable_date: nil) + deprecation_replacement: nil, deprecation_date: nil, disable_date: nil) end let(:disabled_cask) do instance_double(Cask::Cask, deprecated?: false, disabled?: true, disable_reason: nil, - deprecation_date: nil, disable_date: nil) + disable_replacement: 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) + 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) + 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) + 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) end before do - allow(deprecated_formula).to receive(:is_a?).with(Formula).and_return(true) - allow(deprecated_formula).to receive(:is_a?).with(Cask::Cask).and_return(false) - allow(disabled_formula).to receive(:is_a?).with(Formula).and_return(true) - allow(disabled_formula).to receive(:is_a?).with(Cask::Cask).and_return(false) - allow(deprecated_cask).to receive(:is_a?).with(Formula).and_return(false) - allow(deprecated_cask).to receive(:is_a?).with(Cask::Cask).and_return(true) - allow(disabled_cask).to receive(:is_a?).with(Formula).and_return(false) - allow(disabled_cask).to receive(:is_a?).with(Cask::Cask).and_return(true) + formulae = [ + deprecated_formula, + disabled_formula, + deprecated_formula_with_replacement, + disabled_formula_with_replacement, + ] + + casks = [ + deprecated_cask, + disabled_cask, + deprecated_cask_with_replacement, + disabled_cask_with_replacement, + ] + + formulae.each do |f| + allow(f).to receive(:is_a?).with(Formula).and_return(true) + allow(f).to receive(:is_a?).with(Cask::Cask).and_return(false) + end + + casks.each do |c| + allow(c).to receive(:is_a?).with(Formula).and_return(false) + allow(c).to receive(:is_a?).with(Cask::Cask).and_return(true) + end end describe "::type" do @@ -69,6 +100,26 @@ expect(described_class.message(disabled_cask)) .to eq "disabled!" end + + it "returns a replacement message for a deprecated formula" do + expect(described_class.message(deprecated_formula_with_replacement)) + .to eq "deprecated because it does not build!\nConsider replacing it with:\n brew install foo" + end + + it "returns a replacement message for a disabled formula" do + expect(described_class.message(disabled_formula_with_replacement)) + .to eq "disabled because it is broken!\nConsider replacing it with:\n brew install bar" + end + + it "returns a replacement message for a deprecated cask" do + expect(described_class.message(deprecated_cask_with_replacement)) + .to eq "deprecated because it is discontinued upstream!\nConsider replacing it with:\n brew install baz" + end + + it "returns a replacement message for a disabled cask" do + expect(described_class.message(disabled_cask_with_replacement)) + .to eq "disabled!\nConsider replacing it with:\n brew install qux" + end end describe "::to_reason_string_or_symbol" do diff --git a/Library/Homebrew/test/support/fixtures/cask/everything.json b/Library/Homebrew/test/support/fixtures/cask/everything.json index d290bd45d16ea..712f3e9bd0daf 100644 --- a/Library/Homebrew/test/support/fixtures/cask/everything.json +++ b/Library/Homebrew/test/support/fixtures/cask/everything.json @@ -92,9 +92,11 @@ "deprecated": false, "deprecation_date": null, "deprecation_reason": null, + "deprecation_replacement": null, "disabled": false, "disable_date": null, "disable_reason": null, + "disable_replacement": null, "tap_git_head": "abcdef1234567890abcdef1234567890abcdef12", "languages": [ "en",