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