Merge pull request #19786 from botantony/autobump

formula/cask: add `no_autobump` method
This commit is contained in:
Mike McQuaid 2025-04-29 17:02:35 +00:00 committed by GitHub
commit 335933f67c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 192 additions and 22 deletions

View File

@ -17,6 +17,7 @@ FORMULA_COMPONENT_PRECEDENCE_LIST = T.let([
[{ name: :head, type: :method_call }],
[{ name: :stable, type: :block_call }],
[{ name: :livecheck, type: :block_call }],
[{ name: :no_autobump!, type: :method_call }],
[{ name: :bottle, type: :block_call }],
[{ name: :pour_bottle?, type: :block_call }],
[{ name: :head, type: :block_call }],

View File

@ -0,0 +1,7 @@
# typed: strict
# frozen_string_literal: true
# TODO: add more reasons here
NO_AUTOBUMP_REASONS_LIST = T.let({
incompatible_version_format: "incompatible version format",
}.freeze, T::Hash[Symbol, String])

View File

@ -371,6 +371,9 @@ module Cask
"url" => url,
"url_specs" => url_specs,
"version" => version,
"autobump" => autobump?,
"no_autobump_message" => no_autobump_message,
"skip_livecheck" => livecheck.skip?,
"installed" => installed_version,
"installed_time" => install_time&.to_i,
"bundle_version" => bundle_long_version,

View File

@ -1,6 +1,7 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "autobump_constants"
require "locale"
require "lazy_object"
require "livecheck"
@ -102,6 +103,9 @@ module Cask
:livecheck,
:livecheck_defined?,
:livecheckable?, # TODO: remove once `#livecheckable?` is removed
:no_autobump!,
:autobump?,
:no_autobump_message,
:on_system_blocks_exist?,
:on_system_block_min_os,
:depends_on_set_in_block?,
@ -112,7 +116,7 @@ module Cask
include OnSystem::MacOSAndLinux
attr_reader :cask, :token, :artifacts, :deprecation_date, :deprecation_reason,
attr_reader :cask, :token, :no_autobump_message, :artifacts, :deprecation_date, :deprecation_reason,
:deprecation_replacement_cask, :deprecation_replacement_formula,
:disable_date, :disable_reason, :disable_replacement_cask,
:disable_replacement_formula, :on_system_block_min_os
@ -147,6 +151,8 @@ module Cask
@livecheck = T.let(Livecheck.new(cask), Livecheck)
@livecheck_defined = T.let(false, T::Boolean)
@name = T.let([], T::Array[String])
@autobump = T.let(true, T::Boolean)
@no_autobump_defined = T.let(false, T::Boolean)
@on_system_blocks_exist = T.let(false, T::Boolean)
@os = T.let(nil, T.nilable(String))
@on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion))
@ -540,6 +546,37 @@ module Cask
@livecheck_defined == true
end
# Excludes the cask from autobump list.
#
# TODO: limit this method to the official taps only (f.e. raise
# an error if `!tap.official?`)
#
# @api public
sig { params(because: T.any(String, Symbol)).void }
def no_autobump!(because:)
if because.is_a?(Symbol) && !NO_AUTOBUMP_REASONS_LIST.key?(because)
raise ArgumentError, "'because' argument should use valid symbol or a string!"
end
if !@cask.allow_reassignment && @no_autobump_defined
raise CaskInvalidError.new(cask, "'no_autobump_defined' stanza may only appear once.")
end
@no_autobump_defined = true
@no_autobump_message = because
@autobump = false
end
# Is the cask in autobump list?
def autobump?
@autobump == true
end
# Is no_autobump! method defined?
def no_autobump_defined?
@no_autobump_defined == true
end
# Declare that a cask is no longer functional or supported.
#
# NOTE: A warning will be shown when trying to install this cask.

View File

@ -81,8 +81,8 @@ module Homebrew
Whoops, the #{cask.token} cask has its version update
pull requests automatically opened by BrewTestBot every ~3 hours!
We'd still love your contributions, though, so try another one
that's not in the autobump list:
#{Formatter.url("#{cask.tap.remote}/blob/master/.github/autobump.txt")}
that is excluded from autobump list (i.e. it has 'no_autobump!'
method or 'livecheck' block with 'skip'.)
EOS
odie "You have too many PRs open: close or merge some first!" if GitHub.too_many_open_prs?(cask.tap)

View File

