Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecate! and disable! to casks #16292

Merged
merged 9 commits into from Dec 17, 2023
Merged
12 changes: 6 additions & 6 deletions Library/Homebrew/cask/audit.rb
Expand Up @@ -289,7 +289,7 @@ def audit_latest_with_auto_updates

sig { params(livecheck_result: T.any(NilClass, T::Boolean, Symbol)).void }
def audit_hosting_with_livecheck(livecheck_result: audit_livecheck_version)
return if cask.discontinued?
return if cask.deprecated? || cask.disabled?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need a new (strict?) audit for usage of caveats :discontinued.

return if cask.version&.latest?
return unless cask.url
return if block_url_offline?
Expand Down Expand Up @@ -544,7 +544,7 @@ def audit_livecheck_version
)
end

# Respect cask skip conditions (e.g. discontinued, latest, unversioned)
# Respect cask skip conditions (e.g. deprecated, disabled, latest, unversioned)
skip_info ||= Homebrew::Livecheck::SkipConditions.skip_information(cask)
return :skip if skip_info.present?

Expand Down Expand Up @@ -682,8 +682,8 @@ def audit_gitlab_prerelease_version

sig { void }
def audit_github_repository_archived
# Discontinued casks may have an archived repo.
return if cask.discontinued?
# Deprecated/disabled casks may have an archived repo.
return if cask.deprecated? || cask.disabled?

user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil?
Expand All @@ -696,8 +696,8 @@ def audit_github_repository_archived

sig { void }
def audit_gitlab_repository_archived
# Discontinued casks may have an archived repo.
return if cask.discontinued?
# Deprecated/disabled casks may have an archived repo.
return if cask.deprecated? || cask.disabled?

user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil?
Expand Down
6 changes: 6 additions & 0 deletions Library/Homebrew/cask/cask.rb
Expand Up @@ -331,6 +331,12 @@ def to_h
"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,
Comment on lines +334 to +339
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like disabled and deprecated are really only necessary on the formula side to aid with HTML generation for formulae.brew.sh. Probably be a good idea to add a follow-up to enable that for casks too.

Copy link
Member

@Bo98 Bo98 Dec 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to slim this, we could easily make the logic on the formule.brew.sh side be deprecation_date <= today.

"tap_git_head" => tap_git_head,
"languages" => languages,
"ruby_source_path" => ruby_source_path,
Expand Down
12 changes: 11 additions & 1 deletion Library/Homebrew/cask/cask.rbi
Expand Up @@ -20,7 +20,17 @@ module Cask

def desc; end

def discontinued?; end
def deprecated?; end

def deprecation_date; end

def deprecation_reason; end

def disabled?; end

def disable_date; end

def disable_reason; end

def homepage; end

Expand Down
10 changes: 10 additions & 0 deletions Library/Homebrew/cask/cask_loader.rb
Expand Up @@ -286,6 +286,16 @@
desc json_cask[:desc]
homepage json_cask[:homepage]

if (deprecation_date = json_cask[:deprecation_date].presence)
reason = DeprecateDisable.to_reason_string_or_symbol json_cask[:deprecation_reason], type: :cask
deprecate! date: deprecation_date, because: reason

Check warning on line 291 in Library/Homebrew/cask/cask_loader.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask_loader.rb#L291

Added line #L291 was not covered by tests
end

if (disable_date = json_cask[:disable_date].presence)
reason = DeprecateDisable.to_reason_string_or_symbol json_cask[:disable_reason], type: :cask
disable! date: disable_date, because: reason

Check warning on line 296 in Library/Homebrew/cask/cask_loader.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/cask_loader.rb#L296

Added line #L296 was not covered by tests
end

auto_updates json_cask[:auto_updates] unless json_cask[:auto_updates].nil?
conflicts_with(**json_cask[:conflicts_with]) if json_cask[:conflicts_with].present?

Expand Down
41 changes: 37 additions & 4 deletions Library/Homebrew/cask/dsl.rb
Expand Up @@ -84,6 +84,14 @@
:url,
:version,
:appdir,
:deprecate!,
:deprecated?,
:deprecation_date,
:deprecation_reason,
:disable!,
:disabled?,
:disable_date,
:disable_reason,
:discontinued?,
:livecheck,
:livecheckable?,
Expand All @@ -96,9 +104,9 @@
extend Predicable
include OnSystem::MacOSOnly

attr_reader :cask, :token
attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :disable_date, :disable_reason

attr_predicate :on_system_blocks_exist?
attr_predicate :on_system_blocks_exist?, :disabled?, :livecheckable?

def initialize(cask)
@cask = cask
Expand Down Expand Up @@ -316,9 +324,15 @@
end

def discontinued?
# odeprecated "`discontinued?`", "`deprecated?` or `disabled?`"
Rylan12 marked this conversation as resolved.
Show resolved Hide resolved
@caveats&.discontinued? == true
end

# TODO: replace with with attr_predicate once discontinued? is disabled
def deprecated?
@deprecated == true || @caveats&.discontinued? == true
end

