From f8ae5f3bc438cc1bf88b33e930d4cda55b150f1e Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Fri, 8 Jul 2022 10:21:57 -0700 Subject: [PATCH 01/58] cmd/uninstall: Add env variable that runs autoremove after uninstalls When HOMEBREW_UNINSTALL_AUTOREMOVE is set, `brew autoremove` is run after every successful call to `brew uninstall`. --- Library/Homebrew/cmd/uninstall.rb | 9 +++++++++ Library/Homebrew/env_config.rb | 4 ++++ .../Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi | 2 ++ docs/Manpage.md | 3 +++ manpages/brew.1 | 6 ++++++ 5 files changed, 24 insertions(+) diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 8c40d4219d..e8e1bb386c 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -50,6 +50,11 @@ module Homebrew all_kegs: args.force?, ) + # If ignore_unavailable is true and the named args + # are a series of invalid kegs and casks, + # #to_kegs_to_casks will return empty arrays. + return if all_kegs.blank? && casks.blank? + kegs_by_rack = all_kegs.group_by(&:rack) Uninstall.uninstall_kegs( @@ -73,5 +78,9 @@ module Homebrew force: args.force?, ) end + + return unless Homebrew::EnvConfig.uninstall_autoremove? + + system HOMEBREW_BREW_FILE, "autoremove" end end diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 6397f0abb4..115b292a97 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -343,6 +343,10 @@ module Homebrew default_text: "macOS: `/private/tmp`, Linux: `/tmp`.", default: HOMEBREW_DEFAULT_TEMP, }, + HOMEBREW_UNINSTALL_AUTOREMOVE: { + description: "If set, `brew autoremove` is run after every successful call to `brew uninstall`.", + boolean: true, + }, HOMEBREW_UPDATE_REPORT_ALL_FORMULAE: { description: "If set, `brew update` lists changes to all formulae and cask files rather than only showing " \ "when they are new and not installed or outdated and installed.", diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index ba6af6ace7..271c840e99 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -2572,6 +2572,8 @@ module Homebrew::EnvConfig def self.temp(); end + def self.uninstall_autoremove?(); end + def self.update_report_all_formulae?(); end def self.update_to_tag?(); end diff --git a/docs/Manpage.md b/docs/Manpage.md index f74003edb1..bd9abaa82a 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -2184,6 +2184,9 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just *Default:* macOS: `/private/tmp`, Linux: `/tmp`. +- `HOMEBREW_UNINSTALL_AUTOREMOVE` +
If set, `brew autoremove` is run after every successful call to `brew uninstall`. + - `HOMEBREW_UPDATE_REPORT_ALL_FORMULAE`
If set, `brew update` lists changes to all formulae and cask files rather than only showing when they are new and not installed or outdated and installed. diff --git a/manpages/brew.1 b/manpages/brew.1 index 87c2999563..d340397ea2 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -3207,6 +3207,12 @@ Use this path as the temporary directory for building packages\. Changing this m \fIDefault:\fR macOS: \fB/private/tmp\fR, Linux: \fB/tmp\fR\. . .TP +\fBHOMEBREW_UNINSTALL_AUTOREMOVE\fR +. +.br +If set, \fBbrew autoremove\fR is run after every successful call to \fBbrew uninstall\fR\. +. +.TP \fBHOMEBREW_UPDATE_REPORT_ALL_FORMULAE\fR . .br From 74f1eca14c9b2e353eb0c32e35a005dfbb333a9b Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Tue, 12 Jul 2022 00:28:25 -0700 Subject: [PATCH 02/58] autoremove.rb: moved logic out into formula.rb, and uninstall.rb This allows us to autoremove formulae in the autoremove and uninstall commands without having to shell out to brew. --- Library/Homebrew/cmd/autoremove.rb | 30 +-------------------------- Library/Homebrew/cmd/uninstall.rb | 2 +- Library/Homebrew/formula.rb | 33 +++++++++++++++++++++++++++++- Library/Homebrew/uninstall.rb | 14 +++++++++++++ 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/Library/Homebrew/cmd/autoremove.rb b/Library/Homebrew/cmd/autoremove.rb index a80cb4ae49..c1a9b65cec 100644 --- a/Library/Homebrew/cmd/autoremove.rb +++ b/Library/Homebrew/cmd/autoremove.rb @@ -20,37 +20,9 @@ module Homebrew end end - def get_removable_formulae(formulae) - removable_formulae = Formula.installed_formulae_with_no_dependents(formulae).reject do |f| - Tab.for_keg(f.any_installed_keg).installed_on_request - end - - removable_formulae += get_removable_formulae(formulae - removable_formulae) if removable_formulae.present? - - removable_formulae - end - def autoremove args = autoremove_args.parse - removable_formulae = get_removable_formulae(Formula.installed) - - if (casks = Cask::Caskroom.casks.presence) - removable_formulae -= casks.flat_map { |cask| cask.depends_on[:formula] } - .compact - .map { |f| Formula[f] } - .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } - end - return if removable_formulae.blank? - - formulae_names = removable_formulae.map(&:full_name).sort - - verb = args.dry_run? ? "Would uninstall" : "Uninstalling" - oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" - puts formulae_names.join("\n") - return if args.dry_run? - - kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) - Uninstall.uninstall_kegs(kegs_by_rack) + Uninstall.autoremove_kegs(Formula.removable_formulae, dry_run: args.dry_run?) end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index e8e1bb386c..fffa54a69f 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -81,6 +81,6 @@ module Homebrew return unless Homebrew::EnvConfig.uninstall_autoremove? - system HOMEBREW_BREW_FILE, "autoremove" + Uninstall.autoremove_kegs(Formula.removable_formulae) end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 17488e0ed1..d367e157dd 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1706,7 +1706,7 @@ class Formula end.uniq(&:name) end - # An array of all installed {Formula} without dependents + # An array of all installed {Formula} without {Formula} dependents # @private def self.installed_formulae_with_no_dependents(formulae = installed) return [] if formulae.blank? @@ -1720,6 +1720,37 @@ class Formula installed.select { |f| f.installed_alias_path == alias_path } end + # An array of all installed {Formula} with {Cask} dependents. + # @private + def self.installed_formulae_with_cask_dependents + Cask::Caskroom.casks + .flat_map { |cask| cask.depends_on[:formula] } + .compact + .map { |f| Formula[f] } + .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } + end + + # An array of all installed {Formula} without {Formula} or {Cask} dependents + # that weren't installed on request. + # @private + def self.removable_formulae + formulae = installed + all_removable_formulae = T.let([], T::Array[Formula]) + + loop do + removable_formulae = installed_formulae_with_no_dependents(formulae).reject do |f| + Tab.for_keg(f.any_installed_keg).installed_on_request + end + + break if removable_formulae.blank? + + all_removable_formulae += removable_formulae + formulae -= removable_formulae + end + + all_removable_formulae - installed_formulae_with_cask_dependents + end + # an array of all alias files of core {Formula} # @private def self.core_alias_files diff --git a/Library/Homebrew/uninstall.rb b/Library/Homebrew/uninstall.rb index 1d9a1585e4..ee00fb849c 100644 --- a/Library/Homebrew/uninstall.rb +++ b/Library/Homebrew/uninstall.rb @@ -97,6 +97,20 @@ module Homebrew end end + def autoremove_kegs(removable_formulae, dry_run: false) + return if removable_formulae.blank? + + formulae_names = removable_formulae.map(&:full_name).sort + + verb = dry_run ? "Would uninstall" : "Uninstalling" + oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" + puts formulae_names.join("\n") + return if dry_run + + kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) + Uninstall.uninstall_kegs(kegs_by_rack) + end + def handle_unsatisfied_dependents(kegs_by_rack, casks: [], ignore_dependencies: false, named_args: []) return if ignore_dependencies From 74f7b604cd00f1bd45410e6bbb6de36916ae9077 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Wed, 13 Jul 2022 11:28:54 -0700 Subject: [PATCH 03/58] Changed env variable to HOMEBREW_AUTOREMOVE --- Library/Homebrew/env_config.rb | 9 +++++---- .../sorbet/rbi/hidden-definitions/hidden.rbi | 4 ++-- docs/Manpage.md | 6 +++--- manpages/brew.1 | 12 ++++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 115b292a97..b2b27fda69 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -36,6 +36,11 @@ module Homebrew "disable auto-update entirely with HOMEBREW_NO_AUTO_UPDATE.", default: 300, }, + HOMEBREW_AUTOREMOVE: { + description: "If set, calls to `brew install`, `brew upgrade`, `brew reinstall` and `brew uninstall` " \ + "will automatically remove unused formula dependents.", + boolean: true, + }, HOMEBREW_BAT: { description: "If set, use `bat` for the `brew cat` command.", boolean: true, @@ -343,10 +348,6 @@ module Homebrew default_text: "macOS: `/private/tmp`, Linux: `/tmp`.", default: HOMEBREW_DEFAULT_TEMP, }, - HOMEBREW_UNINSTALL_AUTOREMOVE: { - description: "If set, `brew autoremove` is run after every successful call to `brew uninstall`.", - boolean: true, - }, HOMEBREW_UPDATE_REPORT_ALL_FORMULAE: { description: "If set, `brew update` lists changes to all formulae and cask files rather than only showing " \ "when they are new and not installed or outdated and installed.", diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index 271c840e99..f22b19bd3a 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -2442,6 +2442,8 @@ module Homebrew::EnvConfig def self.artifact_domain(); end + def self.autoremove?(); end + def self.auto_update_secs(); end def self.bat?(); end @@ -2572,8 +2574,6 @@ module Homebrew::EnvConfig def self.temp(); end - def self.uninstall_autoremove?(); end - def self.update_report_all_formulae?(); end def self.update_to_tag?(); end diff --git a/docs/Manpage.md b/docs/Manpage.md index bd9abaa82a..b7a7a28215 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1945,6 +1945,9 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just *Default:* `300`. +- `HOMEBREW_AUTOREMOVE` +
If set, calls to `brew install`, `brew upgrade`, `brew reinstall` and `brew uninstall` will automatically remove unused formula dependents. + - `HOMEBREW_BAT`
If set, use `bat` for the `brew cat` command. @@ -2184,9 +2187,6 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just *Default:* macOS: `/private/tmp`, Linux: `/tmp`. -- `HOMEBREW_UNINSTALL_AUTOREMOVE` -
If set, `brew autoremove` is run after every successful call to `brew uninstall`. - - `HOMEBREW_UPDATE_REPORT_ALL_FORMULAE`
If set, `brew update` lists changes to all formulae and cask files rather than only showing when they are new and not installed or outdated and installed. diff --git a/manpages/brew.1 b/manpages/brew.1 index d340397ea2..680e979622 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -2757,6 +2757,12 @@ Run \fBbrew update\fR once every \fBHOMEBREW_AUTO_UPDATE_SECS\fR seconds before \fIDefault:\fR \fB300\fR\. . .TP +\fBHOMEBREW_AUTOREMOVE\fR +. +.br +If set, calls to \fBbrew install\fR, \fBbrew upgrade\fR, \fBbrew reinstall\fR and \fBbrew uninstall\fR will automatically remove unused formula dependents\. +. +.TP \fBHOMEBREW_BAT\fR . .br @@ -3207,12 +3213,6 @@ Use this path as the temporary directory for building packages\. Changing this m \fIDefault:\fR macOS: \fB/private/tmp\fR, Linux: \fB/tmp\fR\. . .TP -\fBHOMEBREW_UNINSTALL_AUTOREMOVE\fR -. -.br -If set, \fBbrew autoremove\fR is run after every successful call to \fBbrew uninstall\fR\. -. -.TP \fBHOMEBREW_UPDATE_REPORT_ALL_FORMULAE\fR . .br From fa43418bfbd7b0c1ceea3b03663b6d93282f4b84 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Wed, 13 Jul 2022 11:46:43 -0700 Subject: [PATCH 04/58] Moved autoremove logic into autoremove.rb This allows us to call that logic internally in other brew commands instead of having to shell out. --- Library/Homebrew/autoremove.rb | 64 ++++++++++++++++++++++++++++++ Library/Homebrew/cmd/autoremove.rb | 5 +-- Library/Homebrew/cmd/install.rb | 3 ++ Library/Homebrew/cmd/reinstall.rb | 3 ++ Library/Homebrew/cmd/uninstall.rb | 4 +- Library/Homebrew/cmd/upgrade.rb | 3 ++ Library/Homebrew/env_config.rb | 2 +- Library/Homebrew/formula.rb | 31 --------------- Library/Homebrew/uninstall.rb | 14 ------- 9 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 Library/Homebrew/autoremove.rb diff --git a/Library/Homebrew/autoremove.rb b/Library/Homebrew/autoremove.rb new file mode 100644 index 0000000000..cebf54a1ab --- /dev/null +++ b/Library/Homebrew/autoremove.rb @@ -0,0 +1,64 @@ +# typed: false +# frozen_string_literal: true + +require "cask/caskroom" +require "formula" +require "uninstall" + +module Homebrew + # Helpers for removing unused formulae. + # + # @api private + module Autoremove + module_function + + def remove_unused_formulae(dry_run: false) + removable_formulae = unused_formulae_with_no_dependents + + return if removable_formulae.blank? + + formulae_names = removable_formulae.map(&:full_name).sort + + verb = dry_run ? "Would autoremove" : "Autoremoving" + oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" + puts formulae_names.join("\n") + return if dry_run + + kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) + Uninstall.uninstall_kegs(kegs_by_rack) + end + + # An array of installed {Formula} without {Formula} or {Cask} + # dependents that weren't installed on request. + # @private + def unused_formulae_with_no_dependents + unused_formulae = unused_formulae_with_no_formula_dependents(Formula.installed) + unused_formulae - installed_formulae_with_cask_dependents + end + + # Recursive function that returns an array of installed {Formula} without + # {Formula} dependents that weren't installed on request. + # @private + def unused_formulae_with_no_formula_dependents(formulae) + unused_formulae = Formula.installed_formulae_with_no_dependents(formulae).reject do |f| + Tab.for_keg(f.any_installed_keg).installed_on_request + end + + if unused_formulae.present? + unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae) + end + + unused_formulae + end + + # An array of all installed {Formula} with {Cask} dependents. + # @private + def installed_formulae_with_cask_dependents + Cask::Caskroom.casks + .flat_map { |cask| cask.depends_on[:formula] } + .compact + .map { |f| Formula[f] } + .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } + end + end +end diff --git a/Library/Homebrew/cmd/autoremove.rb b/Library/Homebrew/cmd/autoremove.rb index c1a9b65cec..9cd02cb40a 100644 --- a/Library/Homebrew/cmd/autoremove.rb +++ b/Library/Homebrew/cmd/autoremove.rb @@ -1,9 +1,8 @@ # typed: true # frozen_string_literal: true -require "formula" +require "autoremove" require "cli/parser" -require "uninstall" module Homebrew module_function @@ -23,6 +22,6 @@ module Homebrew def autoremove args = autoremove_args.parse - Uninstall.autoremove_kegs(Formula.removable_formulae, dry_run: args.dry_run?) + Autoremove.remove_unused_formulae(dry_run: args.dry_run?) end end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index cff794979e..8c555b92ee 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -1,6 +1,7 @@ # typed: false # frozen_string_literal: true +require "autoremove" require "cask/config" require "cask/cmd" require "cask/cmd/install" @@ -254,6 +255,8 @@ module Homebrew ) Homebrew.messages.display_messages(display_times: args.display_times?) + + Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? rescue FormulaUnreadableError, FormulaClassUnavailableError, TapFormulaUnreadableError, TapFormulaClassUnavailableError => e # Need to rescue before `FormulaUnavailableError` (superclass of this) diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index bc6bca5053..e24f30ce74 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -1,6 +1,7 @@ # typed: false # frozen_string_literal: true +require "autoremove" require "formula_installer" require "development_tools" require "messages" @@ -152,5 +153,7 @@ module Homebrew end Homebrew.messages.display_messages(display_times: args.display_times?) + + Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index fffa54a69f..0ba3018709 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -79,8 +79,6 @@ module Homebrew ) end - return unless Homebrew::EnvConfig.uninstall_autoremove? - - Uninstall.autoremove_kegs(Formula.removable_formulae) + Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? end end diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index ec7520a7c3..62b5e7cbc1 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -1,6 +1,7 @@ # typed: false # frozen_string_literal: true +require "autoremove" require "cli/parser" require "formula_installer" require "install" @@ -111,6 +112,8 @@ module Homebrew upgrade_outdated_casks(casks, args: args) unless only_upgrade_formulae Homebrew.messages.display_messages(display_times: args.display_times?) + + Autoremove.remove_unused_formulae(dry_run: args.dry_run?) if Homebrew::EnvConfig.autoremove? end sig { params(formulae: T::Array[Formula], args: CLI::Args).returns(T::Boolean) } diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index b2b27fda69..f56c3a0ddb 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -36,7 +36,7 @@ module Homebrew "disable auto-update entirely with HOMEBREW_NO_AUTO_UPDATE.", default: 300, }, - HOMEBREW_AUTOREMOVE: { + HOMEBREW_AUTOREMOVE: { description: "If set, calls to `brew install`, `brew upgrade`, `brew reinstall` and `brew uninstall` " \ "will automatically remove unused formula dependents.", boolean: true, diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index d367e157dd..fc496d4e8d 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1720,37 +1720,6 @@ class Formula installed.select { |f| f.installed_alias_path == alias_path } end - # An array of all installed {Formula} with {Cask} dependents. - # @private - def self.installed_formulae_with_cask_dependents - Cask::Caskroom.casks - .flat_map { |cask| cask.depends_on[:formula] } - .compact - .map { |f| Formula[f] } - .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } - end - - # An array of all installed {Formula} without {Formula} or {Cask} dependents - # that weren't installed on request. - # @private - def self.removable_formulae - formulae = installed - all_removable_formulae = T.let([], T::Array[Formula]) - - loop do - removable_formulae = installed_formulae_with_no_dependents(formulae).reject do |f| - Tab.for_keg(f.any_installed_keg).installed_on_request - end - - break if removable_formulae.blank? - - all_removable_formulae += removable_formulae - formulae -= removable_formulae - end - - all_removable_formulae - installed_formulae_with_cask_dependents - end - # an array of all alias files of core {Formula} # @private def self.core_alias_files diff --git a/Library/Homebrew/uninstall.rb b/Library/Homebrew/uninstall.rb index ee00fb849c..1d9a1585e4 100644 --- a/Library/Homebrew/uninstall.rb +++ b/Library/Homebrew/uninstall.rb @@ -97,20 +97,6 @@ module Homebrew end end - def autoremove_kegs(removable_formulae, dry_run: false) - return if removable_formulae.blank? - - formulae_names = removable_formulae.map(&:full_name).sort - - verb = dry_run ? "Would uninstall" : "Uninstalling" - oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" - puts formulae_names.join("\n") - return if dry_run - - kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) - Uninstall.uninstall_kegs(kegs_by_rack) - end - def handle_unsatisfied_dependents(kegs_by_rack, casks: [], ignore_dependencies: false, named_args: []) return if ignore_dependencies From fe83500617293a1e777cb8c7cec404574513fd20 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Thu, 14 Jul 2022 13:16:26 -0700 Subject: [PATCH 05/58] Moved autoremove logic into cleanup.rb and formula.rb Cleanup.rb: - Added #autoremove method - #autoremove is called in clean when HOMEBREW_AUTOREMOVE is set Formula.rb: - Added #unused_formulae_with_no_dependents and helpers Removed old autoremove.rb module. --- Library/Homebrew/autoremove.rb | 64 --------------------------- Library/Homebrew/cleanup.rb | 23 ++++++++++ Library/Homebrew/cmd/autoremove.rb | 4 +- Library/Homebrew/cmd/install.rb | 3 -- Library/Homebrew/cmd/leaves.rb | 2 +- Library/Homebrew/cmd/reinstall.rb | 3 -- Library/Homebrew/cmd/uninstall.rb | 2 +- Library/Homebrew/cmd/upgrade.rb | 3 -- Library/Homebrew/formula.rb | 37 +++++++++++++++- Library/Homebrew/test/formula_spec.rb | 6 +-- 10 files changed, 66 insertions(+), 81 deletions(-) delete mode 100644 Library/Homebrew/autoremove.rb diff --git a/Library/Homebrew/autoremove.rb b/Library/Homebrew/autoremove.rb deleted file mode 100644 index cebf54a1ab..0000000000 --- a/Library/Homebrew/autoremove.rb +++ /dev/null @@ -1,64 +0,0 @@ -# typed: false -# frozen_string_literal: true - -require "cask/caskroom" -require "formula" -require "uninstall" - -module Homebrew - # Helpers for removing unused formulae. - # - # @api private - module Autoremove - module_function - - def remove_unused_formulae(dry_run: false) - removable_formulae = unused_formulae_with_no_dependents - - return if removable_formulae.blank? - - formulae_names = removable_formulae.map(&:full_name).sort - - verb = dry_run ? "Would autoremove" : "Autoremoving" - oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" - puts formulae_names.join("\n") - return if dry_run - - kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) - Uninstall.uninstall_kegs(kegs_by_rack) - end - - # An array of installed {Formula} without {Formula} or {Cask} - # dependents that weren't installed on request. - # @private - def unused_formulae_with_no_dependents - unused_formulae = unused_formulae_with_no_formula_dependents(Formula.installed) - unused_formulae - installed_formulae_with_cask_dependents - end - - # Recursive function that returns an array of installed {Formula} without - # {Formula} dependents that weren't installed on request. - # @private - def unused_formulae_with_no_formula_dependents(formulae) - unused_formulae = Formula.installed_formulae_with_no_dependents(formulae).reject do |f| - Tab.for_keg(f.any_installed_keg).installed_on_request - end - - if unused_formulae.present? - unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae) - end - - unused_formulae - end - - # An array of all installed {Formula} with {Cask} dependents. - # @private - def installed_formulae_with_cask_dependents - Cask::Caskroom.casks - .flat_map { |cask| cask.depends_on[:formula] } - .compact - .map { |f| Formula[f] } - .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } - end - end -end diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 4eb4689c9b..d31a51fca1 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -219,6 +219,9 @@ module Homebrew .each do |formula| cleanup_formula(formula, quiet: quiet, ds_store: false, cache_db: false) end + + Cleanup.autoremove(dry_run: dry_run?) if Homebrew::EnvConfig.autoremove? + cleanup_cache cleanup_logs cleanup_lockfiles @@ -261,6 +264,8 @@ module Homebrew end cleanup_cask(cask) if cask end + + Cleanup.autoremove(dry_run: dry_run?) if Homebrew::EnvConfig.autoremove? end end @@ -519,5 +524,23 @@ module Homebrew print "and #{d} directories " if d.positive? puts "from #{HOMEBREW_PREFIX}" end + + def self.autoremove(dry_run: false) + removable_formulae = Formula.unused_formulae_with_no_dependents + + return if removable_formulae.blank? + + formulae_names = removable_formulae.map(&:full_name).sort + + verb = dry_run ? "Would autoremove" : "Autoremoving" + oh1 "#{verb} #{formulae_names.count} unneeded #{"formula".pluralize(formulae_names.count)}:" + puts formulae_names.join("\n") + return if dry_run + + require "uninstall" + + kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) + Uninstall.uninstall_kegs(kegs_by_rack) + end end end diff --git a/Library/Homebrew/cmd/autoremove.rb b/Library/Homebrew/cmd/autoremove.rb index 9cd02cb40a..94dea99605 100644 --- a/Library/Homebrew/cmd/autoremove.rb +++ b/Library/Homebrew/cmd/autoremove.rb @@ -1,7 +1,7 @@ # typed: true # frozen_string_literal: true -require "autoremove" +require "cleanup" require "cli/parser" module Homebrew @@ -22,6 +22,6 @@ module Homebrew def autoremove args = autoremove_args.parse - Autoremove.remove_unused_formulae(dry_run: args.dry_run?) + Cleanup.autoremove(dry_run: args.dry_run?) end end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 8c555b92ee..cff794979e 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -1,7 +1,6 @@ # typed: false # frozen_string_literal: true -require "autoremove" require "cask/config" require "cask/cmd" require "cask/cmd/install" @@ -255,8 +254,6 @@ module Homebrew ) Homebrew.messages.display_messages(display_times: args.display_times?) - - Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? rescue FormulaUnreadableError, FormulaClassUnavailableError, TapFormulaUnreadableError, TapFormulaClassUnavailableError => e # Need to rescue before `FormulaUnavailableError` (superclass of this) diff --git a/Library/Homebrew/cmd/leaves.rb b/Library/Homebrew/cmd/leaves.rb index 1c003b4b96..f14bed7dfc 100644 --- a/Library/Homebrew/cmd/leaves.rb +++ b/Library/Homebrew/cmd/leaves.rb @@ -37,7 +37,7 @@ module Homebrew def leaves args = leaves_args.parse - leaves_list = Formula.installed_formulae_with_no_dependents + leaves_list = Formula.installed_formulae_with_no_formula_dependents leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request? leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency? diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index e24f30ce74..bc6bca5053 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -1,7 +1,6 @@ # typed: false # frozen_string_literal: true -require "autoremove" require "formula_installer" require "development_tools" require "messages" @@ -153,7 +152,5 @@ module Homebrew end Homebrew.messages.display_messages(display_times: args.display_times?) - - Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 0ba3018709..50a9abd5d1 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -79,6 +79,6 @@ module Homebrew ) end - Autoremove.remove_unused_formulae if Homebrew::EnvConfig.autoremove? + Cleanup.autoremove if Homebrew::EnvConfig.autoremove? end end diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index 62b5e7cbc1..ec7520a7c3 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -1,7 +1,6 @@ # typed: false # frozen_string_literal: true -require "autoremove" require "cli/parser" require "formula_installer" require "install" @@ -112,8 +111,6 @@ module Homebrew upgrade_outdated_casks(casks, args: args) unless only_upgrade_formulae Homebrew.messages.display_messages(display_times: args.display_times?) - - Autoremove.remove_unused_formulae(dry_run: args.dry_run?) if Homebrew::EnvConfig.autoremove? end sig { params(formulae: T::Array[Formula], args: CLI::Args).returns(T::Boolean) } diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index fc496d4e8d..5caa3876fd 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1706,14 +1706,49 @@ class Formula end.uniq(&:name) end + # An array of all installed {Formula} with {Cask} dependents. + # @private + def self.installed_formulae_with_cask_dependents + require "cask/caskroom" + + Cask::Caskroom.casks + .flat_map { |cask| cask.depends_on[:formula] } + .compact + .map { |f| Formula[f] } + .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } + end + # An array of all installed {Formula} without {Formula} dependents # @private - def self.installed_formulae_with_no_dependents(formulae = installed) + def self.installed_formulae_with_no_formula_dependents(formulae = installed) return [] if formulae.blank? formulae - formulae.flat_map(&:runtime_formula_dependencies) end + # Recursive function that returns an array of installed {Formula} without + # {Formula} dependents that weren't installed on request. + # @private + def self.unused_formulae_with_no_formula_dependents(formulae) + unused_formulae = installed_formulae_with_no_formula_dependents(formulae).reject do |f| + Tab.for_keg(f.any_installed_keg).installed_on_request + end + + if unused_formulae.present? + unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae) + end + + unused_formulae + end + + # An array of installed {Formula} without {Formula} or {Cask} + # dependents that weren't installed on request. + # @private + def self.unused_formulae_with_no_dependents + unused_formulae = unused_formulae_with_no_formula_dependents(installed) + unused_formulae - installed_formulae_with_cask_dependents + end + def self.installed_with_alias_path(alias_path) return [] if alias_path.nil? diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index e47a197361..e8f3fa81d6 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -446,7 +446,7 @@ describe Formula do end end - describe "::installed_formulae_with_no_dependents" do + describe "::installed_formulae_with_no_formula_dependents" do let(:formula_is_dep) do formula "foo" do url "foo-1.1" @@ -473,12 +473,12 @@ describe Formula do specify "without formulae parameter" do allow(described_class).to receive(:installed).and_return(formulae) - expect(described_class.installed_formulae_with_no_dependents) + expect(described_class.installed_formulae_with_no_formula_dependents) .to eq([formula_with_deps]) end specify "with formulae parameter" do - expect(described_class.installed_formulae_with_no_dependents(formulae)) + expect(described_class.installed_formulae_with_no_formula_dependents(formulae)) .to eq([formula_with_deps]) end end From cb91f8b66503557221a495fe8d853f19c904eaf6 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Fri, 15 Jul 2022 16:27:22 -0700 Subject: [PATCH 06/58] Add tests for retrieving formula based on dependents Also, made sure to clear formula cache before and after autoremoving packages. --- Library/Homebrew/cleanup.rb | 25 ++++-- Library/Homebrew/cmd/leaves.rb | 2 +- Library/Homebrew/formula.rb | 27 +++--- Library/Homebrew/test/formula_spec.rb | 123 ++++++++++++++++++++++---- 4 files changed, 139 insertions(+), 38 deletions(-) diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index d31a51fca1..e381088a1d 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -160,7 +160,7 @@ module Homebrew cleanup = Cleanup.new(dry_run: dry_run) if cleanup.periodic_clean_due? cleanup.periodic_clean! - elsif f.latest_version_installed? && !cleanup.skip_clean_formula?(f) + elsif f.latest_version_installed? && !Cleanup.skip_clean_formula?(f) ohai "Running `brew cleanup #{f}`..." puts_no_install_cleanup_disable_message_if_not_already! cleanup.cleanup_formula(f) @@ -177,7 +177,7 @@ module Homebrew @puts_no_install_cleanup_disable_message_if_not_already = true end - def skip_clean_formula?(f) + def self.skip_clean_formula?(f) return false if Homebrew::EnvConfig.no_cleanup_formulae.blank? skip_clean_formulae = Homebrew::EnvConfig.no_cleanup_formulae.split(",") @@ -215,7 +215,7 @@ module Homebrew if args.empty? Formula.installed .sort_by(&:name) - .reject { |f| skip_clean_formula?(f) } + .reject { |f| Cleanup.skip_clean_formula?(f) } .each do |formula| cleanup_formula(formula, quiet: quiet, ds_store: false, cache_db: false) end @@ -256,7 +256,7 @@ module Homebrew nil end - if formula && skip_clean_formula?(formula) + if formula && Cleanup.skip_clean_formula?(formula) onoe "Refusing to clean #{formula} because it is listed in " \ "#{Tty.bold}HOMEBREW_NO_CLEANUP_FORMULAE#{Tty.reset}!" elsif formula @@ -264,8 +264,6 @@ module Homebrew end cleanup_cask(cask) if cask end - - Cleanup.autoremove(dry_run: dry_run?) if Homebrew::EnvConfig.autoremove? end end @@ -526,7 +524,17 @@ module Homebrew end def self.autoremove(dry_run: false) - removable_formulae = Formula.unused_formulae_with_no_dependents + require "cask/caskroom" + + # If this runs after install, uninstall, reinstall or upgrade, + # the cache of installed formulae may no longer be valid. + Formula.clear_cache unless dry_run + + # Remove formulae listed in HOMEBREW_NO_CLEANUP_FORMULAE. + formulae = Formula.installed.reject(&method(:skip_clean_formula?)) + casks = Cask::Caskroom.casks + + removable_formulae = Formula.unused_formulae_with_no_dependents(formulae, casks) return if removable_formulae.blank? @@ -541,6 +549,9 @@ module Homebrew kegs_by_rack = removable_formulae.map(&:any_installed_keg).group_by(&:rack) Uninstall.uninstall_kegs(kegs_by_rack) + + # The installed formula cache will be invalid after uninstalling. + Formula.clear_cache end end end diff --git a/Library/Homebrew/cmd/leaves.rb b/Library/Homebrew/cmd/leaves.rb index f14bed7dfc..ec134de4a1 100644 --- a/Library/Homebrew/cmd/leaves.rb +++ b/Library/Homebrew/cmd/leaves.rb @@ -37,7 +37,7 @@ module Homebrew def leaves args = leaves_args.parse - leaves_list = Formula.installed_formulae_with_no_formula_dependents + leaves_list = Formula.formulae_with_no_formula_dependents(Formula.installed) leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request? leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency? diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5caa3876fd..4db86ebed3 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1708,29 +1708,26 @@ class Formula # An array of all installed {Formula} with {Cask} dependents. # @private - def self.installed_formulae_with_cask_dependents - require "cask/caskroom" - - Cask::Caskroom.casks - .flat_map { |cask| cask.depends_on[:formula] } - .compact - .map { |f| Formula[f] } - .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } + def self.formulae_with_cask_dependents(casks) + casks.flat_map { |cask| cask.depends_on[:formula] } + .compact + .map { |f| Formula[f] } + .flat_map { |f| [f, *f.runtime_formula_dependencies].compact } end # An array of all installed {Formula} without {Formula} dependents # @private - def self.installed_formulae_with_no_formula_dependents(formulae = installed) + def self.formulae_with_no_formula_dependents(formulae) return [] if formulae.blank? formulae - formulae.flat_map(&:runtime_formula_dependencies) end - # Recursive function that returns an array of installed {Formula} without + # Recursive function that returns an array of {Formula} without # {Formula} dependents that weren't installed on request. # @private def self.unused_formulae_with_no_formula_dependents(formulae) - unused_formulae = installed_formulae_with_no_formula_dependents(formulae).reject do |f| + unused_formulae = formulae_with_no_formula_dependents(formulae).reject do |f| Tab.for_keg(f.any_installed_keg).installed_on_request end @@ -1741,12 +1738,12 @@ class Formula unused_formulae end - # An array of installed {Formula} without {Formula} or {Cask} + # An array of {Formula} without {Formula} or {Cask} # dependents that weren't installed on request. # @private - def self.unused_formulae_with_no_dependents - unused_formulae = unused_formulae_with_no_formula_dependents(installed) - unused_formulae - installed_formulae_with_cask_dependents + def self.unused_formulae_with_no_dependents(formulae, casks) + unused_formulae = unused_formulae_with_no_formula_dependents(formulae) + unused_formulae - formulae_with_cask_dependents(casks) end def self.installed_with_alias_path(alias_path) diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index e8f3fa81d6..1ed7890557 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -446,40 +446,133 @@ describe Formula do end end - describe "::installed_formulae_with_no_formula_dependents" do - let(:formula_is_dep) do - formula "foo" do - url "foo-1.1" + shared_context "with formulae for dependency testing" do + let(:formula_with_deps) do + formula "zero" do + url "zero-1.0" end end - let(:formula_with_deps) do - formula "bar" do - url "bar-1.0" + let(:formula_is_dep1) do + formula "one" do + url "one-1.1" + end + end + + let(:formula_is_dep2) do + formula "two" do + url "two-1.1" end end let(:formulae) do [ formula_with_deps, - formula_is_dep, + formula_is_dep1, + formula_is_dep2, ] end before do - allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([formula_is_dep]) + allow(formula_with_deps).to receive(:runtime_formula_dependencies).and_return([formula_is_dep1, + formula_is_dep2]) + allow(formula_is_dep1).to receive(:runtime_formula_dependencies).and_return([formula_is_dep2]) end + end - specify "without formulae parameter" do - allow(described_class).to receive(:installed).and_return(formulae) + describe "::formulae_with_no_formula_dependents" do + include_context "with formulae for dependency testing" - expect(described_class.installed_formulae_with_no_formula_dependents) + it "filters out dependencies" do + expect(described_class.formulae_with_no_formula_dependents(formulae)) .to eq([formula_with_deps]) end + end - specify "with formulae parameter" do - expect(described_class.installed_formulae_with_no_formula_dependents(formulae)) - .to eq([formula_with_deps]) + describe "::unused_formulae_with_no_formula_dependents" do + include_context "with formulae for dependency testing" + + let(:tab_from_keg) { double } + + before do + allow(Tab).to receive(:for_keg).and_return(tab_from_keg) + end + + specify "installed on request" do + allow(tab_from_keg).to receive(:installed_on_request).and_return(true) + expect(described_class.unused_formulae_with_no_formula_dependents(formulae)) + .to eq([]) + end + + specify "not installed on request" do + allow(tab_from_keg).to receive(:installed_on_request).and_return(false) + expect(described_class.unused_formulae_with_no_formula_dependents(formulae)) + .to eq(formulae) + end + end + + shared_context "with formulae and casks for dependency testing" do + include_context "with formulae for dependency testing" + + require "cask/cask_loader" + + let(:cask_one_dep) do + Cask::CaskLoader.load(+<<-RUBY) + cask "red" do + depends_on formula: "two" + end + RUBY + end + + let(:cask_multiple_deps) do + Cask::CaskLoader.load(+<<-RUBY) + cask "blue" do + depends_on formula: "zero" + end + RUBY + end + + let(:cask_no_deps1) do + Cask::CaskLoader.load(+<<-RUBY) + cask "green" do + end + RUBY + end + + let(:cask_no_deps2) do + Cask::CaskLoader.load(+<<-RUBY) + cask "purple" do + end + RUBY + end + + let(:casks_no_deps) { [cask_no_deps1, cask_no_deps2] } + let(:casks_one_dep) { [cask_no_deps1, cask_no_deps2, cask_one_dep] } + let(:casks_multiple_deps) { [cask_no_deps1, cask_no_deps2, cask_multiple_deps] } + + before do + allow(described_class).to receive("[]").with("zero").and_return(formula_with_deps) + allow(described_class).to receive("[]").with("one").and_return(formula_is_dep1) + allow(described_class).to receive("[]").with("two").and_return(formula_is_dep2) + end + end + + describe "::formulae_with_cask_dependents" do + include_context "with formulae and casks for dependency testing" + + specify "no dependents" do + expect(described_class.formulae_with_cask_dependents(casks_no_deps)) + .to eq([]) + end + + specify "one dependent" do + expect(described_class.formulae_with_cask_dependents(casks_one_dep)) + .to eq([formula_is_dep2]) + end + + specify "multiple dependents" do + expect(described_class.formulae_with_cask_dependents(casks_multiple_deps)) + .to eq(formulae) end end From 85cf0ca6d4b3d9d934c61150891baaeea3209ae2 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Sun, 17 Jul 2022 00:59:43 -0700 Subject: [PATCH 07/58] Updated env variable docs on manpage Updated docs for: HOMEBREW_AUTOREMOVE HOMEBREW_NO_CLEANUP_FORMULAE --- Library/Homebrew/env_config.rb | 9 +++++---- docs/Manpage.md | 4 ++-- manpages/brew.1 | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index f56c3a0ddb..014b24d27d 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -37,8 +37,9 @@ module Homebrew default: 300, }, HOMEBREW_AUTOREMOVE: { - description: "If set, calls to `brew install`, `brew upgrade`, `brew reinstall` and `brew uninstall` " \ - "will automatically remove unused formula dependents.", + description: "If set, calls to `brew cleanup` and `brew uninstall` will automatically " \ + "remove unused formula dependents and if HOMEBREW_NO_INSTALL_CLEANUP is not set, " \ + "`brew cleanup` will start running `brew autoremove` periodically.", boolean: true, }, HOMEBREW_BAT: { @@ -268,8 +269,8 @@ module Homebrew boolean: true, }, HOMEBREW_NO_CLEANUP_FORMULAE: { - description: "A comma-separated list of formulae. Homebrew will refuse to clean up a " \ - "formula if it appears on this list.", + description: "A comma-separated list of formulae. Homebrew will refuse to clean up " \ + "or autoremove a formula if it appears on this list.", }, HOMEBREW_NO_COLOR: { description: "If set, do not print text with colour added.", diff --git a/docs/Manpage.md b/docs/Manpage.md index b7a7a28215..370adf8034 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1946,7 +1946,7 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just *Default:* `300`. - `HOMEBREW_AUTOREMOVE` -
If set, calls to `brew install`, `brew upgrade`, `brew reinstall` and `brew uninstall` will automatically remove unused formula dependents. +
If set, calls to `brew cleanup` and `brew uninstall` will automatically remove unused formula dependents and if HOMEBREW_NO_INSTALL_CLEANUP is not set, `brew cleanup` will start running `brew autoremove` periodically. - `HOMEBREW_BAT`
If set, use `bat` for the `brew cat` command. @@ -2128,7 +2128,7 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just
If set, do not check for broken linkage of dependents or outdated dependents after installing, upgrading or reinstalling formulae. This will result in fewer dependents (and their dependencies) being upgraded or reinstalled but may result in more breakage from running `brew install *`formula`*` or `brew upgrade *`formula`*`. - `HOMEBREW_NO_CLEANUP_FORMULAE` -
A comma-separated list of formulae. Homebrew will refuse to clean up a formula if it appears on this list. +
A comma-separated list of formulae. Homebrew will refuse to clean up or autoremove a formula if it appears on this list. - `HOMEBREW_NO_COLOR`
If set, do not print text with colour added. diff --git a/manpages/brew.1 b/manpages/brew.1 index 680e979622..2de072478d 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -2760,7 +2760,7 @@ Run \fBbrew update\fR once every \fBHOMEBREW_AUTO_UPDATE_SECS\fR seconds before \fBHOMEBREW_AUTOREMOVE\fR . .br -If set, calls to \fBbrew install\fR, \fBbrew upgrade\fR, \fBbrew reinstall\fR and \fBbrew uninstall\fR will automatically remove unused formula dependents\. +If set, calls to \fBbrew cleanup\fR and \fBbrew uninstall\fR will automatically remove unused formula dependents and if HOMEBREW_NO_INSTALL_CLEANUP is not set, \fBbrew cleanup\fR will start running \fBbrew autoremove\fR periodically\. . .TP \fBHOMEBREW_BAT\fR @@ -3102,7 +3102,7 @@ If set, do not check for broken linkage of dependents or outdated dependents aft \fBHOMEBREW_NO_CLEANUP_FORMULAE\fR . .br -A comma\-separated list of formulae\. Homebrew will refuse to clean up a formula if it appears on this list\. +A comma\-separated list of formulae\. Homebrew will refuse to clean up or autoremove a formula if it appears on this list\. . .TP \fBHOMEBREW_NO_COLOR\fR From ea1f2098ace015de8556fac9b8333667db0ea33c Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 28 Jul 2022 14:52:19 -0400 Subject: [PATCH 08/58] Move `HOMEBREW_SIMULATE_MACOS_ON_LINUX` handling to `SimulateSystem` --- Library/Homebrew/default_prefix.rb | 4 +++- Library/Homebrew/extend/on_system.rb | 5 ---- .../Homebrew/extend/os/linux/keg_relocate.rb | 12 ---------- Library/Homebrew/formula_auditor.rb | 2 +- Library/Homebrew/keg_relocate.rb | 9 ++++++- Library/Homebrew/resource_auditor.rb | 2 +- Library/Homebrew/simulate_system.rb | 19 ++++++++++----- Library/Homebrew/software_spec.rb | 16 +++++++------ Library/Homebrew/test/simulate_system_spec.rb | 24 +++++++++++++++++++ Library/Homebrew/test/software_spec_spec.rb | 14 +++++++++++ 10 files changed, 73 insertions(+), 34 deletions(-) diff --git a/Library/Homebrew/default_prefix.rb b/Library/Homebrew/default_prefix.rb index b22f98268d..70e493c233 100644 --- a/Library/Homebrew/default_prefix.rb +++ b/Library/Homebrew/default_prefix.rb @@ -1,10 +1,12 @@ # typed: true # frozen_string_literal: true +require "simulate_system" + module Homebrew DEFAULT_PREFIX, DEFAULT_REPOSITORY = if OS.mac? && Hardware::CPU.arm? [HOMEBREW_MACOS_ARM_DEFAULT_PREFIX, HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY] - elsif OS.linux? && !EnvConfig.simulate_macos_on_linux? + elsif Homebrew::SimulateSystem.simulating_or_running_on_linux? [HOMEBREW_LINUX_DEFAULT_PREFIX, HOMEBREW_LINUX_DEFAULT_REPOSITORY] else [HOMEBREW_DEFAULT_PREFIX, HOMEBREW_DEFAULT_REPOSITORY] diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/extend/on_system.rb index a7f7b3744f..6b3ec69ecc 100644 --- a/Library/Homebrew/extend/on_system.rb +++ b/Library/Homebrew/extend/on_system.rb @@ -20,11 +20,6 @@ module OnSystem sig { params(os_name: Symbol, or_condition: T.nilable(Symbol)).returns(T::Boolean) } def os_condition_met?(os_name, or_condition = nil) - if Homebrew::EnvConfig.simulate_macos_on_linux? - return false if os_name == :linux - return true if [:macos, *MacOSVersions::SYMBOLS.keys].include?(os_name) - end - return Homebrew::SimulateSystem.send("simulating_or_running_on_#{os_name}?") if BASE_OS_OPTIONS.include?(os_name) raise ArgumentError, "Invalid OS condition: #{os_name.inspect}" unless MacOSVersions::SYMBOLS.key?(os_name) diff --git a/Library/Homebrew/extend/os/linux/keg_relocate.rb b/Library/Homebrew/extend/os/linux/keg_relocate.rb index ee1537910c..2fb382916d 100644 --- a/Library/Homebrew/extend/os/linux/keg_relocate.rb +++ b/Library/Homebrew/extend/os/linux/keg_relocate.rb @@ -80,16 +80,4 @@ class Keg end elf_files end - - def self.bottle_dependencies - @bottle_dependencies ||= begin - formulae = [] - gcc = Formulary.factory(CompilerSelector.preferred_gcc) - if !Homebrew::EnvConfig.simulate_macos_on_linux? && - DevelopmentTools.non_apple_gcc_version("gcc") < gcc.version.to_i - formulae << gcc - end - formulae - end - end end diff --git a/Library/Homebrew/formula_auditor.rb b/Library/Homebrew/formula_auditor.rb index a06941104d..218cd91412 100644 --- a/Library/Homebrew/formula_auditor.rb +++ b/Library/Homebrew/formula_auditor.rb @@ -329,7 +329,7 @@ module Homebrew # The number of conflicts on Linux is absurd. # TODO: remove this and check these there too. - return if OS.linux? && !Homebrew::EnvConfig.simulate_macos_on_linux? + return if Homebrew::SimulateSystem.simulating_or_running_on_linux? recursive_runtime_formulae = formula.runtime_formula_dependencies(undeclared: false) version_hash = {} diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index 1df8ee86a4..a396f43205 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -368,7 +368,14 @@ class Keg end def self.bottle_dependencies - [] + return [] unless Homebrew::SimulateSystem.simulating_or_running_on_linux? + + @bottle_dependencies ||= begin + formulae = [] + gcc = Formulary.factory(CompilerSelector.preferred_gcc) + formulae << gcc if DevelopmentTools.non_apple_gcc_version("gcc") < gcc.version.to_i + formulae + end end end diff --git a/Library/Homebrew/resource_auditor.rb b/Library/Homebrew/resource_auditor.rb index 8155d311c0..624f2b8e07 100644 --- a/Library/Homebrew/resource_auditor.rb +++ b/Library/Homebrew/resource_auditor.rb @@ -105,7 +105,7 @@ module Homebrew # Ideally `ca-certificates` would not be excluded here, but sourcing a HTTP mirror was tricky. # Instead, we have logic elsewhere to pass `--insecure` to curl when downloading the certs. # TODO: try remove the OS/env conditional - if (OS.mac? || Homebrew::EnvConfig.simulate_macos_on_linux?) && spec_name == :stable && + if Homebrew::SimulateSystem.simulating_or_running_on_macos? && spec_name == :stable && owner.name != "ca-certificates" && curl_dep && !urls.find { |u| u.start_with?("http://") } problem "should always include at least one HTTP mirror" end diff --git a/Library/Homebrew/simulate_system.rb b/Library/Homebrew/simulate_system.rb index de61eabef7..206e9a460f 100644 --- a/Library/Homebrew/simulate_system.rb +++ b/Library/Homebrew/simulate_system.rb @@ -9,7 +9,14 @@ module Homebrew class << self extend T::Sig - attr_reader :os, :arch + attr_reader :arch + + sig { returns(T.nilable(Symbol)) } + def os + return :macos if @os.blank? && !OS.mac? && Homebrew::EnvConfig.simulate_macos_on_linux? + + @os + end sig { params(new_os: Symbol).void } def os=(new_os) @@ -33,16 +40,16 @@ module Homebrew sig { returns(T::Boolean) } def simulating_or_running_on_macos? - return OS.mac? if @os.blank? + return OS.mac? if os.blank? - [:macos, *MacOSVersions::SYMBOLS.keys].include?(@os) + [:macos, *MacOSVersions::SYMBOLS.keys].include?(os) end sig { returns(T::Boolean) } def simulating_or_running_on_linux? - return OS.linux? if @os.blank? + return OS.linux? if os.blank? - @os == :linux + os == :linux end sig { returns(Symbol) } @@ -52,7 +59,7 @@ module Homebrew sig { returns(Symbol) } def current_os - return @os if @os.present? + return T.must(os) if os.present? return :linux if OS.linux? MacOS.version.to_sym diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 30e9856d83..0a246d3469 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -170,14 +170,16 @@ class SoftwareSpec @uses_from_macos_elements << deps - # Linux simulating macOS. Assume oldest macOS version. - return if Homebrew::EnvConfig.simulate_macos_on_linux? && !bounds.key?(:since) - - # macOS new enough for dependency to not be required. + # Check whether macOS is new enough for dependency to not be required. if Homebrew::SimulateSystem.simulating_or_running_on_macos? - current_os = MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) - since_os = MacOS::Version.from_symbol(bounds[:since]) if bounds.key?(:since) - return if current_os >= since_os + # Assume the oldest macOS version when simulating a generic macOS version + return if Homebrew::SimulateSystem.current_os == :macos && !bounds.key?(:since) + + if Homebrew::SimulateSystem.current_os != :macos + current_os = MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) + since_os = MacOS::Version.from_symbol(bounds[:since]) if bounds.key?(:since) + return if current_os >= since_os + end end depends_on deps diff --git a/Library/Homebrew/test/simulate_system_spec.rb b/Library/Homebrew/test/simulate_system_spec.rb index 0b72700d9f..3923680064 100644 --- a/Library/Homebrew/test/simulate_system_spec.rb +++ b/Library/Homebrew/test/simulate_system_spec.rb @@ -36,6 +36,12 @@ describe Homebrew::SimulateSystem do described_class.os = :monterey expect(described_class.simulating_or_running_on_macos?).to be true end + + it "returns true on Linux with HOMEBREW_SIMULATE_MACOS_ON_LINUX", :needs_linux do + described_class.clear + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + expect(described_class.simulating_or_running_on_macos?).to be true + end end describe "::simulating_or_running_on_linux?" do @@ -66,6 +72,12 @@ describe Homebrew::SimulateSystem do described_class.os = :monterey expect(described_class.simulating_or_running_on_linux?).to be false end + + it "returns false on Linux with HOMEBREW_SIMULATE_MACOS_ON_LINUX", :needs_linux do + described_class.clear + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + expect(described_class.simulating_or_running_on_linux?).to be false + end end describe "::current_arch" do @@ -114,5 +126,17 @@ describe Homebrew::SimulateSystem do described_class.os = :monterey expect(described_class.current_os).to eq :monterey end + + it "returns the current macOS version on macOS with HOMEBREW_SIMULATE_MACOS_ON_LINUX", :needs_macos do + described_class.clear + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + expect(described_class.current_os).to eq MacOS.version.to_sym + end + + it "returns `:macos` on Linux with HOMEBREW_SIMULATE_MACOS_ON_LINUX", :needs_linux do + described_class.clear + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + expect(described_class.current_os).to eq :macos + end end end diff --git a/Library/Homebrew/test/software_spec_spec.rb b/Library/Homebrew/test/software_spec_spec.rb index a737088803..8271b35681 100644 --- a/Library/Homebrew/test/software_spec_spec.rb +++ b/Library/Homebrew/test/software_spec_spec.rb @@ -150,6 +150,20 @@ describe SoftwareSpec do expect(spec.deps.first.tags).to include(:build) end + it "ignores dependencies with HOMEBREW_SIMULATE_MACOS_ON_LINUX" do + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + spec.uses_from_macos("foo") + + expect(spec.deps).to be_empty + end + + it "ignores dependencies with tags with HOMEBREW_SIMULATE_MACOS_ON_LINUX" do + ENV["HOMEBREW_SIMULATE_MACOS_ON_LINUX"] = "1" + spec.uses_from_macos("foo" => :build) + + expect(spec.deps).to be_empty + end + it "ignores OS version specifications" do spec.uses_from_macos("foo", since: :mojave) spec.uses_from_macos("bar" => :build, :since => :mojave) From 58be76bc9d8e19c057be7d171a5e1956ed2ffaa0 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Sun, 31 Jul 2022 20:42:32 -0400 Subject: [PATCH 09/58] Fix `on_{system}` handling of generic macOS versions --- Library/Homebrew/extend/on_system.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/extend/on_system.rb index 6b3ec69ecc..1a2cf7598b 100644 --- a/Library/Homebrew/extend/on_system.rb +++ b/Library/Homebrew/extend/on_system.rb @@ -31,7 +31,11 @@ module OnSystem return false if Homebrew::SimulateSystem.simulating_or_running_on_linux? base_os = MacOS::Version.from_symbol(os_name) - current_os = MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) + current_os = if Homebrew::SimulateSystem.current_os == :macos + Version::NULL + else + MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) + end return current_os >= base_os if or_condition == :or_newer return current_os <= base_os if or_condition == :or_older From 7be6b8e6d1172b0a5a65944254c2ef4f73342ad4 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Tue, 2 Aug 2022 23:49:05 +0800 Subject: [PATCH 10/58] update-report: migrate GCC recursive dependents When GCC 12 ships (Homebrew/homebrew-core#106755) ships, most (all?) Linux bottles that depend on GCC will break. Let's fix that by using the same trick for handling divergent formula revisions when migrating formulae from linuxbrew-core (#11982). We set the recorded `version_scheme` to -1, which spoofs the formula being outdated. When `brew upgrade` installs GCC 12, the broken formulae will have their bottles reinstalled too. This works because the reinstallation will also rewrite the existing RPATHs to point to the new version of GCC instead (#13631). This should handle most of the breakage. --- Library/Homebrew/cmd/update-report.rb | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 89b8f50fca..b8aa5169f2 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -148,6 +148,8 @@ module Homebrew 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 = [] @@ -289,6 +291,34 @@ module Homebrew #{e} EOS end + + def migrate_gcc_dependents_if_needed + return if OS.mac? + + # TODO: Remove this block when GCC 12 ships. + begin + return if Formula["gcc"].version < 12 + rescue FormulaUnavailableError + return if Homebrew::EnvConfig.install_from_api? + end + + return if Settings.read("gcc.dependents.migrated") == "true" + + Formula.installed.each do |formula| + next unless formula.tap.core_tap? + next unless formula.recursive_dependencies.map(&:name).include? "gcc" + + keg = formula.installed_kegs.last + tab = Tab.for_keg(keg) + # Force reinstallation upon `brew upgrade` to fix the bottle RPATH. + tab.source["versions"]["version_scheme"] = -1 + tab.write + rescue TapFormulaUnavailableError + nil + end + + Settings.write "gcc.dependents.migrated", true + end end class Reporter From d04051a9b94553fb4290929f12aeb5c72062d2aa Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Fri, 29 Jul 2022 23:49:23 -0700 Subject: [PATCH 11/58] Add integration tests for autoremove cmd --- Library/Homebrew/test/cmd/autoremove_spec.rb | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Library/Homebrew/test/cmd/autoremove_spec.rb b/Library/Homebrew/test/cmd/autoremove_spec.rb index 24069e0bb7..1644fbf009 100644 --- a/Library/Homebrew/test/cmd/autoremove_spec.rb +++ b/Library/Homebrew/test/cmd/autoremove_spec.rb @@ -5,4 +5,34 @@ require "cmd/shared_examples/args_parse" describe "brew autoremove" do it_behaves_like "parseable arguments" + + describe "integration test" do + let(:requested_formula) { Formula["testball1"] } + let(:unused_formula) { Formula["testball2"] } + + before do + install_test_formula "testball1" + install_test_formula "testball2" + + # Make testball2 an unused dependency + tab = Tab.for_name("testball2") + tab.installed_on_request = false + tab.installed_as_dependency = true + tab.write + end + + it "only removes unused dependencies", :integration_test do + expect(requested_formula.any_version_installed?).to be true + expect(unused_formula.any_version_installed?).to be true + + # When there are unused dependencies + expect { brew "autoremove" } + .to be_a_success + .and output(/Autoremoving/).to_stdout + .and not_to_output.to_stderr + + expect(requested_formula.any_version_installed?).to be true + expect(unused_formula.any_version_installed?).to be false + end + end end From 7326019c93bb271899e379e524c7001e84d9c7eb Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Wed, 3 Aug 2022 01:32:37 -0400 Subject: [PATCH 12/58] Add clarification comment --- Library/Homebrew/extend/on_system.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Homebrew/extend/on_system.rb b/Library/Homebrew/extend/on_system.rb index 1a2cf7598b..77f323f24c 100644 --- a/Library/Homebrew/extend/on_system.rb +++ b/Library/Homebrew/extend/on_system.rb @@ -32,6 +32,8 @@ module OnSystem base_os = MacOS::Version.from_symbol(os_name) current_os = if Homebrew::SimulateSystem.current_os == :macos + # Assume the oldest macOS version when simulating a generic macOS version + # Version::NULL is always treated as less than any other version. Version::NULL else MacOS::Version.from_symbol(Homebrew::SimulateSystem.current_os) From f0d2de1869f11eb692c38360912340fb6f2a3255 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:29:53 +0800 Subject: [PATCH 13/58] Skip GCC version check This is not necessary. In addition, 1. Avoid an error when `formula.tap` is `nil`. 2. Use the more suggestive `gcc.dep.rpaths.migrated` as the setting name. --- Library/Homebrew/cmd/update-report.rb | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index b8aa5169f2..660b7cf9f2 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -294,18 +294,10 @@ module Homebrew def migrate_gcc_dependents_if_needed return if OS.mac? - - # TODO: Remove this block when GCC 12 ships. - begin - return if Formula["gcc"].version < 12 - rescue FormulaUnavailableError - return if Homebrew::EnvConfig.install_from_api? - end - - return if Settings.read("gcc.dependents.migrated") == "true" + return if Settings.read("gcc.dep.rpaths.migrated") == "true" Formula.installed.each do |formula| - next unless formula.tap.core_tap? + next unless formula.tap&.core_tap? next unless formula.recursive_dependencies.map(&:name).include? "gcc" keg = formula.installed_kegs.last @@ -317,7 +309,7 @@ module Homebrew nil end - Settings.write "gcc.dependents.migrated", true + Settings.write "gcc.dep.rpaths.migrated", true end end From 756c2b2c52828a309942802a0cf6dc6aac82d931 Mon Sep 17 00:00:00 2001 From: Michka Popoff Date: Wed, 3 Aug 2022 14:02:01 +0200 Subject: [PATCH 14/58] github/pr-pull: fix/improve parameters Fixes #13634 --- Library/Homebrew/dev-cmd/pr-pull.rb | 10 +++++----- Library/Homebrew/utils/github.rb | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index 52dd6c20c1..fb49a6d4bd 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -368,26 +368,26 @@ module Homebrew end end - def pr_check_conflicts(name, tap_remote_repo, pr) + def pr_check_conflicts(user, repo, pr) hash_template = proc { |h, k| h[k] = [] } long_build_pr_files = GitHub.search_issues( - "org:#{name}", repo: tap_remote_repo, state: "open", label: "\"no long build conflict\"" + "org:#{user}", repo: repo, state: "open", label: "\"no long build conflict\"" ).each_with_object(Hash.new(hash_template)) do |long_build_pr, hash| number = long_build_pr["number"] - GitHub.get_pull_request_changed_files(name, tap_remote_repo, number).each do |file| + GitHub.get_pull_request_changed_files("#{user}/#{repo}", number).each do |file| key = file["filename"] hash[key] << number end end - this_pr_files = GitHub.get_pull_request_changed_files(name, tap_remote_repo, pr) + this_pr_files = GitHub.get_pull_request_changed_files("#{user}/#{repo}", pr) conflicts = this_pr_files.each_with_object(Hash.new(hash_template)) do |file, hash| filename = file["filename"] next unless long_build_pr_files.key?(filename) long_build_pr_files[filename].each do |pr_number| - key = "#{tap_remote_repo}/pull/#{pr_number}" + key = "#{user}/#{repo}/pull/#{pr_number}" hash[key] << filename end end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index f75af77fa1..d3d956bd55 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -480,7 +480,7 @@ module GitHub def check_for_duplicate_pull_requests(name, tap_remote_repo, state:, file:, args:, version: nil) pull_requests = fetch_pull_requests(name, tap_remote_repo, state: state, version: version).select do |pr| get_pull_request_changed_files( - name, tap_remote_repo, pr["number"] + tap_remote_repo, pr["number"] ).any? { |f| f["filename"] == file } end return if pull_requests.blank? @@ -502,8 +502,8 @@ module GitHub end end - def get_pull_request_changed_files(name, tap_remote_repo, pr) - API.open_rest(url_to("repos", name, tap_remote_repo, "pulls", pr, "files")) + def get_pull_request_changed_files(tap_remote_repo, pr) + API.open_rest(url_to("repos", tap_remote_repo, "pulls", pr, "files")) end def forked_repo_info!(tap_remote_repo, org: nil) From e1f8fa2c9be199365e25cd1ab490095001c18aa8 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Wed, 3 Aug 2022 20:15:45 +0800 Subject: [PATCH 15/58] Improve settings name This leads to a slightly more readable entry in `.git/config`. --- Library/Homebrew/cmd/update-report.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 660b7cf9f2..a2dc75bf97 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -294,7 +294,7 @@ module Homebrew def migrate_gcc_dependents_if_needed return if OS.mac? - return if Settings.read("gcc.dep.rpaths.migrated") == "true" + return if Settings.read("gcc-rpaths.fixed") == "true" Formula.installed.each do |formula| next unless formula.tap&.core_tap? @@ -309,7 +309,7 @@ module Homebrew nil end - Settings.write "gcc.dep.rpaths.migrated", true + Settings.write "gcc-rpaths.fixed", true end end From d5f949e60b6d78aa50f0409a2dfb50a63eaa554e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 2 Aug 2022 21:56:45 -0700 Subject: [PATCH 16/58] Check dependency order in on_system methods --- Library/Homebrew/rubocops/components_order.rb | 6 ------ Library/Homebrew/rubocops/dependency_order.rb | 2 +- Library/Homebrew/rubocops/extend/formula.rb | 6 ++++++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/rubocops/components_order.rb b/Library/Homebrew/rubocops/components_order.rb index 002ce1f9cf..5d28a172a1 100644 --- a/Library/Homebrew/rubocops/components_order.rb +++ b/Library/Homebrew/rubocops/components_order.rb @@ -14,12 +14,6 @@ module RuboCop class ComponentsOrder < FormulaCop extend AutoCorrector - def on_system_methods - @on_system_methods ||= [:intel, :arm, :macos, :linux, :system, *MacOSVersions::SYMBOLS.keys].map do |m| - :"on_#{m}" - end - end - def audit_formula(_node, _class_node, _parent_class_node, body_node) @present_components, @offensive_nodes = check_order(FORMULA_COMPONENT_PRECEDENCE_LIST, body_node) diff --git a/Library/Homebrew/rubocops/dependency_order.rb b/Library/Homebrew/rubocops/dependency_order.rb index fc12fe4119..34775de930 100644 --- a/Library/Homebrew/rubocops/dependency_order.rb +++ b/Library/Homebrew/rubocops/dependency_order.rb @@ -16,7 +16,7 @@ module RuboCop def audit_formula(_node, _class_node, _parent_class_node, body_node) check_dependency_nodes_order(body_node) check_uses_from_macos_nodes_order(body_node) - [:head, :stable].each do |block_name| + ([:head, :stable] + on_system_methods).each do |block_name| block = find_block(body_node, block_name) next unless block diff --git a/Library/Homebrew/rubocops/extend/formula.rb b/Library/Homebrew/rubocops/extend/formula.rb index e6cc658350..d4b09bab73 100644 --- a/Library/Homebrew/rubocops/extend/formula.rb +++ b/Library/Homebrew/rubocops/extend/formula.rb @@ -198,6 +198,12 @@ module RuboCop @file_path !~ Regexp.union(paths_to_exclude) end + + def on_system_methods + @on_system_methods ||= [:intel, :arm, :macos, :linux, :system, *MacOSVersions::SYMBOLS.keys].map do |m| + :"on_#{m}" + end + end end end end From b5a6f90cd82e746f67fa5d90d5146084fcc20982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Tue, 2 Aug 2022 23:04:28 -0700 Subject: [PATCH 17/58] Add tests --- .../test/rubocops/dependency_order_spec.rb | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Library/Homebrew/test/rubocops/dependency_order_spec.rb b/Library/Homebrew/test/rubocops/dependency_order_spec.rb index b080d55ef7..465477dac0 100644 --- a/Library/Homebrew/test/rubocops/dependency_order_spec.rb +++ b/Library/Homebrew/test/rubocops/dependency_order_spec.rb @@ -114,6 +114,34 @@ describe RuboCop::Cop::FormulaAudit::DependencyOrder do end RUBY end + + it "reports and corrects wrong conditional order within a system block" do + expect_offense(<<~RUBY) + class Foo < Formula + homepage "https://brew.sh" + url "https://brew.sh/foo-1.0.tgz" + on_arm do + uses_from_macos "apple" if build.with? "foo" + uses_from_macos "bar" + ^^^^^^^^^^^^^^^^^^^^^ dependency "bar" (line 6) should be put before dependency "apple" (line 5) + uses_from_macos "foo" => :optional + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dependency "foo" (line 7) should be put before dependency "apple" (line 5) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + homepage "https://brew.sh" + url "https://brew.sh/foo-1.0.tgz" + on_arm do + uses_from_macos "bar" + uses_from_macos "foo" => :optional + uses_from_macos "apple" if build.with? "foo" + end + end + RUBY + end end context "when auditing `depends_on`" do @@ -224,5 +252,33 @@ describe RuboCop::Cop::FormulaAudit::DependencyOrder do end RUBY end + + it "reports and corrects wrong conditional order within a system block" do + expect_offense(<<~RUBY) + class Foo < Formula + homepage "https://brew.sh" + url "https://brew.sh/foo-1.0.tgz" + on_linux do + depends_on "apple" if build.with? "foo" + depends_on "bar" + ^^^^^^^^^^^^^^^^ dependency "bar" (line 6) should be put before dependency "apple" (line 5) + depends_on "foo" => :optional + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dependency "foo" (line 7) should be put before dependency "apple" (line 5) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + homepage "https://brew.sh" + url "https://brew.sh/foo-1.0.tgz" + on_linux do + depends_on "bar" + depends_on "foo" => :optional + depends_on "apple" if build.with? "foo" + end + end + RUBY + end end end From 0355c60787c01b86f49c0028d470115e17bb294f Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 22:06:00 +0100 Subject: [PATCH 18/58] dev-cmd/contributions: Retrieve a user's repo contributions over time - Before each AGM it's currently a manual process for a PLC member to search commit logs and GitHub to figure out who contributed to Homebrew, so who should remain a member. - I noticed that [looking at commits for a user](https://github.com/Homebrew/homebrew-core/commits?author=issyl0&since=2022-01-01&until=2023-01-01) would not count `Co-Authored-By`, which happens a lot now there's an autosquash action on PRs in `Homebrew/homebrew-core`, say if someone fixed a formula's build or tests or whatever and then the PR got auto-merged. - Here's `brew contributions` that uses `git log` to be able to go back through all time or a specific time period (`--from`, `--to`). It's up to individual PLC discretion for "activity", but it does at least go some way to automating the data retrieval. - Example (I can use my username as `--email` because my username is in all of the email addresses that I use for committing to Homebrew): ``` $ brew contributions --email=issyl0 --repos=brew,core Person issyl0 directly authored 732 commits and co-authored 31 commits to brew, core in all time. ``` --- Library/Homebrew/dev-cmd/contributions.rb | 101 ++++++++++++++++++ .../test/dev-cmd/contributions_spec.rb | 8 ++ 2 files changed, 109 insertions(+) create mode 100755 Library/Homebrew/dev-cmd/contributions.rb create mode 100644 Library/Homebrew/test/dev-cmd/contributions_spec.rb diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb new file mode 100755 index 0000000000..2a528cf86b --- /dev/null +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -0,0 +1,101 @@ +# typed: true +# frozen_string_literal: true + +require "cli/parser" + +module Homebrew + extend T::Sig + + module_function + + SUPPORTED_REPOS = %w[brew core cask bundle].freeze + + sig { returns(CLI::Parser) } + def contributions_args + Homebrew::CLI::Parser.new do + usage_banner <<~EOS + `contributions` + + Contributions to Homebrew repos for a user. + EOS + + flag "--email=", + description: "A user's email address that they commit with." + + flag "--from=", + description: "Date (ISO-8601 format) to start searching contributions." + + flag "--to=", + description: "Date (ISO-8601 format) to stop searching contributions." + + comma_array "--repos=", + description: "The Homebrew repositories to search for contributions in. " \ + "Comma separated. Supported repos: #{SUPPORTED_REPOS.join(", ")}." + + named_args :none + end + end + + sig { returns(NilClass) } + def contributions + args = contributions_args.parse + + return ofail "`--repos` and `--email` are required." if !args[:repos] || !args[:email] + + commits = 0 + coauthorships = 0 + + args[:repos].each do |repo| + repo_location = find_repo_path_for_repo(repo) + unless repo_location + return ofail "Couldn't find location for #{repo}. Do you have it tapped, or is there a typo? " \ + "We only support #{SUPPORTED_REPOS.join(", ")} repos so far." + end + + commits += git_log_cmd("author", repo_location, args) + coauthorships += git_log_cmd("coauthorships", repo_location, args) + end + + sentence = "Person #{args[:email]} directly authored #{commits} commits" + sentence += " and co-authored #{coauthorships} commits" + sentence += " to #{args[:repos].join(", ")}" + sentence += if args[:from] && args[:to] + " between #{args[:from]} and #{args[:to]}" + elsif args[:from] + " after #{args[:from]}" + elsif args[:to] + " before #{args[:to]}" + else + " in all time" + end + sentence += "." + + puts sentence + end + + sig { params(repo: String).returns(T.nilable(String)) } + def find_repo_path_for_repo(repo) + case repo + when "brew" + HOMEBREW_REPOSITORY + when "core" + "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-core" + when "cask" + "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-cask" + when "bundle" + "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-bundle" + end + end + + sig { params(kind: String, repo_location: String, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_cmd(kind, repo_location, args) + cmd = "git -C #{repo_location} log --oneline" + cmd += " --author=#{args[:email]}" if kind == "author" + cmd += " --format='%(trailers:key=Co-authored-by:)'" if kind == "coauthorships" + cmd += " --before=#{args[:to]}" if args[:to] + cmd += " --after=#{args[:from]}" if args[:from] + cmd += " | grep #{args[:email]}" if kind == "coauthorships" + + `#{cmd} | wc -l`.strip.to_i + end +end diff --git a/Library/Homebrew/test/dev-cmd/contributions_spec.rb b/Library/Homebrew/test/dev-cmd/contributions_spec.rb new file mode 100644 index 0000000000..bd60333dc0 --- /dev/null +++ b/Library/Homebrew/test/dev-cmd/contributions_spec.rb @@ -0,0 +1,8 @@ +# typed: false +# frozen_string_literal: true + +require "cmd/shared_examples/args_parse" + +describe "brew contributions" do + it_behaves_like "parseable arguments" +end From 503b51c54f6588c9e38037e8cbfb9cda4f1aba7e Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 22:44:25 +0100 Subject: [PATCH 19/58] dev-cmd/contributions: Better error messages for bad `--repos` ``` $ brew contributions --email=me@issyl0.co.uk --repos=coreeeee Error: Unsupported repo: coreeeee. Try one of brew, core, cask, bundle. $ brew untap homebrew/bundle $ brew contributions --email=me@issyl0.co.uk --repos=bundle Error: Couldn't find repo bundle locally. Do you have it tapped? ``` --- Library/Homebrew/dev-cmd/contributions.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 2a528cf86b..b3eb9c3bc5 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -46,14 +46,15 @@ module Homebrew coauthorships = 0 args[:repos].each do |repo| - repo_location = find_repo_path_for_repo(repo) - unless repo_location - return ofail "Couldn't find location for #{repo}. Do you have it tapped, or is there a typo? " \ - "We only support #{SUPPORTED_REPOS.join(", ")} repos so far." + if SUPPORTED_REPOS.exclude?(repo) + return ofail "Unsupported repo: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." end - commits += git_log_cmd("author", repo_location, args) - coauthorships += git_log_cmd("coauthorships", repo_location, args) + repo_path = find_repo_path_for_repo(repo) + return ofail "Couldn't find repo #{repo} locally. Do you have it tapped?" unless Dir.exist?(repo_path) + + commits += git_log_cmd("author", repo_path, args) + coauthorships += git_log_cmd("coauthorships", repo_path, args) end sentence = "Person #{args[:email]} directly authored #{commits} commits" @@ -87,9 +88,9 @@ module Homebrew end end - sig { params(kind: String, repo_location: String, args: Homebrew::CLI::Args).returns(Integer) } - def git_log_cmd(kind, repo_location, args) - cmd = "git -C #{repo_location} log --oneline" + sig { params(kind: String, repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_cmd(kind, repo_path, args) + cmd = "git -C #{repo_path} log --oneline" cmd += " --author=#{args[:email]}" if kind == "author" cmd += " --format='%(trailers:key=Co-authored-by:)'" if kind == "coauthorships" cmd += " --before=#{args[:to]}" if args[:to] From fef9a286feb08c938b5f8f70403c512b50646657 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 23:18:27 +0100 Subject: [PATCH 20/58] dev-cmd/contributions: Simplify `find_repo_path_for_repo` Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index b3eb9c3bc5..4a8ace15e5 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -51,7 +51,7 @@ module Homebrew end repo_path = find_repo_path_for_repo(repo) - return ofail "Couldn't find repo #{repo} locally. Do you have it tapped?" unless Dir.exist?(repo_path) + return ofail "Couldn't find repo #{repo} locally. Run `brew tap homebrew/#{repo}`." unless repo_path.exist? commits += git_log_cmd("author", repo_path, args) coauthorships += git_log_cmd("coauthorships", repo_path, args) @@ -76,16 +76,9 @@ module Homebrew sig { params(repo: String).returns(T.nilable(String)) } def find_repo_path_for_repo(repo) - case repo - when "brew" - HOMEBREW_REPOSITORY - when "core" - "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-core" - when "cask" - "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-cask" - when "bundle" - "#{HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-bundle" - end + return HOMEBREW_REPOSITORY if repo == "brew" + + Tap.fetch("homebrew", repo).path end sig { params(kind: String, repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } From c330ea4e3bacc52d80b1213feca846443fa776ed Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 23:24:52 +0100 Subject: [PATCH 21/58] dev-cmd/contributions: Split `git_log_cmd` into two methods - Otherwise it's quite involved and a bit hard to read. --- Library/Homebrew/dev-cmd/contributions.rb | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 4a8ace15e5..ff29bb9a8f 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -53,8 +53,8 @@ module Homebrew repo_path = find_repo_path_for_repo(repo) return ofail "Couldn't find repo #{repo} locally. Run `brew tap homebrew/#{repo}`." unless repo_path.exist? - commits += git_log_cmd("author", repo_path, args) - coauthorships += git_log_cmd("coauthorships", repo_path, args) + commits += git_log_author_cmd(repo_path, args) + coauthorships += git_log_coauthor_cmd(repo_path, args) end sentence = "Person #{args[:email]} directly authored #{commits} commits" @@ -81,14 +81,22 @@ module Homebrew Tap.fetch("homebrew", repo).path end - sig { params(kind: String, repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } - def git_log_cmd(kind, repo_path, args) - cmd = "git -C #{repo_path} log --oneline" - cmd += " --author=#{args[:email]}" if kind == "author" - cmd += " --format='%(trailers:key=Co-authored-by:)'" if kind == "coauthorships" + sig { params(repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_author_cmd(repo_path, args) + cmd = "git -C #{repo_path} log --oneline --author=#{args[:email]}" cmd += " --before=#{args[:to]}" if args[:to] cmd += " --after=#{args[:from]}" if args[:from] - cmd += " | grep #{args[:email]}" if kind == "coauthorships" + + `#{cmd} | wc -l`.strip.to_i + end + + sig { params(repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } + def git_log_coauthor_cmd(repo_path, args) + cmd = "git -C #{repo_path} log --oneline" + cmd += " --format='%(trailers:key=Co-authored-by:)'" + cmd += " --before=#{args[:to]}" if args[:to] + cmd += " --after=#{args[:from]}" if args[:from] + cmd += " | grep #{args[:email]}" `#{cmd} | wc -l`.strip.to_i end From 441df3d866df9c4cae5503945e03036bae71dbbd Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 23:32:48 +0100 Subject: [PATCH 22/58] dev-cmd/contributions: Switch `--email` to be a named arg - This way we can do the more intuitive: ``` $ brew contributions --repos=brew,core rslpolster@gmail.com Person rslpolster@gmail.com directly authored 1580 commits and co-authored 125 commits to brew, core in all time. ``` --- Library/Homebrew/dev-cmd/contributions.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index ff29bb9a8f..c727d04f1f 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -14,14 +14,11 @@ module Homebrew def contributions_args Homebrew::CLI::Parser.new do usage_banner <<~EOS - `contributions` + `contributions [email]` Contributions to Homebrew repos for a user. EOS - flag "--email=", - description: "A user's email address that they commit with." - flag "--from=", description: "Date (ISO-8601 format) to start searching contributions." @@ -32,7 +29,7 @@ module Homebrew description: "The Homebrew repositories to search for contributions in. " \ "Comma separated. Supported repos: #{SUPPORTED_REPOS.join(", ")}." - named_args :none + named_args :email end end @@ -40,7 +37,7 @@ module Homebrew def contributions args = contributions_args.parse - return ofail "`--repos` and `--email` are required." if !args[:repos] || !args[:email] + return ofail "`--repos` is required." if args.repos.empty? commits = 0 coauthorships = 0 @@ -57,7 +54,7 @@ module Homebrew coauthorships += git_log_coauthor_cmd(repo_path, args) end - sentence = "Person #{args[:email]} directly authored #{commits} commits" + sentence = "Person #{args.named.first} directly authored #{commits} commits" sentence += " and co-authored #{coauthorships} commits" sentence += " to #{args[:repos].join(", ")}" sentence += if args[:from] && args[:to] @@ -83,7 +80,7 @@ module Homebrew sig { params(repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } def git_log_author_cmd(repo_path, args) - cmd = "git -C #{repo_path} log --oneline --author=#{args[:email]}" + cmd = "git -C #{repo_path} log --oneline --author=#{args.named.first}" cmd += " --before=#{args[:to]}" if args[:to] cmd += " --after=#{args[:from]}" if args[:from] @@ -96,7 +93,7 @@ module Homebrew cmd += " --format='%(trailers:key=Co-authored-by:)'" cmd += " --before=#{args[:to]}" if args[:to] cmd += " --after=#{args[:from]}" if args[:from] - cmd += " | grep #{args[:email]}" + cmd += " | grep #{args.named.first}" `#{cmd} | wc -l`.strip.to_i end From 8a67ce76b5b35840ae2e429dedf4168e807e644d Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 24 Jul 2022 23:41:00 +0100 Subject: [PATCH 23/58] dev-cmd/contributions: Appease `brew typecheck` --- Library/Homebrew/dev-cmd/contributions.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index c727d04f1f..99cb102922 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -37,7 +37,7 @@ module Homebrew def contributions args = contributions_args.parse - return ofail "`--repos` is required." if args.repos.empty? + return ofail "`--repos` is required." if args[:repos].empty? commits = 0 coauthorships = 0 @@ -50,8 +50,8 @@ module Homebrew repo_path = find_repo_path_for_repo(repo) return ofail "Couldn't find repo #{repo} locally. Run `brew tap homebrew/#{repo}`." unless repo_path.exist? - commits += git_log_author_cmd(repo_path, args) - coauthorships += git_log_coauthor_cmd(repo_path, args) + commits += git_log_author_cmd(T.must(repo_path), args) + coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) end sentence = "Person #{args.named.first} directly authored #{commits} commits" @@ -71,14 +71,14 @@ module Homebrew puts sentence end - sig { params(repo: String).returns(T.nilable(String)) } + sig { params(repo: String).returns(Pathname) } def find_repo_path_for_repo(repo) return HOMEBREW_REPOSITORY if repo == "brew" Tap.fetch("homebrew", repo).path end - sig { params(repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } + sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } def git_log_author_cmd(repo_path, args) cmd = "git -C #{repo_path} log --oneline --author=#{args.named.first}" cmd += " --before=#{args[:to]}" if args[:to] @@ -87,7 +87,7 @@ module Homebrew `#{cmd} | wc -l`.strip.to_i end - sig { params(repo_path: String, args: Homebrew::CLI::Args).returns(Integer) } + sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } def git_log_coauthor_cmd(repo_path, args) cmd = "git -C #{repo_path} log --oneline" cmd += " --format='%(trailers:key=Co-authored-by:)'" From 33d3aef2950bc16b3fc3fc971ba672c4dfaf07d1 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 25 Jul 2022 10:28:51 +0100 Subject: [PATCH 24/58] dev-cmd/contributions: Only one email is allowed currently Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 99cb102922..e035f27081 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -29,7 +29,7 @@ module Homebrew description: "The Homebrew repositories to search for contributions in. " \ "Comma separated. Supported repos: #{SUPPORTED_REPOS.join(", ")}." - named_args :email + named_args :email, number: 1 end end From f6b2fe50935f75d8bc258da2600f005faa0722da Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 25 Jul 2022 10:30:12 +0100 Subject: [PATCH 25/58] dev-cmd/contributions: Improve sentence string line wrapping Co-authored-by: Mike McQuaid --- Library/Homebrew/dev-cmd/contributions.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index e035f27081..9a7960e117 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -54,9 +54,9 @@ module Homebrew coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) end - sentence = "Person #{args.named.first} directly authored #{commits} commits" - sentence += " and co-authored #{coauthorships} commits" - sentence += " to #{args[:repos].join(", ")}" + sentence = "Person #{args.named.first} directly authored #{commits} commits" \ + " and co-authored #{coauthorships} commits" \ + " to #{args[:repos].join(", ")}" sentence += if args[:from] && args[:to] " between #{args[:from]} and #{args[:to]}" elsif args[:from] From 70c300daf240c90c52071ef222b54e444d07056a Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 25 Jul 2022 10:43:55 +0100 Subject: [PATCH 26/58] dev-cmd/contributions: Use `Utils.safe_popen_read` not backticks --- Library/Homebrew/dev-cmd/contributions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 9a7960e117..dbf336b966 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -84,7 +84,7 @@ module Homebrew cmd += " --before=#{args[:to]}" if args[:to] cmd += " --after=#{args[:from]}" if args[:from] - `#{cmd} | wc -l`.strip.to_i + Utils.safe_popen_read(cmd).lines.count end sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } @@ -95,6 +95,6 @@ module Homebrew cmd += " --after=#{args[:from]}" if args[:from] cmd += " | grep #{args.named.first}" - `#{cmd} | wc -l`.strip.to_i + Utils.safe_popen_read(cmd).lines.count end end From 5a34bc86c4bc91474ee175e7cd1745325223911a Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 25 Jul 2022 10:52:32 +0100 Subject: [PATCH 27/58] dev-cmd/contributions: RuboCop has opinions on where spaces should go --- Library/Homebrew/dev-cmd/contributions.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index dbf336b966..22aa7b307b 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -54,9 +54,9 @@ module Homebrew coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) end - sentence = "Person #{args.named.first} directly authored #{commits} commits" \ - " and co-authored #{coauthorships} commits" \ - " to #{args[:repos].join(", ")}" + sentence = "Person #{args.named.first} directly authored #{commits} commits " \ + "and co-authored #{coauthorships} commits " \ + "to #{args[:repos].join(", ")}" sentence += if args[:from] && args[:to] " between #{args[:from]} and #{args[:to]}" elsif args[:from] From 8e5a5672fbfc82dec4eb6108346d13302159aec0 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 11:20:04 +0100 Subject: [PATCH 28/58] dev-cmd/contributions: Better shell argument handling, plus stop piping to `grep` --- Library/Homebrew/dev-cmd/contributions.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 22aa7b307b..d83a6e78ca 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -80,21 +80,20 @@ module Homebrew sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } def git_log_author_cmd(repo_path, args) - cmd = "git -C #{repo_path} log --oneline --author=#{args.named.first}" - cmd += " --before=#{args[:to]}" if args[:to] - cmd += " --after=#{args[:from]}" if args[:from] + cmd = ["git", "-C", repo_path, "log", "--oneline", "--author=#{args.named.first}"] + cmd << "--before=#{args[:to]}" if args[:to] + cmd << "--after=#{args[:from]}" if args[:from] - Utils.safe_popen_read(cmd).lines.count + Utils.safe_popen_read(*cmd).lines.count end sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } def git_log_coauthor_cmd(repo_path, args) - cmd = "git -C #{repo_path} log --oneline" - cmd += " --format='%(trailers:key=Co-authored-by:)'" - cmd += " --before=#{args[:to]}" if args[:to] - cmd += " --after=#{args[:from]}" if args[:from] - cmd += " | grep #{args.named.first}" + cmd = ["git", "-C", repo_path, "log", "--oneline"] + cmd << "--format='%(trailers:key=Co-authored-by:)'" + cmd << "--before=#{args[:to]}" if args[:to] + cmd << "--after=#{args[:from]}" if args[:from] - Utils.safe_popen_read(cmd).lines.count + Utils.safe_popen_read(*cmd).lines.select { |l| l.include?(args.named.first) }.length end end From be08b773f15df746415490117214e400ee593fbe Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 11:28:14 +0100 Subject: [PATCH 29/58] dev-cmd/contributions: Add `--all` to scan everything, plus auto-tapping --- Library/Homebrew/dev-cmd/contributions.rb | 27 +++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index d83a6e78ca..7b4c530f30 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -8,7 +8,11 @@ module Homebrew module_function - SUPPORTED_REPOS = %w[brew core cask bundle].freeze + SUPPORTED_REPOS = ( + %w[brew core cask] + + OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") } + + OFFICIAL_CASK_TAPS + ).freeze sig { returns(CLI::Parser) } def contributions_args @@ -25,10 +29,15 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." + flag "--all", + description: "Show contributions across all official Homebrew formula, cask and command repos." + comma_array "--repos=", description: "The Homebrew repositories to search for contributions in. " \ "Comma separated. Supported repos: #{SUPPORTED_REPOS.join(", ")}." + conflicts "--all", "--repos" + named_args :email, number: 1 end end @@ -37,18 +46,22 @@ module Homebrew def contributions args = contributions_args.parse - return ofail "`--repos` is required." if args[:repos].empty? - commits = 0 coauthorships = 0 - args[:repos].each do |repo| - if SUPPORTED_REPOS.exclude?(repo) + repos = args[:repos] || SUPPORTED_REPOS + repos.each do |repo| + if !args[:all] && SUPPORTED_REPOS.exclude?(repo) return ofail "Unsupported repo: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." end repo_path = find_repo_path_for_repo(repo) - return ofail "Couldn't find repo #{repo} locally. Run `brew tap homebrew/#{repo}`." unless repo_path.exist? + if !repo_path.exist? + next if repo == "versions" # This tap is deprecated, tapping it will error. + + opoo "Couldn't find repo #{repo} locally. Tapping it now..." + Utils.safe_system("brew", "tap", repo_path) # TODO: Figure out why `exit code 1` happens here. + end commits += git_log_author_cmd(T.must(repo_path), args) coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) @@ -56,7 +69,7 @@ module Homebrew sentence = "Person #{args.named.first} directly authored #{commits} commits " \ "and co-authored #{coauthorships} commits " \ - "to #{args[:repos].join(", ")}" + "to #{args[:all] ? "all Homebrew repos" : repos.join(", ")}" sentence += if args[:from] && args[:to] " between #{args[:from]} and #{args[:to]}" elsif args[:from] From 5fa192c261d33e3ab84d397c3a427867f7b07c25 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 12:00:27 +0100 Subject: [PATCH 30/58] dev-cmd/contributions: Use `--repos=all` not a separate `--all` flag --- Library/Homebrew/dev-cmd/contributions.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 7b4c530f30..7320c18d30 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -29,14 +29,10 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - flag "--all", - description: "Show contributions across all official Homebrew formula, cask and command repos." - comma_array "--repos=", description: "The Homebrew repositories to search for contributions in. " \ - "Comma separated. Supported repos: #{SUPPORTED_REPOS.join(", ")}." - - conflicts "--all", "--repos" + "Comma separated. Pass `all` to search all repos. " \ + "Supported repos: #{SUPPORTED_REPOS.join(", ")}." named_args :email, number: 1 end @@ -49,9 +45,10 @@ module Homebrew commits = 0 coauthorships = 0 - repos = args[:repos] || SUPPORTED_REPOS + all_repos = args[:repos].first == "all" + repos = all_repos ? SUPPORTED_REPOS : args[:repos] repos.each do |repo| - if !args[:all] && SUPPORTED_REPOS.exclude?(repo) + if SUPPORTED_REPOS.exclude?(repo) return ofail "Unsupported repo: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." end @@ -69,7 +66,7 @@ module Homebrew sentence = "Person #{args.named.first} directly authored #{commits} commits " \ "and co-authored #{coauthorships} commits " \ - "to #{args[:all] ? "all Homebrew repos" : repos.join(", ")}" + "across #{all_repos ? "all Homebrew repos" : repos.join(", ")}" sentence += if args[:from] && args[:to] " between #{args[:from]} and #{args[:to]}" elsif args[:from] From 6974faa29ba1a5d66877efe5ab841b8eb017a548 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 12:10:05 +0100 Subject: [PATCH 31/58] dev-cmd/contributions: Appease `brew style` --- Library/Homebrew/dev-cmd/contributions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 7320c18d30..1a8c0ba2f5 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -53,7 +53,7 @@ module Homebrew end repo_path = find_repo_path_for_repo(repo) - if !repo_path.exist? + unless repo_path.exist? next if repo == "versions" # This tap is deprecated, tapping it will error. opoo "Couldn't find repo #{repo} locally. Tapping it now..." @@ -104,6 +104,6 @@ module Homebrew cmd << "--before=#{args[:to]}" if args[:to] cmd << "--after=#{args[:from]}" if args[:from] - Utils.safe_popen_read(*cmd).lines.select { |l| l.include?(args.named.first) }.length + Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(args.named.first) } end end From dd986f770541cde17bfc913dccd95d030ec9da98 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 12:49:37 +0100 Subject: [PATCH 32/58] dev-cmd/contributions: Use `.to_sentence` not `.join(", ")` Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 1a8c0ba2f5..e841a4b4b2 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -66,7 +66,7 @@ module Homebrew sentence = "Person #{args.named.first} directly authored #{commits} commits " \ "and co-authored #{coauthorships} commits " \ - "across #{all_repos ? "all Homebrew repos" : repos.join(", ")}" + "across #{all_repos ? "all Homebrew repos" : repos.to_sentence}" sentence += if args[:from] && args[:to] " between #{args[:from]} and #{args[:to]}" elsif args[:from] From 1ac109720352548c1a29dec12627313a5df4582b Mon Sep 17 00:00:00 2001 From: Issy Long Date: Thu, 28 Jul 2022 12:50:04 +0100 Subject: [PATCH 33/58] dev-cmd/contributions: Better usage banner Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index e841a4b4b2..049eed6fe3 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -17,9 +17,8 @@ module Homebrew sig { returns(CLI::Parser) } def contributions_args Homebrew::CLI::Parser.new do - usage_banner <<~EOS - `contributions [email]` - + usage_banner "`contributions` []" + description <<~EOS Contributions to Homebrew repos for a user. EOS From f5d57781864fde455b9153fdf33900e450e8b630 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Fri, 29 Jul 2022 21:19:42 +0100 Subject: [PATCH 34/58] dev-cmd/contributions: Use `--repositories` not `--repos` Co-authored-by: Mike McQuaid --- Library/Homebrew/dev-cmd/contributions.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 049eed6fe3..2689a46a63 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -28,10 +28,10 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - comma_array "--repos=", + comma_array "--repositories=", description: "The Homebrew repositories to search for contributions in. " \ - "Comma separated. Pass `all` to search all repos. " \ - "Supported repos: #{SUPPORTED_REPOS.join(", ")}." + "Comma separated. Pass `all` to search all repositories. " \ + "Supported repositories: #{SUPPORTED_REPOS.join(", ")}." named_args :email, number: 1 end @@ -44,18 +44,18 @@ module Homebrew commits = 0 coauthorships = 0 - all_repos = args[:repos].first == "all" - repos = all_repos ? SUPPORTED_REPOS : args[:repos] + all_repos = args[:repositories].first == "all" + repos = all_repos ? SUPPORTED_REPOS : args[:repositories] repos.each do |repo| if SUPPORTED_REPOS.exclude?(repo) - return ofail "Unsupported repo: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." + return ofail "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." end repo_path = find_repo_path_for_repo(repo) unless repo_path.exist? next if repo == "versions" # This tap is deprecated, tapping it will error. - opoo "Couldn't find repo #{repo} locally. Tapping it now..." + opoo "Repository #{repo} not yet tapped! Tapping it now..." Utils.safe_system("brew", "tap", repo_path) # TODO: Figure out why `exit code 1` happens here. end From 63a1a078b9b101fd5654ffcdf099632f3f258851 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Fri, 29 Jul 2022 21:20:16 +0100 Subject: [PATCH 35/58] dev-cmd/contributions: Improve `SUPPORTED_REPOS` array syntax Co-authored-by: Mike McQuaid --- Library/Homebrew/dev-cmd/contributions.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 2689a46a63..73a77c1261 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -8,11 +8,11 @@ module Homebrew module_function - SUPPORTED_REPOS = ( - %w[brew core cask] + - OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") } + - OFFICIAL_CASK_TAPS - ).freeze + SUPPORTED_REPOS = [ + %w[brew core cask], + OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") }, + OFFICIAL_CASK_TAPS, + ].flatten.freeze sig { returns(CLI::Parser) } def contributions_args From 5d312d25d0094c6cdbb1ff379ea6c15cc4ac1b8c Mon Sep 17 00:00:00 2001 From: Issy Long Date: Fri, 29 Jul 2022 21:49:59 +0100 Subject: [PATCH 36/58] dev-cmd/contributions: Properly auto-tap --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 73a77c1261..3e68f32d3d 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -56,7 +56,7 @@ module Homebrew next if repo == "versions" # This tap is deprecated, tapping it will error. opoo "Repository #{repo} not yet tapped! Tapping it now..." - Utils.safe_system("brew", "tap", repo_path) # TODO: Figure out why `exit code 1` happens here. + Tap.fetch("homebrew", repo).install end commits += git_log_author_cmd(T.must(repo_path), args) From c02e03a1798c383863ee87b7a14a66a122db6679 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sat, 30 Jul 2022 00:36:31 +0100 Subject: [PATCH 37/58] dev-cmd/contributions: Use methods to get arguments - I got these with hash syntax because I couldn't figure out Sorbet, but there's `args.rbi` to add the CLI args methods to. Nice! - In doing this I realised that `--repositories` is required again, we no longer infer `--repositories=all` from no `--repositories` passed as we did in a previous version of this. --- Library/Homebrew/cli/args.rbi | 9 ++++++++ Library/Homebrew/dev-cmd/contributions.rb | 25 ++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Library/Homebrew/cli/args.rbi b/Library/Homebrew/cli/args.rbi index 32b1cd8e6e..e0aaee87f1 100644 --- a/Library/Homebrew/cli/args.rbi +++ b/Library/Homebrew/cli/args.rbi @@ -300,6 +300,15 @@ module Homebrew sig { returns(T.nilable(String)) } def screen_saverdir; end + sig { returns(T::Array[String])} + def repositories; end + + sig { returns(T.nilable(String)) } + def from; end + + sig { returns(T.nilable(String)) } + def to; end + sig { returns(T.nilable(T::Array[String])) } def groups; end diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 3e68f32d3d..0911b3d361 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -40,12 +40,13 @@ module Homebrew sig { returns(NilClass) } def contributions args = contributions_args.parse + return ofail "Please specify `--repositories` to search, or `--repositories=all`." unless args[:repositories].empty? commits = 0 coauthorships = 0 - all_repos = args[:repositories].first == "all" - repos = all_repos ? SUPPORTED_REPOS : args[:repositories] + all_repos = args.repositories.first == "all" + repos = all_repos ? SUPPORTED_REPOS : args.repositories repos.each do |repo| if SUPPORTED_REPOS.exclude?(repo) return ofail "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." @@ -66,12 +67,12 @@ module Homebrew sentence = "Person #{args.named.first} directly authored #{commits} commits " \ "and co-authored #{coauthorships} commits " \ "across #{all_repos ? "all Homebrew repos" : repos.to_sentence}" - sentence += if args[:from] && args[:to] - " between #{args[:from]} and #{args[:to]}" - elsif args[:from] - " after #{args[:from]}" - elsif args[:to] - " before #{args[:to]}" + sentence += if args.from && args.to + " between #{args.from} and #{args.to}" + elsif args.from + " after #{args.from}" + elsif args.to + " before #{args.to}" else " in all time" end @@ -90,8 +91,8 @@ module Homebrew sig { params(repo_path: Pathname, args: Homebrew::CLI::Args).returns(Integer) } def git_log_author_cmd(repo_path, args) cmd = ["git", "-C", repo_path, "log", "--oneline", "--author=#{args.named.first}"] - cmd << "--before=#{args[:to]}" if args[:to] - cmd << "--after=#{args[:from]}" if args[:from] + cmd << "--before=#{args.to}" if args.to + cmd << "--after=#{args.from}" if args.from Utils.safe_popen_read(*cmd).lines.count end @@ -100,8 +101,8 @@ module Homebrew def git_log_coauthor_cmd(repo_path, args) cmd = ["git", "-C", repo_path, "log", "--oneline"] cmd << "--format='%(trailers:key=Co-authored-by:)'" - cmd << "--before=#{args[:to]}" if args[:to] - cmd << "--after=#{args[:from]}" if args[:from] + cmd << "--before=#{args.to}" if args.to + cmd << "--after=#{args.from}" if args.from Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(args.named.first) } end From ae73f28d0fb6ccba90462fd9e0b5660a2c835d6b Mon Sep 17 00:00:00 2001 From: Issy Long Date: Sun, 31 Jul 2022 21:25:20 +0100 Subject: [PATCH 38/58] dev-cmd/contributions: Use a named arg for required `repositories` - This is apparently "more in-keeping with how we do required arguments (never requiring flags)" across Homebrew. - New usage: `brew contributions me@issyl0.co.uk all`, `brew contributions me@issyl0.co.uk brew,core` --- Library/Homebrew/dev-cmd/contributions.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 0911b3d361..f51e6fdb99 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -17,9 +17,13 @@ module Homebrew sig { returns(CLI::Parser) } def contributions_args Homebrew::CLI::Parser.new do - usage_banner "`contributions` []" + usage_banner "`contributions` " description <<~EOS Contributions to Homebrew repos for a user. + + The second argument is a comma-separated list of repos to search. + Specify to search all repositories. + Supported repositories: #{SUPPORTED_REPOS.join(", ")}. EOS flag "--from=", @@ -28,25 +32,20 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - comma_array "--repositories=", - description: "The Homebrew repositories to search for contributions in. " \ - "Comma separated. Pass `all` to search all repositories. " \ - "Supported repositories: #{SUPPORTED_REPOS.join(", ")}." - - named_args :email, number: 1 + named_args [:email, :repositories], min: 2, max: 2 end end sig { returns(NilClass) } def contributions args = contributions_args.parse - return ofail "Please specify `--repositories` to search, or `--repositories=all`." unless args[:repositories].empty? commits = 0 coauthorships = 0 - all_repos = args.repositories.first == "all" - repos = all_repos ? SUPPORTED_REPOS : args.repositories + all_repos = args.named.last == "all" + repos = all_repos ? SUPPORTED_REPOS : args.named.last.split(",") + repos.each do |repo| if SUPPORTED_REPOS.exclude?(repo) return ofail "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}." From 0c7825accd9ad960cc045e503bb4edab3a955b2a Mon Sep 17 00:00:00 2001 From: Issy Long Date: Mon, 1 Aug 2022 17:38:56 +0100 Subject: [PATCH 39/58] dev-cmd/contributions: Make the first arg either a name or email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This is easier than mapping GitHub usernames to email addresses, when folks don't have email addresses always public on their GitHub profiles. Also, the users of this command (PLC members, other interested parties) don't have to remember folks' email addresses. - It also gives better data for people who've changed their name over the years, and who commit with multiple email addresses (personal and work). ``` ❯ brew contributions "Issy Long" all Person Issy Long directly authored 687 commits and co-authored 33 commits across all Homebrew repos in all time. ❯ brew contributions "Rylan Polster" all Person Rylan Polster directly authored 1747 commits and co-authored 133 commits across all Homebrew repos in all time. ❯ brew contributions me@issyl0.co.uk all Person me@issyl0.co.uk directly authored 711 commits and co-authored 25 commits across all Homebrew repos in all time. ❯ brew contributions "Mike McQuaid" all Person Mike McQuaid directly authored 26879 commits and co-authored 204 commits across all Homebrew repos in all time. ``` --- Library/Homebrew/dev-cmd/contributions.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index f51e6fdb99..38a3888ca9 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -17,10 +17,12 @@ module Homebrew sig { returns(CLI::Parser) } def contributions_args Homebrew::CLI::Parser.new do - usage_banner "`contributions` " + usage_banner "`contributions` " description <<~EOS Contributions to Homebrew repos for a user. + The first argument is a name (e.g. "BrewTestBot") or an email address (e.g. "brewtestbot@brew.sh"). + The second argument is a comma-separated list of repos to search. Specify to search all repositories. Supported repositories: #{SUPPORTED_REPOS.join(", ")}. @@ -32,7 +34,7 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - named_args [:email, :repositories], min: 2, max: 2 + named_args [:email, :name, :repositories], min: 2, max: 2 end end From 5ecdf10e27f872d445be9947147071f13b280881 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 10:17:48 +0100 Subject: [PATCH 40/58] dev-cmd/contributions: Start output with the name/email Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 38a3888ca9..217249c679 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -65,7 +65,7 @@ module Homebrew coauthorships += git_log_coauthor_cmd(T.must(repo_path), args) end - sentence = "Person #{args.named.first} directly authored #{commits} commits " \ + sentence = "#{args.named.first} directly authored #{commits} commits " \ "and co-authored #{coauthorships} commits " \ "across #{all_repos ? "all Homebrew repos" : repos.to_sentence}" sentence += if args.from && args.to From a7effbdd3591ec36316764254423370def951e90 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 10:18:21 +0100 Subject: [PATCH 41/58] dev-cmd/contributions: Remove named arg names, unused Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 217249c679..ead3abd767 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -34,7 +34,7 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - named_args [:email, :name, :repositories], min: 2, max: 2 + named_args min: 2, max: 2 end end From 8c762d96879d598eaeb9e78abefadb2b6619f631 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 10:19:03 +0100 Subject: [PATCH 42/58] dev-cmd/contributions: Improve Sorbet typing Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index ead3abd767..de13dbe777 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -38,7 +38,7 @@ module Homebrew end end - sig { returns(NilClass) } + sig { void } def contributions args = contributions_args.parse From 31331663e4412dffd1c13b2ea920ec291fd71dbb Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 16:48:40 +0100 Subject: [PATCH 43/58] dev-cmd/contributions: Don't have `cask` in SUPPORTED_REPOS twice - Also stop skipping a "versions" repo. Since 023261038192a4f55c95a4d2486873ec1c9a728a the `Homebrew/homebrew-cask-versions` tap won't get mistaken for `Homebrew/homebrew-versions` tap. --- Library/Homebrew/dev-cmd/contributions.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index de13dbe777..f600c3ca30 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -11,7 +11,7 @@ module Homebrew SUPPORTED_REPOS = [ %w[brew core cask], OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") }, - OFFICIAL_CASK_TAPS, + OFFICIAL_CASK_TAPS.reject { |t| t == "cask" }, ].flatten.freeze sig { returns(CLI::Parser) } @@ -55,7 +55,6 @@ module Homebrew repo_path = find_repo_path_for_repo(repo) unless repo_path.exist? - next if repo == "versions" # This tap is deprecated, tapping it will error. opoo "Repository #{repo} not yet tapped! Tapping it now..." Tap.fetch("homebrew", repo).install From 0bb8a061932238596572cb3054ecb70f6f5a1c86 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 17:01:52 +0100 Subject: [PATCH 44/58] dev-cmd/contributions: Revert to `--repositories` flag - This doesn't require "all" to be specified as part of the command, it's the default, so usage is now just: ``` $ brew contributions "Issy Long" $ brew contributions "Issy Long" --repositories=brew,core $ brew contributions me@issyl0.co.uk --repositories=cask,bundle ``` - As we discussed in the PR review before, `comma_array` doesn't allow two names, so we can't (yet) do `comma_array "--repositories", "--repos"` like we can with `flag`. That's an enhancement for the future if we want to make the flags here less verbose. But now that "all" is the default, maybe less necessary. --- Library/Homebrew/dev-cmd/contributions.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index f600c3ca30..b479f75d38 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -17,24 +17,24 @@ module Homebrew sig { returns(CLI::Parser) } def contributions_args Homebrew::CLI::Parser.new do - usage_banner "`contributions` " + usage_banner "`contributions` " description <<~EOS Contributions to Homebrew repos for a user. The first argument is a name (e.g. "BrewTestBot") or an email address (e.g. "brewtestbot@brew.sh"). - - The second argument is a comma-separated list of repos to search. - Specify to search all repositories. - Supported repositories: #{SUPPORTED_REPOS.join(", ")}. EOS + comma_array "--repositories", + description: "Specify a comma-separated (no spaces) list of repositories to search. " \ + "Supported repositories: #{SUPPORTED_REPOS.join(", ")}. " \ + "Omitting this flag, or specifying `--repositories=all`, will search all repositories." flag "--from=", description: "Date (ISO-8601 format) to start searching contributions." flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - named_args min: 2, max: 2 + named_args min: 1, max: 1 end end @@ -45,8 +45,8 @@ module Homebrew commits = 0 coauthorships = 0 - all_repos = args.named.last == "all" - repos = all_repos ? SUPPORTED_REPOS : args.named.last.split(",") + all_repos = args.repositories.nil? || args.repositories.include?("all") + repos = all_repos ? SUPPORTED_REPOS : args.repositories repos.each do |repo| if SUPPORTED_REPOS.exclude?(repo) From 4874602f25a05591360ee8b7b70d9fba492e70f0 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 17:43:24 +0100 Subject: [PATCH 45/58] dev-cmd/contributions: Better usage string & named args numbering Co-authored-by: Rylan Polster --- Library/Homebrew/dev-cmd/contributions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index b479f75d38..2bff07966b 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -17,7 +17,7 @@ module Homebrew sig { returns(CLI::Parser) } def contributions_args Homebrew::CLI::Parser.new do - usage_banner "`contributions` " + usage_banner "`contributions` [<--repositories>`=`]" description <<~EOS Contributions to Homebrew repos for a user. @@ -34,7 +34,7 @@ module Homebrew flag "--to=", description: "Date (ISO-8601 format) to stop searching contributions." - named_args min: 1, max: 1 + named_args number: 1 end end From b5fb015d2604aa3bcb8fc3f4f00dc5b0b72c659c Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 17:52:54 +0100 Subject: [PATCH 46/58] official_taps: Add `cask-fonts`, `cask-drivers` taps - These weren't in `OFFICIAL_CASK_TAPS` but they probably should be given they're active repos in the Homebrew org. --- Library/Homebrew/official_taps.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Homebrew/official_taps.rb b/Library/Homebrew/official_taps.rb index 53fb779b2c..75c0f232db 100644 --- a/Library/Homebrew/official_taps.rb +++ b/Library/Homebrew/official_taps.rb @@ -3,6 +3,8 @@ OFFICIAL_CASK_TAPS = %w[ cask + cask-drivers + cask-fonts cask-versions ].freeze From 11d661c497e157560dc70e6748b63d1353aeaf48 Mon Sep 17 00:00:00 2001 From: Issy Long Date: Wed, 3 Aug 2022 18:26:30 +0100 Subject: [PATCH 47/58] dev-cmd/contributions: Friendlier `--repositories` help text --- Library/Homebrew/dev-cmd/contributions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 2bff07966b..946c40b8f1 100755 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -26,7 +26,7 @@ module Homebrew comma_array "--repositories", description: "Specify a comma-separated (no spaces) list of repositories to search. " \ - "Supported repositories: #{SUPPORTED_REPOS.join(", ")}. " \ + "Supported repositories: #{SUPPORTED_REPOS.map { |t| "`#{t}`" }.to_sentence}." \ "Omitting this flag, or specifying `--repositories=all`, will search all repositories." flag "--from=", description: "Date (ISO-8601 format) to start searching contributions." From 74de60345674187c229d2444cc72c5e36d43b5ca Mon Sep 17 00:00:00 2001 From: BrewTestBot <1589480+BrewTestBot@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:07:13 +0000 Subject: [PATCH 48/58] Update maintainers, manpage and completions. Autogenerated by the [update-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/update-man-completions.yml) workflow. --- completions/bash/brew | 20 ++++++++++++++++++++ completions/fish/brew.fish | 10 ++++++++++ completions/internal_commands_list.txt | 1 + completions/zsh/_brew | 13 +++++++++++++ docs/Manpage.md | 13 +++++++++++++ manpages/brew.1 | 18 ++++++++++++++++++ 6 files changed, 75 insertions(+) diff --git a/completions/bash/brew b/completions/bash/brew index 86abb433c2..7ee107cea1 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -670,6 +670,25 @@ _brew_config() { esac } +_brew_contributions() { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${cur}" in + -*) + __brewcomp " + --debug + --from + --help + --quiet + --repositories + --to + --verbose + " + return + ;; + *) + esac +} + _brew_create() { local cur="${COMP_WORDS[COMP_CWORD]}" case "${cur}" in @@ -2479,6 +2498,7 @@ _brew() { commands) _brew_commands ;; completions) _brew_completions ;; config) _brew_config ;; + contributions) _brew_contributions ;; create) _brew_create ;; deps) _brew_deps ;; desc) _brew_desc ;; diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index d1ba1881af..17e0059810 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -529,6 +529,16 @@ __fish_brew_complete_arg 'config' -l quiet -d 'Make some output more quiet' __fish_brew_complete_arg 'config' -l verbose -d 'Make some output more verbose' +__fish_brew_complete_cmd 'contributions' 'Contributions to Homebrew repos for a user' +__fish_brew_complete_arg 'contributions' -l debug -d 'Display any debugging information' +__fish_brew_complete_arg 'contributions' -l from -d 'Date (ISO-8601 format) to start searching contributions' +__fish_brew_complete_arg 'contributions' -l help -d 'Show this message' +__fish_brew_complete_arg 'contributions' -l quiet -d 'Make some output more quiet' +__fish_brew_complete_arg 'contributions' -l repositories -d 'Specify a comma-separated (no spaces) list of repositories to search. Supported repositories: `brew`, `core`, `cask`, `aliases`, `autoupdate`, `bundle`, `command-not-found`, `test-bot`, `services`, `cask-drivers`, `cask-fonts` and `cask-versions`.Omitting this flag, or specifying `--repositories=all`, will search all repositories' +__fish_brew_complete_arg 'contributions' -l to -d 'Date (ISO-8601 format) to stop searching contributions' +__fish_brew_complete_arg 'contributions' -l verbose -d 'Make some output more verbose' + + __fish_brew_complete_cmd 'create' 'Generate a formula or, with `--cask`, a cask for the downloadable file at URL and open it in the editor' __fish_brew_complete_arg 'create' -l HEAD -d 'Indicate that URL points to the package\'s repository rather than a file' __fish_brew_complete_arg 'create' -l autotools -d 'Create a basic template for an Autotools-style build' diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt index 09af017ba6..408b5635d0 100644 --- a/completions/internal_commands_list.txt +++ b/completions/internal_commands_list.txt @@ -26,6 +26,7 @@ command commands completions config +contributions create deps desc diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 7c33b10012..e8b6c5d327 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -152,6 +152,7 @@ __brew_internal_commands() { 'commands:Show lists of built-in and external commands' 'completions:Control whether Homebrew automatically links external tap shell completion files' 'config:Show Homebrew and system configuration info useful for debugging' + 'contributions:Contributions to Homebrew repos for a user' 'create:Generate a formula or, with `--cask`, a cask for the downloadable file at URL and open it in the editor' 'deps:Show dependencies for formula' 'desc:Display formula'\''s name and one-line description' @@ -652,6 +653,18 @@ _brew_config() { '--verbose[Make some output more verbose]' } +# brew contributions +_brew_contributions() { + _arguments \ + '--debug[Display any debugging information]' \ + '--from[Date (ISO-8601 format) to start searching contributions]' \ + '--help[Show this message]' \ + '--quiet[Make some output more quiet]' \ + '--repositories[Specify a comma-separated (no spaces) list of repositories to search. Supported repositories: `brew`, `core`, `cask`, `aliases`, `autoupdate`, `bundle`, `command-not-found`, `test-bot`, `services`, `cask-drivers`, `cask-fonts` and `cask-versions`.Omitting this flag, or specifying `--repositories=all`, will search all repositories]' \ + '--to[Date (ISO-8601 format) to stop searching contributions]' \ + '--verbose[Make some output more verbose]' +} + # brew create _brew_create() { _arguments \ diff --git a/docs/Manpage.md b/docs/Manpage.md index 57e1e3b4ef..b29bae2c63 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1081,6 +1081,19 @@ Display the source of a *`formula`* or *`cask`*. Display the path to the file being used when invoking `brew` *`cmd`*. +### `contributions` *`email|name`* [*`--repositories`*`=`] + +Contributions to Homebrew repos for a user. + +The first argument is a name (e.g. "BrewTestBot") or an email address (e.g. "brewtestbot@brew.sh"). + +* `--repositories`: + Specify a comma-separated (no spaces) list of repositories to search. Supported repositories: `brew`, `core`, `cask`, `aliases`, `autoupdate`, `bundle`, `command-not-found`, `test-bot`, `services`, `cask-drivers`, `cask-fonts` and `cask-versions`.Omitting this flag, or specifying `--repositories=all`, will search all repositories. +* `--from`: + Date (ISO-8601 format) to start searching contributions. +* `--to`: + Date (ISO-8601 format) to stop searching contributions. + ### `create` [*`options`*] *`URL`* Generate a formula or, with `--cask`, a cask for the downloadable file at *`URL`* diff --git a/manpages/brew.1 b/manpages/brew.1 index 760aa819c6..5d2cfe0259 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1539,6 +1539,24 @@ Treat all named arguments as casks\. .SS "\fBcommand\fR \fIcommand\fR [\.\.\.]" Display the path to the file being used when invoking \fBbrew\fR \fIcmd\fR\. . +.SS "\fBcontributions\fR \fIemail|name\fR [\fI\-\-repositories\fR\fB=\fR]" +Contributions to Homebrew repos for a user\. +. +.P +The first argument is a name (e\.g\. "BrewTestBot") or an email address (e\.g\. "brewtestbot@brew\.sh")\. +. +.TP +\fB\-\-repositories\fR +Specify a comma\-separated (no spaces) list of repositories to search\. Supported repositories: \fBbrew\fR, \fBcore\fR, \fBcask\fR, \fBaliases\fR, \fBautoupdate\fR, \fBbundle\fR, \fBcommand\-not\-found\fR, \fBtest\-bot\fR, \fBservices\fR, \fBcask\-drivers\fR, \fBcask\-fonts\fR and \fBcask\-versions\fR\.Omitting this flag, or specifying \fB\-\-repositories=all\fR, will search all repositories\. +. +.TP +\fB\-\-from\fR +Date (ISO\-8601 format) to start searching contributions\. +. +.TP +\fB\-\-to\fR +Date (ISO\-8601 format) to stop searching contributions\. +. .SS "\fBcreate\fR [\fIoptions\fR] \fIURL\fR" Generate a formula or, with \fB\-\-cask\fR, a cask for the downloadable file at \fIURL\fR and open it in the editor\. Homebrew will attempt to automatically derive the formula name and version, but if it fails, you\'ll have to make your own template\. The \fBwget\fR formula serves as a simple example\. For the complete API, see: \fIhttps://rubydoc\.brew\.sh/Formula\fR . From ef20a53b4c1314cb1644fd2a23d01403beb60fbb Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Wed, 3 Aug 2022 23:28:44 +0100 Subject: [PATCH 49/58] utils/ruby.sh: fix HOMEBREW_USE_RUBY_FROM_PATH on non-latest macOS --- Library/Homebrew/utils/ruby.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/utils/ruby.sh b/Library/Homebrew/utils/ruby.sh index 3293949f53..51e1f9eab0 100644 --- a/Library/Homebrew/utils/ruby.sh +++ b/Library/Homebrew/utils/ruby.sh @@ -47,10 +47,10 @@ need_vendored_ruby() { if [[ -n "${HOMEBREW_FORCE_VENDOR_RUBY}" ]] then return 0 - elif [[ -n "${HOMEBREW_MACOS_SYSTEM_RUBY_NEW_ENOUGH}" ]] + elif [[ -n "${HOMEBREW_MACOS_SYSTEM_RUBY_NEW_ENOUGH}" && -z "${HOMEBREW_USE_RUBY_FROM_PATH}" ]] then return 1 - elif [[ -z "${HOMEBREW_MACOS}" ]] && test_ruby "${HOMEBREW_RUBY_PATH}" + elif [[ -z "${HOMEBREW_MACOS}" || -n "${HOMEBREW_USE_RUBY_FROM_PATH}" ]] && test_ruby "${HOMEBREW_RUBY_PATH}" then return 1 else From 6a93c5d53d67e67e2eb8a52544397261c99ea57d Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Wed, 3 Aug 2022 20:33:49 -0400 Subject: [PATCH 50/58] Add `on_{system}` methods to formula cookbook --- docs/Formula-Cookbook.md | 46 ++++++++++++++++++++++++++++++++++++++++ manpages/brew.1 | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/Formula-Cookbook.md b/docs/Formula-Cookbook.md index 0355953b85..a0b9b11f68 100644 --- a/docs/Formula-Cookbook.md +++ b/docs/Formula-Cookbook.md @@ -544,6 +544,52 @@ Instead of `git diff | pbcopy`, for some editors `git diff >> path/to/your/formu If anything isn’t clear, you can usually figure it out by `grep`ping the `$(brew --repository homebrew/core)` directory. Please submit a pull request to amend this document if you think it will help! +### Handling different system configurations + +Often, formulae need different dependencies, resources, patches, conflicts, deprecations or `keg_only` statuses on different OSes and arches. In these cases, the components can be nested inside `on_macos`, `on_linux`, `on_arm` or `on_intel` blocks. For example, here's how to add `gcc` as a Linux-only dependency: + +```ruby +on_linux do + depends_on "gcc" +end +``` + +Components can also be declared only for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all macOS versions that are newer than Mojave, use: + +```ruby +on_mojave :or_newer do + depends_on "gettext" => :build +end +``` + +Sometimes, a dependency is needed on certain macOS versions and on Linux. In these cases, a special `on_system` method can be used: + +```ruby +on_system :linux, macos: :sierra_or_older do + depends_on "gettext" => :build +end +``` + +To check multiple conditions, nest the corresponding blocks. For example, the following code adds a `gettext` build dependency only on an ARM and macOS: + +```ruby +on_macos do + on_intel do + depends_on "gettext" => :build + end +end +``` + +#### Inside `def install` and `test do` + +Inside `def install` and `test` do, don't use these `on_*` methods. Instead, use `if` statements and the following conditionals: + +* `OS.mac?` and `OS.linux?` return `true` or `false` based on the OS +* `Hardware::CPU.intel?` and `Hardware::CPU.arm?` return `true` or `false` based on the arch +* `MacOS.version` returns the current macOS version. Use `==`, `<=` or `>=` to compare to symbols corresponding to macOS versions (e.g. `if MacOS.version >= :mojave`) + +See [`rust`](https://github.com/Homebrew/homebrew-core/blob/fe831237a7c24033a48f588a1578ba54f953f922/Formula/rust.rb#L72) for an example. + ### `livecheck` blocks When `brew livecheck` is unable to identify versions for a formula, we can control its behavior using a `livecheck` block. Here is a simple example to check a page for links containing a filename like `example-1.2.tar.gz`: diff --git a/manpages/brew.1 b/manpages/brew.1 index 5d2cfe0259..526bde8619 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW" "1" "July 2022" "Homebrew" "brew" +.TH "BREW" "1" "August 2022" "Homebrew" "brew" . .SH "NAME" \fBbrew\fR \- The Missing Package Manager for macOS (or Linux) From 9176d650546211b9dd47e0bb9f5a4b63adf6ac1e Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Wed, 3 Aug 2022 20:38:23 -0400 Subject: [PATCH 51/58] Clarifications --- docs/Formula-Cookbook.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Formula-Cookbook.md b/docs/Formula-Cookbook.md index a0b9b11f68..0e88d3c9b8 100644 --- a/docs/Formula-Cookbook.md +++ b/docs/Formula-Cookbook.md @@ -554,7 +554,7 @@ on_linux do end ``` -Components can also be declared only for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all macOS versions that are newer than Mojave, use: +Components can also be declared for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all macOS versions that are newer than Mojave, use: ```ruby on_mojave :or_newer do @@ -562,7 +562,7 @@ on_mojave :or_newer do end ``` -Sometimes, a dependency is needed on certain macOS versions and on Linux. In these cases, a special `on_system` method can be used: +Sometimes, a dependency is needed on certain macOS versions *and* on Linux. In these cases, a special `on_system` method can be used: ```ruby on_system :linux, macos: :sierra_or_older do @@ -570,7 +570,7 @@ on_system :linux, macos: :sierra_or_older do end ``` -To check multiple conditions, nest the corresponding blocks. For example, the following code adds a `gettext` build dependency only on an ARM and macOS: +To check multiple conditions, nest the corresponding blocks. For example, the following code adds a `gettext` build dependency when on ARM *and* macOS: ```ruby on_macos do From 78bf62ee5d05ca6d1fdaa30f48e2a6728968dce9 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 4 Aug 2022 10:15:00 -0400 Subject: [PATCH 52/58] Apply suggestions from code review Co-authored-by: Adrian Ho --- docs/Formula-Cookbook.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Formula-Cookbook.md b/docs/Formula-Cookbook.md index 0e88d3c9b8..daf02b572e 100644 --- a/docs/Formula-Cookbook.md +++ b/docs/Formula-Cookbook.md @@ -554,7 +554,7 @@ on_linux do end ``` -Components can also be declared for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all macOS versions that are newer than Mojave, use: +Components can also be declared for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all later macOS versions, use: ```ruby on_mojave :or_newer do @@ -574,7 +574,7 @@ To check multiple conditions, nest the corresponding blocks. For example, the fo ```ruby on_macos do - on_intel do + on_arm do depends_on "gettext" => :build end end @@ -582,7 +582,7 @@ end #### Inside `def install` and `test do` -Inside `def install` and `test` do, don't use these `on_*` methods. Instead, use `if` statements and the following conditionals: +Inside `def install` and `test do`, don't use these `on_*` methods. Instead, use `if` statements and the following conditionals: * `OS.mac?` and `OS.linux?` return `true` or `false` based on the OS * `Hardware::CPU.intel?` and `Hardware::CPU.arm?` return `true` or `false` based on the arch From a65f5c2d499909c1a71fae0c0e5a52b52e986ece Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 18:04:45 +0000 Subject: [PATCH 53/58] build(deps): bump rubocop from 1.32.0 to 1.33.0 in /Library/Homebrew Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.32.0 to 1.33.0. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.32.0...v1.33.0) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Library/Homebrew/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/Gemfile.lock b/Library/Homebrew/Gemfile.lock index 6499d238a7..854b6fdcee 100644 --- a/Library/Homebrew/Gemfile.lock +++ b/Library/Homebrew/Gemfile.lock @@ -124,7 +124,7 @@ GEM rspec (>= 3, < 4) rspec_junit_formatter (0.5.1) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.32.0) + rubocop (1.33.0) json (~> 2.3) parallel (~> 1.10) parser (>= 3.1.0.0) From 345b283058cac16956375f05b5a61ac0b2d09c15 Mon Sep 17 00:00:00 2001 From: BrewTestBot <1589480+BrewTestBot@users.noreply.github.com> Date: Thu, 4 Aug 2022 18:15:52 +0000 Subject: [PATCH 54/58] brew vendor-gems: commit updates. --- Library/Homebrew/vendor/bundle/bundler/setup.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/vendor/bundle/bundler/setup.rb b/Library/Homebrew/vendor/bundle/bundler/setup.rb index b0b852f339..355961b21a 100644 --- a/Library/Homebrew/vendor/bundle/bundler/setup.rb +++ b/Library/Homebrew/vendor/bundle/bundler/setup.rb @@ -86,7 +86,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec_junit_formatter $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-ast-1.19.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.11.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.2.0/lib" -$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.32.0/lib" +$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.33.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.14.3/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.15.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.12.1/lib" From 3eacc0fb7c989916b1bbb7946c125bdef3038939 Mon Sep 17 00:00:00 2001 From: Michka Popoff Date: Thu, 4 Aug 2022 23:18:19 +0200 Subject: [PATCH 55/58] pr-pull: fix check conflicts The previous refactorings broke the conflict check. Go back to the initially proposed syntax to fill the hashes/arrays, which is more readable, and which works (the proc syntax does not seem to work for our purpose here) Remove space before the error message, else only the first line of the output has 2 spaces and this looks weird --- Library/Homebrew/dev-cmd/pr-pull.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index fb49a6d4bd..eb9935f14a 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -369,41 +369,43 @@ module Homebrew end def pr_check_conflicts(user, repo, pr) - hash_template = proc { |h, k| h[k] = [] } long_build_pr_files = GitHub.search_issues( "org:#{user}", repo: repo, state: "open", label: "\"no long build conflict\"" - ).each_with_object(Hash.new(hash_template)) do |long_build_pr, hash| + ).each_with_object({}) do |long_build_pr, hash| number = long_build_pr["number"] GitHub.get_pull_request_changed_files("#{user}/#{repo}", number).each do |file| key = file["filename"] + hash[key] ||= [] hash[key] << number end end this_pr_files = GitHub.get_pull_request_changed_files("#{user}/#{repo}", pr) - conflicts = this_pr_files.each_with_object(Hash.new(hash_template)) do |file, hash| + conflicts = this_pr_files.each_with_object({}) do |file, hash| filename = file["filename"] next unless long_build_pr_files.key?(filename) long_build_pr_files[filename].each do |pr_number| key = "#{user}/#{repo}/pull/#{pr_number}" + hash[key] ||= [] hash[key] << filename end end + return if conflicts.blank? # Raise an error, display the conflicting PR. For example: # Error: You are trying to merge a pull request that conflicts with a long running build in: - # { - # "homebrew-core/pull/98809": [ - # "Formula/icu4c.rb", - # "Formula/node@10.rb" - # ] - # } + # { + # "homebrew-core/pull/98809": [ + # "Formula/icu4c.rb", + # "Formula/node@10.rb" + # ] + # } odie <<~EOS You are trying to merge a pull request that conflicts with a long running build in: - #{JSON.pretty_generate(conflicts)} + #{JSON.pretty_generate(conflicts)} EOS end From 61544369e45234e3452eb2c21e67c43736aa08a1 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 5 Aug 2022 21:41:28 +0800 Subject: [PATCH 56/58] pr-pull: fix PRs conflicting with themselves https://github.com/Homebrew/homebrew-core/pull/106755#issuecomment-1206460655 --- Library/Homebrew/dev-cmd/pr-pull.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index eb9935f14a..c62cedbfb3 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -373,6 +373,8 @@ module Homebrew "org:#{user}", repo: repo, state: "open", label: "\"no long build conflict\"" ).each_with_object({}) do |long_build_pr, hash| number = long_build_pr["number"] + next if number == pr + GitHub.get_pull_request_changed_files("#{user}/#{repo}", number).each do |file| key = file["filename"] hash[key] ||= [] From a82974974cc9c16858c1d5bf50e25024d037ed22 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Fri, 5 Aug 2022 21:51:39 +0800 Subject: [PATCH 57/58] Update Library/Homebrew/dev-cmd/pr-pull.rb Co-authored-by: Bo Anderson --- Library/Homebrew/dev-cmd/pr-pull.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index c62cedbfb3..9bac23c98c 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -373,7 +373,7 @@ module Homebrew "org:#{user}", repo: repo, state: "open", label: "\"no long build conflict\"" ).each_with_object({}) do |long_build_pr, hash| number = long_build_pr["number"] - next if number == pr + next if number == pr.to_i GitHub.get_pull_request_changed_files("#{user}/#{repo}", number).each do |file| key = file["filename"] From 2d95b9acda5b56380a3ca603fd2e0a0afbfa91f0 Mon Sep 17 00:00:00 2001 From: Carlo Cabrera <30379873+carlocab@users.noreply.github.com> Date: Sat, 6 Aug 2022 00:26:48 +0800 Subject: [PATCH 58/58] linux/keg_relocate: remove patchelf exemption I don't think this is needed anymore. We probably needed this when we used `patchelf` to do `RPATH` rewriting, but this is no longer the case. --- Library/Homebrew/extend/os/linux/keg_relocate.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/Library/Homebrew/extend/os/linux/keg_relocate.rb b/Library/Homebrew/extend/os/linux/keg_relocate.rb index 922cdcb339..887ff07c80 100644 --- a/Library/Homebrew/extend/os/linux/keg_relocate.rb +++ b/Library/Homebrew/extend/os/linux/keg_relocate.rb @@ -8,9 +8,6 @@ class Keg # Patching the dynamic linker of glibc breaks it. return if name.match? Version.formula_optionally_versioned_regex(:glibc) - # Patching patchelf fails with "Text file busy" or SIGBUS. - return if name == "patchelf" - old_prefix, new_prefix = relocation.replacement_pair_for(:prefix) elf_files.each do |file|