@ -117,8 +117,8 @@ module Homebrew
Whoops, the #{formula.name} formula has its version update
pull requests automatically opened by BrewTestBot every ~3 hours!
We'd still love your contributions, though, so try another one
that's not in the autobump list:
#{Formatter.url("#{tap.remote}/blob/master/.github/autobump.txt")}
that is excluded from autobump list (i.e. it has 'no_autobump!'
method or 'livecheck' block with 'skip'.)
EOS
odie "You have too many PRs open: close or merge some first!" if GitHub.too_many_open_prs?(tap)

View File

@ -58,7 +58,7 @@ module Homebrew
conflicts "--cask", "--formula"
conflicts "--tap=", "--installed"
conflicts "--tap=", "--no-auto"
conflicts "--tap=", "--no-autobump"
conflicts "--eval-all", "--installed"
conflicts "--installed", "--auto"
conflicts "--no-pull-requests", "--open-pr"

View File

@ -100,10 +100,7 @@ module Homebrew
tap = formula_or_cask.tap
next false if tap.nil?
autobump_lists[tap] ||= begin
autobump_path = tap.path/".github/autobump.txt"
autobump_path.exist? ? autobump_path.readlines.map(&:strip) : []
end
autobump_lists[tap] ||= tap.autobump
name = formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
next unless autobump_lists[tap].include?(name)

View File

@ -1,6 +1,7 @@
# typed: strict
# frozen_string_literal: true
require "autobump_constants"
require "cache_store"
require "did_you_mean"
require "formula_support"
@ -213,6 +214,13 @@ class Formula
sig { returns(T::Boolean) }
attr_accessor :follow_installed_alias
# Message that explains why the formula was excluded from autobump list.
# Returns `nil` if no message is specified.
#
# @see .no_autobump!
sig { returns(T.nilable(T.any(String, Symbol))) }
attr_reader :no_autobump_message
alias follow_installed_alias? follow_installed_alias
# Whether or not to force the use of a bottle.
@ -242,6 +250,9 @@ class Formula
@head = T.let(nil, T.nilable(SoftwareSpec))
@stable = T.let(nil, T.nilable(SoftwareSpec))
@autobump = T.let(true, T::Boolean)
@no_autobump_message = T.let(nil, T.nilable(T.any(String, Symbol)))
@force_bottle = T.let(force_bottle, T::Boolean)
@tap = T.let(tap, T.nilable(Tap))
@ -474,6 +485,23 @@ class Formula
# @see .livecheckable?
delegate livecheckable?: :"self.class"
# Exclude the formula from autobump list.
# @!method no_autobump!
# @see .no_autobump!
delegate no_autobump!: :"self.class"
# Is the formula in autobump list?
# @!method autobump?
# @see .autobump?
delegate autobump?: :"self.class"
# Is no_autobump! method defined?
# @!method no_autobump_defined?
# @see .no_autobump_defined?
delegate no_autobump_defined?: :"self.class"
delegate no_autobump_message: :"self.class"
# Is a service specification defined for the software?
# @!method service?
# @see .service?
@ -2484,6 +2512,9 @@ class Formula
"urls" => urls_hash,
"revision" => revision,
"version_scheme" => version_scheme,
"autobump" => autobump?,
"no_autobump_message" => no_autobump_message,
"skip_livecheck" => livecheck.skip?,
"bottle" => {},
"pour_bottle_only_if" => self.class.pour_bottle_only_if&.to_s,
"keg_only" => keg_only?,
@ -4182,6 +4213,40 @@ class Formula
@livecheck.instance_eval(&block)
end
# Method that excludes the formula from the autobump list.
#
# TODO: limit this method to the official taps only (f.e. raise
# an error if `!tap.official?`)
#
# @api public
sig { params(because: T.any(String, Symbol)).void }
def no_autobump!(because:)
if because.is_a?(Symbol) && !NO_AUTOBUMP_REASONS_LIST.key?(because)
raise ArgumentError, "'because' argument should use valid symbol or a string!"
end
@no_autobump_defined = T.let(true, T.nilable(T::Boolean))
@no_autobump_message = T.let(because, T.nilable(T.any(String, Symbol)))
@autobump = T.let(false, T.nilable(T::Boolean))
end
# Is the formula in autobump list?
sig { returns(T::Boolean) }
def autobump?
@autobump != false # @autobump may be `nil`
end
# Is no_autobump! method defined?
sig { returns(T::Boolean) }
def no_autobump_defined? = @no_autobump_defined == true
# Message that explains why the formula was excluded from autobump list.
# Returns `nil` if no message is specified.
#
# @see .no_autobump!
sig { returns(T.nilable(T.any(String, Symbol))) }
attr_reader :no_autobump_message
# Service can be used to define services.
# This method evaluates the DSL specified in the service block of the
# {Formula} (if it exists) and sets the instance variables of a Service