# @api public
def auto_updates(auto_updates = nil)
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
Expand All @@ -337,8 +351,27 @@
@livecheck.instance_eval(&block)
end

def livecheckable?
@livecheckable == true
# @api public
def deprecate!(date:, because:)
@deprecation_date = Date.parse(date)
return if @deprecation_date > Date.today

@deprecation_reason = because
@deprecated = true
end

# @api public
def disable!(date:, because:)
@disable_date = Date.parse(date)

if @disable_date > Date.today
@deprecation_reason = because
@deprecated = true
return

Check warning on line 370 in Library/Homebrew/cask/dsl.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/dsl.rb#L369-L370

Added lines #L369 - L370 were not covered by tests
end

@disable_reason = because
@disabled = true
end

ORDINARY_ARTIFACT_CLASSES.each do |klass|
Expand Down
1 change: 1 addition & 0 deletions Library/Homebrew/cask/dsl/caveats.rb
Expand Up @@ -163,6 +163,7 @@ def eval_caveats(&block)
end

caveat :discontinued do
# odeprecated "`caveats :discontinued`", "`deprecate!`"
@discontinued = true
<<~EOS
#{@cask} has been officially discontinued upstream.
Expand Down
17 changes: 17 additions & 0 deletions Library/Homebrew/cask/exceptions.rb
Expand Up @@ -54,6 +54,23 @@
end
end

# Error when a cask cannot be installed.
#
# @api private
class CaskCannotBeInstalledError < AbstractCaskErrorWithToken
attr_reader :message

def initialize(token, message)
super(token)
@message = message

Check warning on line 65 in Library/Homebrew/cask/exceptions.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/exceptions.rb#L64-L65

Added lines #L64 - L65 were not covered by tests
end

sig { returns(String) }
def to_s
"Cask '#{token}' has been #{message}"

Check warning on line 70 in Library/Homebrew/cask/exceptions.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/exceptions.rb#L70

Added line #L70 was not covered by tests
end
end

# Error when a cask conflicts with another cask.
#
# @api private
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/cask/info.rb
Expand Up @@ -12,6 +12,8 @@ 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
output << installation_info(cask)
repo = repo_info(cask)
output << "#{repo}\n" if repo
Expand Down
13 changes: 13 additions & 0 deletions Library/Homebrew/cask/installer.rb
Expand Up @@ -93,6 +93,7 @@ def install
old_config = @cask.config
predecessor = @cask if reinstall? && @cask.installed?

check_deprecate_disable
check_conflicts

print caveats
Expand Down Expand Up @@ -124,6 +125,18 @@ def install
raise
end

def check_deprecate_disable
deprecate_disable_type = DeprecateDisable.type(@cask)
return if deprecate_disable_type.nil?

case deprecate_disable_type
when :deprecated
opoo "#{@cask.token} has been #{DeprecateDisable.message(@cask)}"
when :disabled
raise CaskCannotBeInstalledError.new(@cask, DeprecateDisable.message(@cask))
end
end

def check_conflicts
return unless @cask.conflicts_with

Expand Down
10 changes: 2 additions & 8 deletions Library/Homebrew/cmd/info.rb
Expand Up @@ -278,14 +278,8 @@
puts formula.desc if formula.desc
puts Formatter.url(formula.homepage) if formula.homepage

deprecate_disable_type, deprecate_disable_reason = DeprecateDisable.deprecate_disable_info formula
if deprecate_disable_type.present?
if deprecate_disable_reason.present?
puts "#{deprecate_disable_type.capitalize} because it #{deprecate_disable_reason}!"
else
puts "#{deprecate_disable_type.capitalize}!"
end
end
deprecate_disable_info_string = DeprecateDisable.message(formula)

Check warning on line 281 in Library/Homebrew/cmd/info.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cmd/info.rb#L281

Added line #L281 was not covered by tests
puts deprecate_disable_info_string.capitalize if deprecate_disable_info_string.present?

conflicts = formula.conflicts.map do |conflict|
reason = " (because #{conflict.reason})" if conflict.reason
Expand Down
47 changes: 36 additions & 11 deletions Library/Homebrew/deprecate_disable.rb
Expand Up @@ -7,7 +7,7 @@
module DeprecateDisable
module_function

