brew/Library/Homebrew/cmd/update-report.rb

958 lines
31 KiB
Ruby
Raw Normal View History

2023-03-06 08:20:40 -08:00
# typed: true
# frozen_string_literal: true
require "migrator"
require "formulary"
2023-04-08 14:10:58 +02:00
require "cask/cask_loader"
require "cask/migrator"
require "descriptions"
require "cleanup"
require "description_cache_store"
2019-04-17 18:25:08 +09:00
require "cli/parser"
require "settings"
require "linuxbrew-core-migration"
module Homebrew
2016-09-26 01:44:51 +02:00
module_function
def auto_update_header(args:)
@auto_update_header ||= begin
ohai "Auto-updated Homebrew!" if args.auto_update?
true
end
end
2020-10-20 12:03:48 +02:00
sig { returns(CLI::Parser) }
2019-01-30 21:30:45 +00:00
def update_report_args
Homebrew::CLI::Parser.new do
description <<~EOS
2019-01-30 21:30:45 +00:00
The Ruby implementation of `brew update`. Never called manually.
EOS
switch "--auto-update", "--preinstall",
2019-04-30 08:44:35 +01:00
description: "Run in 'auto-update' mode (faster, less output)."
switch "-f", "--force",
2022-06-28 10:09:59 +01:00
description: "Treat installed and updated formulae as if they are from " \
"the same taps and migrate them anyway."
2020-07-30 18:40:10 +02:00
2019-01-30 21:30:45 +00:00
hide_from_man_page!
end
end
def update_report
return output_update_report if $stdout.tty?
redirect_stdout($stderr) do
output_update_report
end
end
def output_update_report
args = update_report_args.parse
2019-01-30 21:30:45 +00:00
# Run `brew update` (again) if we've got a linuxbrew-core CoreTap
if CoreTap.instance.installed? && CoreTap.instance.linuxbrew_core? &&
ENV["HOMEBREW_LINUXBREW_CORE_MIGRATION"].blank?
ohai "Re-running `brew update` for linuxbrew-core migration"
2023-02-10 08:59:51 +00:00
if Homebrew::EnvConfig.core_git_remote != HOMEBREW_CORE_DEFAULT_GIT_REMOTE
opoo <<~EOS
HOMEBREW_CORE_GIT_REMOTE was set: #{Homebrew::EnvConfig.core_git_remote}.
It has been unset for the migration.
You may need to change this from a linuxbrew-core mirror to a homebrew-core one.
EOS
end
ENV.delete("HOMEBREW_CORE_GIT_REMOTE")
2023-02-10 08:59:51 +00:00
if Homebrew::EnvConfig.bottle_domain != HOMEBREW_BOTTLE_DEFAULT_DOMAIN
opoo <<~EOS
HOMEBREW_BOTTLE_DOMAIN was set: #{Homebrew::EnvConfig.bottle_domain}.
It has been unset for the migration.
You may need to change this from a Linuxbrew package mirror to a Homebrew one.
EOS
end
ENV.delete("HOMEBREW_BOTTLE_DOMAIN")
ENV["HOMEBREW_LINUXBREW_CORE_MIGRATION"] = "1"
FileUtils.rm_f HOMEBREW_LOCKS/"update"
update_args = []
update_args << "--auto-update" if args.auto_update?
update_args << "--force" if args.force?
exec HOMEBREW_BREW_FILE, "update", *update_args
end
if ENV["HOMEBREW_ADDITIONAL_GOOGLE_ANALYTICS_ID"].present?
opoo "HOMEBREW_ADDITIONAL_GOOGLE_ANALYTICS_ID is now a no-op so can be unset."
puts "All Homebrew Google Analytics code and data was destroyed."
2019-11-22 09:08:31 +00:00
end
if ENV["HOMEBREW_NO_GOOGLE_ANALYTICS"].present?
opoo "HOMEBREW_NO_GOOGLE_ANALYTICS is now a no-op so can be unset."
puts "All Homebrew Google Analytics code and data was destroyed."
end
unless args.quiet?
analytics_message
donation_message
install_from_api_message
untap_message
end
install_core_tap_if_necessary
updated = false
new_tag = nil
initial_revision = ENV["HOMEBREW_UPDATE_BEFORE"].to_s
current_revision = ENV["HOMEBREW_UPDATE_AFTER"].to_s
odie "update-report should not be called directly!" if initial_revision.empty? || current_revision.empty?
if initial_revision != current_revision
auto_update_header args: args
updated = true
old_tag = Settings.read "latesttag"
new_tag = Utils.popen_read(
2021-01-01 17:54:59 +09:00
"git", "-C", HOMEBREW_REPOSITORY, "tag", "--list", "--sort=-version:refname", "*.*"
).lines.first.chomp
Settings.write "latesttag", new_tag if new_tag != old_tag
2022-07-06 23:24:01 -07:00
if new_tag == old_tag
2022-07-07 14:17:00 -07:00
ohai "Updated Homebrew from #{shorten_revision(initial_revision)} to #{shorten_revision(current_revision)}."
2022-07-06 23:24:01 -07:00
elsif old_tag.blank?
ohai "Updated Homebrew from #{shorten_revision(initial_revision)} " \
"to #{new_tag} (#{shorten_revision(current_revision)})."
else
2022-07-06 23:15:38 -07:00
ohai "Updated Homebrew from #{old_tag} (#{shorten_revision(initial_revision)}) " \
"to #{new_tag} (#{shorten_revision(current_revision)})."
end
end
# Check if we can parse the JSON and do any Ruby-side follow-up.
unless Homebrew::EnvConfig.no_install_from_api?
Homebrew::API::Formula.write_names_and_aliases
Homebrew::API::Cask.write_names
end
Homebrew.failed = true if ENV["HOMEBREW_UPDATE_FAILED"]
return if Homebrew::EnvConfig.disable_load_formula?
migrate_gcc_dependents_if_needed
hub = ReporterHub.new
updated_taps = []
Tap.each do |tap|
next if !tap.git? || tap.git_repo.origin_url.nil?
2023-07-13 19:28:39 +01:00
next if (tap.core_tap? || tap.core_cask_tap?) && !Homebrew::EnvConfig.no_install_from_api?
2018-09-17 02:45:00 +02:00
if ENV["HOMEBREW_MIGRATE_LINUXBREW_FORMULAE"].present? && tap.core_tap? &&
Settings.read("linuxbrewmigrated") != "true"
ohai "Migrating formulae from linuxbrew-core to homebrew-core"
LINUXBREW_CORE_MIGRATION_LIST.each do |name|
begin
formula = Formula[name]
rescue FormulaUnavailableError
next
end
next unless formula.any_version_installed?
keg = formula.installed_kegs.last
tab = Tab.for_keg(keg)
# force a `brew upgrade` from the linuxbrew-core version to the homebrew-core version (even if lower)
tab.source["versions"]["version_scheme"] = -1
tab.write
end
Settings.write "linuxbrewmigrated", true
end
begin
reporter = Reporter.new(tap)
rescue Reporter::ReporterRevisionUnsetError => e
2023-03-06 08:20:40 -08:00
onoe "#{e.message}\n#{e.backtrace&.join("\n")}" if Homebrew::EnvConfig.developer?
next
end
if reporter.updated?
updated_taps << tap.name
hub.add(reporter, auto_update: args.auto_update?)
end
end
# If we're installing from the API: we cannot use Git to check for #
# differences in packages so instead use {formula,cask}_names.txt to do so.
# The first time this runs: we won't yet have a base state
# ({formula,cask}_names.before.txt) to compare against so we don't output a
# anything and just copy the files for next time.
unless Homebrew::EnvConfig.no_install_from_api?
api_cache = Homebrew::API::HOMEBREW_CACHE_API
core_tap = CoreTap.instance
2023-07-13 19:28:39 +01:00
cask_tap = CoreCaskTap.instance
[
[:formula, core_tap, core_tap.formula_dir],
[:cask, cask_tap, cask_tap.cask_dir],
].each do |type, tap, dir|
names_txt = api_cache/"#{type}_names.txt"
next unless names_txt.exist?
names_before_txt = api_cache/"#{type}_names.before.txt"
if names_before_txt.exist?
reporter = Reporter.new(
tap,
api_names_txt: names_txt,
api_names_before_txt: names_before_txt,
api_dir_prefix: dir,
)
if reporter.updated?
updated_taps << tap.name
hub.add(reporter, auto_update: args.auto_update?)
end
else
FileUtils.cp names_txt, names_before_txt
end
end
end
unless updated_taps.empty?
auto_update_header args: args
puts "Updated #{Utils.pluralize("tap", updated_taps.count, include_count: true)} (#{updated_taps.to_sentence})."
updated = true
end
if updated
if hub.empty?
puts no_changes_message unless args.quiet?
else
if ENV.fetch("HOMEBREW_UPDATE_REPORT_ONLY_INSTALLED", false)
opoo "HOMEBREW_UPDATE_REPORT_ONLY_INSTALLED is now the default behaviour, " \
"so you can unset it from your environment."
end
hub.dump(auto_update: args.auto_update?) unless args.quiet?
hub.reporters.each(&:migrate_tap_migration)
2023-04-08 14:10:58 +02:00
hub.reporters.each(&:migrate_cask_rename)
hub.reporters.each { |r| r.migrate_formula_rename(force: args.force?, verbose: args.verbose?) }
CacheStoreDatabase.use(:descriptions) do |db|
DescriptionCacheStore.new(db)
.update_from_report!(hub)
end
CacheStoreDatabase.use(:cask_descriptions) do |db|
CaskDescriptionCacheStore.new(db)
.update_from_report!(hub)
end
end
puts if args.auto_update?
elsif !args.auto_update? && !ENV["HOMEBREW_UPDATE_FAILED"] && !ENV["HOMEBREW_MIGRATE_LINUXBREW_FORMULAE"]
puts "Already up-to-date." unless args.quiet?
end
Commands.rebuild_commands_completion_list
link_completions_manpages_and_docs
Tap.each(&:link_completions_and_manpages)
2021-05-06 10:03:21 +09:00
failed_fetch_dirs = ENV["HOMEBREW_MISSING_REMOTE_REF_DIRS"]&.split("\n")
if failed_fetch_dirs.present?
failed_fetch_taps = failed_fetch_dirs.map { |dir| Tap.from_path(dir) }
ofail <<~EOS
Some taps failed to update!
The following taps can not read their remote branches:
#{failed_fetch_taps.join("\n ")}
This is happening because the remote branch was renamed or deleted.
Reset taps to point to the correct remote branches by running `brew tap --repair`
EOS
end
2022-06-30 18:41:44 -07:00
return if new_tag.blank? || new_tag == old_tag || args.quiet?
puts
2022-07-06 23:15:38 -07:00
new_major_version, new_minor_version, new_patch_version = new_tag.split(".").map(&:to_i)
old_major_version, old_minor_version = (old_tag.split(".")[0, 2]).map(&:to_i) if old_tag.present?
if old_tag.blank? || new_major_version > old_major_version \
|| new_minor_version > old_minor_version
puts <<~EOS
2022-07-06 23:15:38 -07:00
The #{new_major_version}.#{new_minor_version}.0 release notes are available on the Homebrew Blog:
#{Formatter.url("https://brew.sh/blog/#{new_major_version}.#{new_minor_version}.0")}
EOS
end
return if new_patch_version.zero?
puts <<~EOS
2022-07-06 23:15:38 -07:00
The #{new_tag} changelog can be found at:
#{Formatter.url("https://github.com/Homebrew/brew/releases/tag/#{new_tag}")}
EOS
end
def no_changes_message
"No changes to formulae or casks."
end
def shorten_revision(revision)
Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "rev-parse", "--short", revision).chomp
end
def install_core_tap_if_necessary
return if ENV["HOMEBREW_UPDATE_TEST"]
return unless Homebrew::EnvConfig.no_install_from_api?
return if Homebrew::EnvConfig.automatically_set_no_install_from_api?
return if CoreTap.instance.installed?
2018-09-17 02:45:00 +02:00
CoreTap.ensure_installed!
2023-03-06 08:20:40 -08:00
revision = CoreTap.instance.git_head
ENV["HOMEBREW_UPDATE_BEFORE_HOMEBREW_HOMEBREW_CORE"] = revision
ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision
end
def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
command = "brew update"
Utils::Link.link_completions(repository, command)
Utils::Link.link_manpages(repository, command)
Utils::Link.link_docs(repository, command)
rescue => e
2017-10-15 02:28:32 +02:00
ofail <<~EOS
Failed to link all completions, docs and manpages:
#{e}
EOS
end
def migrate_gcc_dependents_if_needed
2023-01-28 21:38:40 +09:00
# do nothing
end
def analytics_message
return if Utils::Analytics.messages_displayed?
return if Utils::Analytics.no_message_output?
if Utils::Analytics.disabled? && !Utils::Analytics.influx_message_displayed?
ohai "Homebrew's analytics have entirely moved to our InfluxDB instance in the EU."
puts "We gather less data than before and have destroyed all Google Analytics data:"
puts " #{Formatter.url("https://docs.brew.sh/Analytics")}#{Tty.reset}"
puts "Please reconsider re-enabling analytics to help our volunteer maintainers with:"
puts " brew analytics on"
elsif !Utils::Analytics.disabled?
ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1"
# Use the shell's audible bell.
print "\a"
# Use an extra newline and bold to avoid this being missed.
ohai "Homebrew collects anonymous analytics."
puts <<~EOS
#{Tty.bold}Read the analytics documentation (and how to opt-out) here:
#{Formatter.url("https://docs.brew.sh/Analytics")}#{Tty.reset}
No analytics have been recorded yet (nor will be during this `brew` run).
EOS
end
# Consider the messages possibly missed if not a TTY.
Utils::Analytics.messages_displayed! if $stdout.tty?
end
def donation_message
return if Settings.read("donationmessage") == "true"
ohai "Homebrew is run entirely by unpaid volunteers. Please consider donating:"
puts " #{Formatter.url("https://github.com/Homebrew/brew#donations")}\n\n"
# Consider the message possibly missed if not a TTY.
Settings.write "donationmessage", true if $stdout.tty?
end
def install_from_api_message
return if Settings.read("installfromapimessage") == "true"
api_auto_update_secs = Homebrew::EnvConfig.api_auto_update_secs.to_i
api_auto_update_secs_default = Homebrew::EnvConfig::ENVS.fetch(:HOMEBREW_API_AUTO_UPDATE_SECS).fetch(:default)
auto_update_secs_set = api_auto_update_secs.positive? && api_auto_update_secs != api_auto_update_secs_default
no_auto_update_set = Homebrew::EnvConfig.no_auto_update? &&
!ENV["HOMEBREW_GITHUB_HOSTED_RUNNER"] &&
!ENV["GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED"]
no_install_from_api_set = Homebrew::EnvConfig.no_install_from_api? &&
!Homebrew::EnvConfig.automatically_set_no_install_from_api? &&
!Homebrew::EnvConfig.install_from_api_unsupported?
return if !no_auto_update_set && !no_install_from_api_set && !auto_update_secs_set
ohai "You have set:"
puts " HOMEBREW_NO_AUTO_UPDATE" if no_auto_update_set
puts " HOMEBREW_API_AUTO_UPDATE_SECS" if auto_update_secs_set
puts " HOMEBREW_NO_INSTALL_FROM_API" if no_install_from_api_set
puts "but we have dramatically sped up and fixed many bugs in the way we do Homebrew updates since."
puts "Please consider unsetting these and tweaking the values based on the new behaviour."
puts "\n\n"
# Consider the message possibly missed if not a TTY.
Settings.write "installfromapimessage", true if $stdout.tty?
end
def untap_message
return if Homebrew::EnvConfig.no_install_from_api?
return if Homebrew::EnvConfig.developer? || ENV["HOMEBREW_DEV_CMD_RUN"]
return if ENV["HOMEBREW_GITHUB_HOSTED_RUNNER"] || ENV["GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED"]
return if (HOMEBREW_PREFIX/".homebrewdocker").exist?
core_tap = CoreTap.instance
2023-07-13 19:28:39 +01:00
cask_tap = CoreCaskTap.instance
return if !core_tap.installed? && !cask_tap.installed?
puts "Installing from the API is now the default behaviour!"
puts "You can save space and time by running:"
puts " brew untap #{core_tap.name}" if core_tap.installed?
puts " brew untap #{cask_tap.name}" if cask_tap.installed?
end
end
require "extend/os/cmd/update-report"
class Reporter
class ReporterRevisionUnsetError < RuntimeError
def initialize(var_name)
super "#{var_name} is unset!"
end
end
def initialize(tap, api_names_txt: nil, api_names_before_txt: nil, api_dir_prefix: nil)
@tap = tap
# This is slightly involved/weird but all the #report logic is shared so it's worth it.
if installed_from_api?(api_names_txt, api_names_before_txt, api_dir_prefix)
@api_names_txt = api_names_txt
@api_names_before_txt = api_names_before_txt
@api_dir_prefix = api_dir_prefix
else
initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{tap.repo_var}"
@initial_revision = ENV[initial_revision_var].to_s
raise ReporterRevisionUnsetError, initial_revision_var if @initial_revision.empty?
current_revision_var = "HOMEBREW_UPDATE_AFTER#{tap.repo_var}"
@current_revision = ENV[current_revision_var].to_s
raise ReporterRevisionUnsetError, current_revision_var if @current_revision.empty?
end
end
def report(auto_update: false)
return @report if @report
@report = Hash.new { |h, k| h[k] = [] }
return @report unless updated?
diff.each_line do |line|
status, *paths = line.split
src = Pathname.new paths.first
dst = Pathname.new paths.last
2023-04-18 15:06:50 -07:00
next if dst.extname != ".rb"
if paths.any? { |p| tap.cask_file?(p) }
2020-07-13 22:48:53 +10:00
case status
when "A"
# Have a dedicated report array for new casks.
@report[:AC] << tap.formula_file_to_name(src)
2020-07-13 22:48:53 +10:00
when "D"
# Have a dedicated report array for deleted casks.
@report[:DC] << tap.formula_file_to_name(src)
2020-07-13 22:48:53 +10:00
when "M"
# Report updated casks
@report[:MC] << tap.formula_file_to_name(src)
2023-04-08 14:10:58 +02:00
when /^R\d{0,3}/
src_full_name = tap.formula_file_to_name(src)
dst_full_name = tap.formula_file_to_name(dst)
# Don't report formulae that are moved within a tap but not renamed
next if src_full_name == dst_full_name
@report[:DC] << src_full_name
@report[:AC] << dst_full_name
end
end
next unless paths.any? { |p| tap.formula_file?(p) }
case status
when "A", "D"
full_name = tap.formula_file_to_name(src)
name = full_name.split("/").last
new_tap = tap.tap_migrations[name]
@report[status.to_sym] << full_name unless new_tap
when "M"
name = tap.formula_file_to_name(src)
@report[:M] << name
when /^R\d{0,3}/
src_full_name = tap.formula_file_to_name(src)
dst_full_name = tap.formula_file_to_name(dst)
# Don't report formulae that are moved within a tap but not renamed
next if src_full_name == dst_full_name
2018-09-17 02:45:00 +02:00
@report[:D] << src_full_name
@report[:A] << dst_full_name
end
end
2023-04-08 14:10:58 +02:00
renamed_casks = Set.new
@report[:DC].each do |old_full_name|
old_name = old_full_name.split("/").last
new_name = tap.cask_renames[old_name]
next unless new_name
2023-07-13 19:28:39 +01:00
new_full_name = if tap.core_cask_tap?
2023-04-08 14:10:58 +02:00
new_name
else
"#{tap}/#{new_name}"
end
renamed_casks << [old_full_name, new_full_name] if @report[:AC].include?(new_full_name)
end
@report[:AC].each do |new_full_name|
new_name = new_full_name.split("/").last
old_name = tap.cask_renames.key(new_name)
next unless old_name
2023-07-13 19:28:39 +01:00
old_full_name = if tap.core_cask_tap?
2023-04-08 14:10:58 +02:00
old_name
else
"#{tap}/#{old_name}"
end
renamed_casks << [old_full_name, new_full_name]
end
if renamed_casks.any?
@report[:AC] -= renamed_casks.map(&:last)
@report[:DC] -= renamed_casks.map(&:first)
@report[:RC] = renamed_casks.to_a
end
renamed_formulae = Set.new
@report[:D].each do |old_full_name|
old_name = old_full_name.split("/").last
new_name = tap.formula_renames[old_name]
next unless new_name
2020-03-13 21:15:06 +00:00
new_full_name = if tap.core_tap?
new_name
else
2020-03-13 21:15:06 +00:00
"#{tap}/#{new_name}"
end
renamed_formulae << [old_full_name, new_full_name] if @report[:A].include? new_full_name
end
@report[:A].each do |new_full_name|
new_name = new_full_name.split("/").last
old_name = tap.formula_renames.key(new_name)
next unless old_name
2020-03-13 21:15:06 +00:00
old_full_name = if tap.core_tap?
old_name
else
2020-03-13 21:15:06 +00:00
"#{tap}/#{old_name}"
end
renamed_formulae << [old_full_name, new_full_name]
end
2023-04-08 14:10:58 +02:00
if renamed_formulae.any?
@report[:A] -= renamed_formulae.map(&:last)
@report[:D] -= renamed_formulae.map(&:first)
@report[:R] = renamed_formulae.to_a
end
@report
end
def updated?
if installed_from_api?
diff.present?
else
initial_revision != current_revision
end
end
def migrate_tap_migration
(report[:D] + report[:DC]).each do |full_name|
name = full_name.split("/").last
new_tap_name = tap.tap_migrations[name]
next if new_tap_name.nil? # skip if not in tap_migrations list.
new_tap_user, new_tap_repo, new_tap_new_name = new_tap_name.split("/")
new_name = if new_tap_new_name
new_full_name = new_tap_new_name
new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
new_tap_new_name
else
new_full_name = "#{new_tap_name}/#{name}"
name
end
# This means it is a cask
if report[:DC].include? full_name
next unless (HOMEBREW_PREFIX/"Caskroom"/new_name).exist?
2018-09-17 02:45:00 +02:00
new_tap = Tap.fetch(new_tap_name)
new_tap.ensure_installed!
ohai "#{name} has been moved to Homebrew.", <<~EOS
2021-01-26 15:21:24 -05:00
To uninstall the cask, run:
2020-11-18 08:10:21 +01:00
brew uninstall --cask --force #{name}
EOS
next if (HOMEBREW_CELLAR/new_name.split("/").last).directory?
2018-09-17 02:45:00 +02:00
ohai "Installing #{new_name}..."
system HOMEBREW_BREW_FILE, "install", new_full_name
begin
unless Formulary.factory(new_full_name).keg_only?
system HOMEBREW_BREW_FILE, "link", new_full_name, "--overwrite"
end
rescue Exception => e # rubocop:disable Lint/RescueException
2023-03-06 08:20:40 -08:00
onoe "#{e.message}\n#{e.backtrace&.join("\n")}" if Homebrew::EnvConfig.developer?
end
next
end
next unless (dir = HOMEBREW_CELLAR/name).exist? # skip if formula is not installed.
2018-09-17 02:45:00 +02:00
tabs = dir.subdirs.map { |d| Tab.for_keg(Keg.new(d)) }
2023-04-18 15:06:50 -07:00
next if tabs.first.tap != tap # skip if installed formula is not from this tap.
2018-09-17 02:45:00 +02:00
new_tap = Tap.fetch(new_tap_name)
# For formulae migrated to cask: Auto-install cask or provide install instructions.
2018-05-25 18:03:16 +02:00
if new_tap_name.start_with?("homebrew/cask")
if new_tap.installed? && (HOMEBREW_PREFIX/"Caskroom").directory?
ohai "#{name} has been moved to Homebrew Cask."
ohai "brew unlink #{name}"
system HOMEBREW_BREW_FILE, "unlink", name
ohai "brew cleanup"
system HOMEBREW_BREW_FILE, "cleanup"
ohai "brew install --cask #{new_name}"
2020-11-18 08:10:21 +01:00
system HOMEBREW_BREW_FILE, "install", "--cask", new_name
2017-10-15 02:28:32 +02:00
ohai <<~EOS
2018-09-03 20:12:29 +01:00
#{name} has been moved to Homebrew Cask.
The existing keg has been unlinked.
Please uninstall the formula when convenient by running:
brew uninstall --force #{name}
EOS
else
ohai "#{name} has been moved to Homebrew Cask.", <<~EOS
2021-01-26 15:21:24 -05:00
To uninstall the formula and install the cask, run:
brew uninstall --force #{name}
2018-05-25 18:03:16 +02:00
brew tap #{new_tap_name}
2020-11-18 08:10:21 +01:00
brew install --cask #{new_name}
EOS
end
else
new_tap.ensure_installed!
# update tap for each Tab
tabs.each { |tab| tab.tap = new_tap }
tabs.each(&:write)
end
end
end
2023-04-08 14:10:58 +02:00
def migrate_cask_rename
Cask::Caskroom.casks.each do |cask|
Cask::Migrator.migrate_if_needed(cask)
end
end
def migrate_formula_rename(force:, verbose:)
Formula.installed.each do |formula|
next unless Migrator.needs_migration?(formula)
2023-04-27 04:09:28 +01:00
oldnames_to_migrate = formula.oldnames.select do |oldname|
oldname_rack = HOMEBREW_CELLAR/oldname
next false unless oldname_rack.exist?
2023-04-27 04:09:28 +01:00
if oldname_rack.subdirs.empty?
oldname_rack.rmdir_if_possible
next false
end
2023-04-27 04:09:28 +01:00
true
end
2023-04-27 04:09:28 +01:00
next if oldnames_to_migrate.empty?
2023-04-27 04:09:28 +01:00
Migrator.migrate_if_needed(formula, force: force)
end
end
private
attr_reader :tap, :initial_revision, :current_revision, :api_names_txt, :api_names_before_txt, :api_dir_prefix
def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt,
api_dir_prefix = @api_dir_prefix)
!api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil?
end
def diff
@diff ||= if installed_from_api?
# Hack `git diff` output with regexes to look like `git diff-tree` output.
# Yes, I know this is a bit filthy but it saves duplicating the #report logic.
diff_output = Utils.popen_read("git", "diff", "--no-ext-diff", api_names_before_txt, api_names_txt)
header_regex = /^(---|\+\+\+) /.freeze
add_delete_characters = ["+", "-"].freeze
diff_output.lines.map do |line|
next if line.match?(header_regex)
next unless add_delete_characters.include?(line[0])
line.sub(/^\+/, "A #{api_dir_prefix.basename}/")
.sub(/^-/, "D #{api_dir_prefix.basename}/")
.sub(/$/, ".rb")
.chomp
end.compact.join("\n")
else
Utils.popen_read(
"git", "-C", tap.path, "diff-tree", "-r", "--name-status", "--diff-filter=AMDR",
"-M85%", initial_revision, current_revision
)
end
end
end
class ReporterHub
attr_reader :reporters
2020-10-20 12:03:48 +02:00
sig { void }
def initialize
@hash = {}
@reporters = []
end
def select_formula_or_cask(key)
@hash.fetch(key, [])
end
def add(reporter, auto_update: false)
@reporters << reporter
report = reporter.report(auto_update: auto_update).delete_if { |_k, v| v.empty? }
@hash.update(report) { |_key, oldval, newval| oldval.concat(newval) }
end
def empty?
@hash.empty?
end
def dump(auto_update: false)
report_all = ENV["HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"].present?
if report_all && !Homebrew::EnvConfig.no_install_from_api?
odisabled "HOMEBREW_UPDATE_REPORT_ALL_FORMULAE"
opoo "This will not report all formulae because Homebrew cannot get this data from the API."
report_all = false
end
unless Homebrew::EnvConfig.no_update_report_new?
dump_new_formula_report
dump_new_cask_report
end
if report_all
dump_renamed_formula_report
dump_renamed_cask_report
end
dump_deleted_formula_report(report_all)
dump_deleted_cask_report(report_all)
2023-03-06 10:57:53 -08:00
outdated_formulae = []
outdated_casks = []
if report_all
if auto_update
if (changed_formulae = select_formula_or_cask(:M).count) && changed_formulae.positive?
ohai "Modified Formulae",
"Modified #{Utils.pluralize("formula", changed_formulae, plural: "e", include_count: true)}."
end
if (changed_casks = select_formula_or_cask(:MC).count) && changed_casks.positive?
ohai "Modified Casks", "Modified #{Utils.pluralize("cask", changed_casks, include_count: true)}."
end
else
dump_modified_formula_report
dump_modified_cask_report
end
else
outdated_formulae = Formula.installed.select(&:outdated?).map(&:name)
outdated_casks = Cask::Caskroom.casks.select(&:outdated?).map(&:token)
unless auto_update
output_dump_formula_or_cask_report "Outdated Formulae", outdated_formulae
output_dump_formula_or_cask_report "Outdated Casks", outdated_casks
end
end
return if outdated_formulae.blank? && outdated_casks.blank?
outdated_formulae = outdated_formulae.count
2023-03-06 10:57:53 -08:00
outdated_casks = outdated_casks.count
update_pronoun = if (outdated_formulae + outdated_casks) == 1
"it"
else
"them"
end
msg = ""
if outdated_formulae.positive?
2023-02-27 20:49:02 -08:00
noun = Utils.pluralize("formula", outdated_formulae, plural: "e")
msg += "#{Tty.bold}#{outdated_formulae}#{Tty.reset} outdated #{noun}"
end
if outdated_casks.positive?
msg += " and " if msg.present?
2023-02-27 20:49:02 -08:00
msg += "#{Tty.bold}#{outdated_casks}#{Tty.reset} outdated #{Utils.pluralize("cask", outdated_casks)}"
end
return if msg.blank?
puts
puts "You have #{msg} installed."
# If we're auto-updating, don't need to suggest commands that we're perhaps
# already running.
return if auto_update
puts <<~EOS
You can upgrade #{update_pronoun} with #{Tty.bold}brew upgrade#{Tty.reset}
or list #{update_pronoun} with #{Tty.bold}brew outdated#{Tty.reset}.
EOS
end
private
def dump_new_formula_report
formulae = select_formula_or_cask(:A).sort.reject { |name| installed?(name) }
output_dump_formula_or_cask_report "New Formulae", formulae
end
def dump_new_cask_report
casks = select_formula_or_cask(:AC).sort.map do |name|
name.split("/").last unless cask_installed?(name)
end.compact
output_dump_formula_or_cask_report "New Casks", casks
end
def dump_renamed_formula_report
formulae = select_formula_or_cask(:R).sort.map do |name, new_name|
name = pretty_installed(name) if installed?(name)
new_name = pretty_installed(new_name) if installed?(new_name)
"#{name} -> #{new_name}"
end
output_dump_formula_or_cask_report "Renamed Formulae", formulae
end
2023-04-08 14:10:58 +02:00
def dump_renamed_cask_report
casks = select_formula_or_cask(:RC).sort.map do |name, new_name|
name = pretty_installed(name) if installed?(name)
new_name = pretty_installed(new_name) if installed?(new_name)
"#{name} -> #{new_name}"
end
output_dump_formula_or_cask_report "Renamed Casks", casks
end
def dump_deleted_formula_report(report_all)
formulae = select_formula_or_cask(:D).sort.map do |name|
if installed?(name)
pretty_uninstalled(name)
elsif report_all
name
end
end.compact
title = if report_all
"Deleted Formulae"
else
"Deleted Installed Formulae"
end
output_dump_formula_or_cask_report title, formulae
end
def dump_deleted_cask_report(report_all)
casks = select_formula_or_cask(:DC).sort.map do |name|
name = name.split("/").last
if cask_installed?(name)
pretty_uninstalled(name)
elsif report_all
name
end
end.compact
title = if report_all
"Deleted Casks"
else
"Deleted Installed Casks"
end
output_dump_formula_or_cask_report title, casks
end
def dump_modified_formula_report
formulae = select_formula_or_cask(:M).sort.map do |name|
if installed?(name)
if outdated?(name)
pretty_outdated(name)
else
pretty_installed(name)
end
else
name
end
end
output_dump_formula_or_cask_report "Modified Formulae", formulae
end
def dump_modified_cask_report
casks = select_formula_or_cask(:MC).sort.map do |name|
name = name.split("/").last
if cask_installed?(name)
if cask_outdated?(name)
pretty_outdated(name)
else
pretty_installed(name)
end
else
name
end
end
output_dump_formula_or_cask_report "Modified Casks", casks
end
def output_dump_formula_or_cask_report(title, formulae_or_casks)
return if formulae_or_casks.blank?
2018-09-17 02:45:00 +02:00
ohai title, Formatter.columns(formulae_or_casks.sort)
end
def installed?(formula)
(HOMEBREW_CELLAR/formula.split("/").last).directory?
end
def outdated?(formula)
Formula[formula].outdated?
rescue FormulaUnavailableError
false
end
def cask_installed?(cask)
(Cask::Caskroom.path/cask).directory?
end
def cask_outdated?(cask)
Cask::CaskLoader.load(cask).outdated?
rescue Cask::CaskError
false
end
end