View File

@ -291,6 +291,10 @@ module Formulary
end
end
if (because = json_formula["no_autobump_msg"])
no_autobump!(because:)
end
bottles_stable = json_formula["bottle"]["stable"].presence
if bottles_stable

View File

@ -30,6 +30,9 @@ class Cask::Cask
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def auto_updates(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def autobump?(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def bash_completion(*args, &block); end
@ -141,6 +144,9 @@ class Cask::Cask
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def name(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def no_autobump_message(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.nilable(MacOSVersion)) }
def on_system_block_min_os(*args, &block); end

View File

@ -6,6 +6,9 @@
class Formula
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def autobump?(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def allow_network_access!(*args, &block); end

View File

@ -985,13 +985,27 @@ class Tap
# Array with autobump names
sig { returns(T::Array[String]) }
def autobump
@autobump ||= if (autobump_file = path/HOMEBREW_TAP_AUTOBUMP_FILE).file?
autobump_packages = if core_cask_tap?
Homebrew::API::Cask.all_casks
else
Homebrew::API::Formula.all_formulae
end
@autobump ||= autobump_packages.select do |_, p|
p["autobump"] == true && !p["skip_livecheck"] && !(p["deprecated"] || p["disabled"])
end.keys
if @autobump.empty?
@autobump = if (autobump_file = path/HOMEBREW_TAP_AUTOBUMP_FILE).file?
autobump_file.readlines(chomp: true)
else
[]
end
end
@autobump
end
# Whether this {Tap} allows running bump commands on the given {Formula} or {Cask}.
sig { params(formula_or_cask_name: String).returns(T::Boolean) }
def allow_bump?(formula_or_cask_name)

View File

@ -155,6 +155,25 @@ RSpec.describe Cask::DSL, :cask do
end
end
describe "no_autobump! stanze" do
it "returns true if no_autobump! is not set" do
expect(cask.autobump?).to be(true)
end
context "when no_autobump! is set" do
let(:cask) do
Cask::Cask.new("checksum-cask") do
no_autobump! because: "some reason"
end
end
it "returns false" do
expect(cask.autobump?).to be(false)
expect(cask.no_autobump_message).to eq("some reason")
end
end
end
describe "language stanza" do
context "when language is set explicitly" do
subject(:cask) do

View File

@ -16,6 +16,9 @@
"user_agent": ":fake"
},
"version": "1.2.3",
"autobump": true,
"no_autobump_message": null,
"skip_livecheck": false,
"installed": null,
"installed_time": null,
"bundle_version": null,

View File

@ -652,6 +652,7 @@ _brew_bundle() {
__brewcomp "
--all
--cask
--check
--cleanup
--debug
--describe

View File

@ -507,6 +507,7 @@ __fish_brew_complete_sub_cmd 'bundle' 'env'
__fish_brew_complete_sub_cmd 'bundle' 'edit'
__fish_brew_complete_arg 'bundle' -l all -d '`list` all dependencies'
__fish_brew_complete_arg 'bundle' -l cask -d '`list` or `dump` Homebrew cask dependencies'
__fish_brew_complete_arg 'bundle' -l check -d 'Check that all dependencies in the Brewfile are installed before running `exec`, `sh`, or `env`'
__fish_brew_complete_arg 'bundle' -l cleanup -d '`install` performs cleanup operation, same as running `cleanup --force`. This is enabled by default if `$HOMEBREW_BUNDLE_INSTALL_CLEANUP` is set and `--global` is passed'
__fish_brew_complete_arg 'bundle' -l debug -d 'Display any debugging information'
__fish_brew_complete_arg 'bundle' -l describe -d '`dump` adds a description comment above each line, unless the dependency does not have a description. This is enabled by default if `$HOMEBREW_BUNDLE_DUMP_DESCRIBE` is set'

View File

@ -540,14 +540,14 @@ _brew_bump() {
'--full-name[Print formulae/casks with fully-qualified names]' \
'--help[Show this message]' \
'(--tap --eval-all --auto)--installed[Check formulae and casks that are currently installed]' \
'--no-autobump[Ignore formulae/casks in autobump list (official repositories only)]' \
'(--tap)--no-autobump[Ignore formulae/casks in autobump list (official repositories only)]' \
'--no-fork[Don'\''t try to fork the repository]' \
'(--open-pr)--no-pull-requests[Do not retrieve pull requests from GitHub]' \
'(--no-pull-requests)--open-pr[Open a pull request for the new version if none have been opened yet]' \
'--quiet[Make some output more quiet]' \
'--repology[Use Repology to check for outdated packages]' \
'--start-with[Letter or word that the list of package results should alphabetically follow]' \
'(--installed --no-auto)--tap[Check formulae and casks within the given tap, specified as user`/`repo]' \
'(--installed --no-autobump)--tap[Check formulae and casks within the given tap, specified as user`/`repo]' \
'--verbose[Make some output more verbose]' \
- formula \
'(--cask)--formula[Check only formulae]' \
@ -650,6 +650,7 @@ _brew_bundle() {
_arguments \
'(--no-vscode)--all[`list` all dependencies]' \
'--cask[`list` or `dump` Homebrew cask dependencies]' \
'--check[Check that all dependencies in the Brewfile are installed before running `exec`, `sh`, or `env`]' \
'--cleanup[`install` performs cleanup operation, same as running `cleanup --force`. This is enabled by default if `$HOMEBREW_BUNDLE_INSTALL_CLEANUP` is set and `--global` is passed]' \
'--debug[Display any debugging information]' \
'--describe[`dump` adds a description comment above each line, unless the dependency does not have a description. This is enabled by default if `$HOMEBREW_BUNDLE_DUMP_DESCRIBE` is set]' \

View File

@ -199,7 +199,7 @@ By default, only Homebrew formula dependencies are listed.
of the corresponding type. Passing `--formula` also removes matches against
formula aliases and old formula names.
`brew bundle exec` *`command`*
`brew bundle exec` \[--check\] *`command`*
: Run an external command in an isolated build environment based on the
`Brewfile` dependencies.
@ -210,11 +210,11 @@ commands like `bundle install`, `npm install`, etc. It will also add compiler
flags which will help with finding keg-only dependencies like `openssl`,
`icu4c`, etc.
`brew bundle sh`
`brew bundle sh` \[--check\]
: Run your shell in a `brew bundle exec` environment.
`brew bundle env`
`brew bundle env` \[--check\]
: Print the environment variables that would be set in a `brew bundle exec`
environment.
@ -319,6 +319,11 @@ flags which will help with finding keg-only dependencies like `openssl`,
: `cleanup` casks using the `zap` command instead of `uninstall`.
`--check`
: Check that all dependencies in the Brewfile are installed before running
`exec`, `sh`, or `env`.
### `casks`
List all locally installable casks including short names.

View File

@ -124,15 +124,15 @@ Add entries to your \fBBrewfile\fP\&\. Adds formulae by default\. Use \fB\-\-cas
\fBbrew bundle remove\fP \fIname\fP [\.\.\.]
Remove entries that match \fBname\fP from your \fBBrewfile\fP\&\. Use \fB\-\-formula\fP, \fB\-\-cask\fP, \fB\-\-tap\fP, \fB\-\-mas\fP, \fB\-\-whalebrew\fP or \fB\-\-vscode\fP to remove only entries of the corresponding type\. Passing \fB\-\-formula\fP also removes matches against formula aliases and old formula names\.
.TP
\fBbrew bundle exec\fP \fIcommand\fP
\fBbrew bundle exec\fP [\-\-check] \fIcommand\fP
Run an external command in an isolated build environment based on the \fBBrewfile\fP dependencies\.
.P
This sanitized build environment ignores unrequested dependencies, which makes sure that things you didn\[u2019]t specify in your \fBBrewfile\fP won\[u2019]t get picked up by commands like \fBbundle install\fP, \fBnpm install\fP, etc\. It will also add compiler flags which will help with finding keg\-only dependencies like \fBopenssl\fP, \fBicu4c\fP, etc\.
.TP
\fBbrew bundle sh\fP
\fBbrew bundle sh\fP [\-\-check]
Run your shell in a \fBbrew bundle exec\fP environment\.
.TP
\fBbrew bundle env\fP
\fBbrew bundle env\fP [\-\-check]
Print the environment variables that would be set in a \fBbrew bundle exec\fP environment\.
.TP
\fB\-\-file\fP
@ -197,6 +197,9 @@ Temporarily start services while running the \fBexec\fP or \fBsh\fP command\. Th
.TP
\fB\-\-zap\fP
\fBcleanup\fP casks using the \fBzap\fP command instead of \fBuninstall\fP\&\.
.TP
\fB\-\-check\fP
Check that all dependencies in the Brewfile are installed before running \fBexec\fP, \fBsh\fP, or \fBenv\fP\&\.
.SS "\fBcasks\fP"
List all locally installable casks including short names\.
.SS "\fBcleanup\fP \fR[\fIoptions\fP] \fR[\fIformula\fP|\fIcask\fP \.\.\.]"