DEPRECATE_DISABLE_REASONS = {
FORMULA_DEPRECATE_DISABLE_REASONS = {
does_not_build: "does not build",
no_license: "has no license",
repo_archived: "has an archived upstream repository",
Expand All @@ -22,19 +22,44 @@ module DeprecateDisable
"We can re-package this once upstream has confirmed that they retagged their release",
}.freeze

def deprecate_disable_info(formula)
if formula.deprecated?
type = :deprecated
reason = formula.deprecation_reason
elsif formula.disabled?
type = :disabled
reason = formula.disable_reason
CASK_DEPRECATE_DISABLE_REASONS = {
discontinued: "is discontinued upstream",
}.freeze

def type(formula_or_cask)
return :deprecated if formula_or_cask.deprecated?

:disabled if formula_or_cask.disabled?
end

def message(formula_or_cask)
return if type(formula_or_cask).blank?

reason = if formula_or_cask.deprecated?
formula_or_cask.deprecation_reason
elsif formula_or_cask.disabled?
formula_or_cask.disable_reason
end

reason = if formula_or_cask.is_a?(Formula) && FORMULA_DEPRECATE_DISABLE_REASONS.key?(reason)
FORMULA_DEPRECATE_DISABLE_REASONS[reason]
elsif formula_or_cask.is_a?(Cask::Cask) && CASK_DEPRECATE_DISABLE_REASONS.key?(reason)
CASK_DEPRECATE_DISABLE_REASONS[reason]
else
return
reason
end

reason = DEPRECATE_DISABLE_REASONS[reason] if DEPRECATE_DISABLE_REASONS.key? reason
return "#{type(formula_or_cask)} because it #{reason}!" if reason.present?

"#{type(formula_or_cask)}!"
end

def to_reason_string_or_symbol(string, type:)
if (type == :formula && FORMULA_DEPRECATE_DISABLE_REASONS.key?(string&.to_sym)) ||
(type == :cask && CASK_DEPRECATE_DISABLE_REASONS.key?(string&.to_sym))
return string.to_sym
end

[type, reason]
string
end
end
12 changes: 12 additions & 0 deletions Library/Homebrew/diagnostic.rb
Expand Up @@ -642,6 +642,18 @@
EOS
end

def check_cask_deprecated_disabled
deprecated_or_disabled = Cask::Caskroom.casks.select(&:deprecated?)
deprecated_or_disabled += Cask::Caskroom.casks.select(&:disabled?)

Check warning on line 647 in Library/Homebrew/diagnostic.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/diagnostic.rb#L646-L647

Added lines #L646 - L647 were not covered by tests
return if deprecated_or_disabled.empty?

<<~EOS

Check warning on line 650 in Library/Homebrew/diagnostic.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/diagnostic.rb#L650

Added line #L650 was not covered by tests
Some installed casks are deprecated or disabled.
You should find replacements for the following casks:
#{deprecated_or_disabled.sort_by(&:token).uniq * "\n "}
EOS
end

sig { returns(T.nilable(String)) }
def check_git_status
return unless Utils::Git.available?
Expand Down
4 changes: 2 additions & 2 deletions Library/Homebrew/formula.rb
Expand Up @@ -3550,7 +3550,7 @@ def pour_bottle?(only_if: nil, &block)
# <pre>deprecate! date: "2020-08-27", because: :unmaintained</pre>
# <pre>deprecate! date: "2020-08-27", because: "has been replaced by foo"</pre>
# @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae
# @see DeprecateDisable::DEPRECATE_DISABLE_REASONS
# @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS
def deprecate!(date:, because:)
@deprecation_date = Date.parse(date)
return if @deprecation_date > Date.today
Expand Down Expand Up @@ -3585,7 +3585,7 @@ def deprecated?
# <pre>disable! date: "2020-08-27", because: :does_not_build</pre>
# <pre>disable! date: "2020-08-27", because: "has been replaced by foo"</pre>
# @see https://docs.brew.sh/Deprecating-Disabling-and-Removing-Formulae
# @see DeprecateDisable::DEPRECATE_DISABLE_REASONS
# @see DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS
def disable!(date:, because:)
@disable_date = Date.parse(date)

Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/formula_auditor.rb
Expand Up @@ -412,7 +412,7 @@ def audit_deps
return if formula.disabled?

return if formula.deprecated? &&
formula.deprecation_reason != DeprecateDisable::DEPRECATE_DISABLE_REASONS[:versioned_formula]
formula.deprecation_reason != DeprecateDisable::FORMULA_DEPRECATE_DISABLE_REASONS[:versioned_formula]

problem <<~EOS
#{formula.full_name} contains conflicting version recursive dependencies:
Expand Down
20 changes: 7 additions & 13 deletions Library/Homebrew/formula_installer.rb
Expand Up @@ -200,21 +200,15 @@ def install_bottle_for?(dep, build)

sig { void }
def prelude
type, reason = DeprecateDisable.deprecate_disable_info formula
if type.present?
case type
deprecate_disable_type = DeprecateDisable.type(formula)
if deprecate_disable_type.present?
message = "#{formula.full_name} has been #{DeprecateDisable.message(formula)}"

case deprecate_disable_type
when :deprecated
if reason.present?
opoo "#{formula.full_name} has been deprecated because it #{reason}!"
else
opoo "#{formula.full_name} has been deprecated!"
end
opoo message
when :disabled
if reason.present?
raise CannotInstallFormulaError, "#{formula.full_name} has been disabled because it #{reason}!"
end

raise CannotInstallFormulaError, "#{formula.full_name} has been disabled!"
raise CannotInstallFormulaError, message
end
end

Expand Down