Add Formula#loaded_from_internal_api?

This commit is contained in:
Rylan Polster
2026-03-26 18:32:53 -07:00
committed by Patrick Linnane
parent 62b53c9a63
commit 0a8c251513
9 changed files with 108 additions and 43 deletions
+15
View File
@@ -447,6 +447,7 @@ class Formula
# The path that was specified to find this formula.
sig { returns(T.nilable(Pathname)) }
def specified_path
return Homebrew::API::Internal.cached_formula_json_file_path if loaded_from_internal_api?
return Homebrew::API::Formula.cached_json_file_path if loaded_from_api?
return alias_path if alias_path&.exist?
@@ -591,6 +592,11 @@ class Formula
# @see .loaded_from_api?
delegate loaded_from_api?: :"self.class"
# Whether this formula was loaded using the internal formulae.brew.sh API.
# @!method loaded_from_internal_api?
# @see .loaded_from_internal_api?
delegate loaded_from_internal_api?: :"self.class"
# The API source data used to load this formula.
# Returns `nil` if the formula was not loaded from the API.
# @!method api_source
@@ -2890,6 +2896,10 @@ class Formula
sig { returns(T::Hash[String, T.untyped]) }
def to_hash_with_variations
if loaded_from_internal_api?
raise UsageError, "Cannot call #to_hash_with_variations on formulae loaded from the internal API"
end
hash = to_hash
# Take from API, merging in local install status.
@@ -3640,6 +3650,7 @@ class Formula
@skip_clean_paths = T.let(Set.new, T.nilable(T::Set[T.any(String, Symbol)]))
@link_overwrite_paths = T.let(Set.new, T.nilable(T::Set[String]))
@loaded_from_api = T.let(false, T.nilable(T::Boolean))
@loaded_from_internal_api = T.let(false, T.nilable(T::Boolean))
@api_source = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
@on_system_blocks_exist = T.let(false, T.nilable(T::Boolean))
@network_access_allowed = T.let(SUPPORTED_NETWORK_ACCESS_PHASES.to_h do |phase|
@@ -3668,6 +3679,10 @@ class Formula
sig { returns(T::Boolean) }
def loaded_from_api? = !!@loaded_from_api
# Whether this formula was loaded using the internal formulae.brew.sh API.
sig { returns(T::Boolean) }
def loaded_from_internal_api? = !!@loaded_from_internal_api
# Whether this formula was loaded using the formulae.brew.sh API.
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
attr_reader :api_source
+1
View File
@@ -1526,6 +1526,7 @@ on_request: installed_on_request?, options:)
tab.built_as_bottle = true
tab.poured_from_bottle = true
tab.loaded_from_api = formula.loaded_from_api?
tab.loaded_from_internal_api = formula.loaded_from_internal_api?
tab.installed_as_dependency = installed_as_dependency?
tab.installed_on_request = installed_on_request?
tab.time = Time.now.to_i
+5 -2
View File
@@ -208,9 +208,10 @@ module Formulary
api_source: T::Hash[String, T.untyped],
tap_git_head: String,
flags: T::Array[String],
internal_api: T::Boolean,
).returns(T.class_of(Formula))
}
def self.load_formula_from_struct!(name, formula_struct, api_source:, tap_git_head:, flags:)
def self.load_formula_from_struct!(name, formula_struct, api_source:, tap_git_head:, flags:, internal_api: false)
namespace = :"FormulaNamespaceAPI#{namespace_key(api_source.to_json)}"
mod = Module.new
@@ -224,6 +225,7 @@ module Formulary
klass = Class.new(::Formula) do
@loaded_from_api = T.let(true, T.nilable(T::Boolean))
@loaded_from_internal_api = T.let(internal_api, T.nilable(T::Boolean))
@api_source = T.let(api_source, T.nilable(T::Hash[String, T.untyped]))
desc formula_struct.desc
@@ -905,7 +907,8 @@ module Formulary
raise FormulaUnavailableError, name if api_source.nil?
Formulary.load_formula_from_struct!(name, formula_struct, api_source:, tap_git_head:, flags:)
Formulary.load_formula_from_struct!(name, formula_struct, api_source:, tap_git_head:, flags:,
internal_api: true)
end
end
+3
View File
@@ -117,6 +117,9 @@ class Formula
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
def loaded_from_api?(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
def loaded_from_internal_api?(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
def network_access_allowed?(*args, &block); end
+46 -37
View File
@@ -31,7 +31,7 @@ class AbstractTab
sig { returns(T.nilable(String)) }
attr_accessor :homebrew_version
attr_accessor :tabfile, :loaded_from_api, :time, :arch, :source, :built_on
attr_accessor :tabfile, :loaded_from_api, :loaded_from_internal_api, :time, :arch, :source, :built_on
# Returns the formula or cask runtime dependencies.
#
@@ -48,6 +48,7 @@ class AbstractTab
@homebrew_version = T.let(nil, T.nilable(String))
@tabfile = T.let(nil, T.nilable(Pathname))
@loaded_from_api = T.let(nil, T.nilable(T::Boolean))
@loaded_from_internal_api = T.let(nil, T.nilable(T::Boolean))
@time = T.let(nil, T.nilable(Integer))
@arch = T.let(nil, T.nilable(String))
@source = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
@@ -74,17 +75,18 @@ class AbstractTab
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T.attached_class) }
def self.create(formula_or_cask)
attributes = {
"homebrew_version" => HOMEBREW_VERSION,
"installed_as_dependency" => false,
"installed_on_request" => false,
"loaded_from_api" => formula_or_cask.loaded_from_api?,
"time" => Time.now.to_i,
"arch" => Hardware::CPU.arch,
"source" => {
"homebrew_version" => HOMEBREW_VERSION,
"installed_as_dependency" => false,
"installed_on_request" => false,
"loaded_from_api" => formula_or_cask.loaded_from_api?,
"loaded_from_internal_api" => formula_or_cask.loaded_from_internal_api?,
"time" => Time.now.to_i,
"arch" => Hardware::CPU.arch,
"source" => {
"tap" => formula_or_cask.tap&.name,
"tap_git_head" => formula_or_cask.tap_git_head,
},
"built_on" => DevelopmentTools.build_system_info,
"built_on" => DevelopmentTools.build_system_info,
}
new(attributes)
@@ -119,19 +121,20 @@ class AbstractTab
sig { returns(T.attached_class) }
def self.empty
attributes = {
"homebrew_version" => HOMEBREW_VERSION,
"installed_as_dependency" => false,
"installed_on_request" => false,
"loaded_from_api" => false,
"time" => nil,
"runtime_dependencies" => nil,
"arch" => nil,
"source" => {
"homebrew_version" => HOMEBREW_VERSION,
"installed_as_dependency" => false,
"installed_on_request" => false,
"loaded_from_api" => false,
"loaded_from_internal_api" => false,
"time" => nil,
"runtime_dependencies" => nil,
"arch" => nil,
"source" => {
"path" => nil,
"tap" => nil,
"tap_git_head" => nil,
},
"built_on" => DevelopmentTools.build_system_info,
"built_on" => DevelopmentTools.build_system_info,
}
new(attributes)
@@ -493,24 +496,25 @@ class Tab < AbstractTab # rubocop:todo Style/OneClassPerFile
sig { params(options: T.nilable(T::Hash[String, T.untyped])).returns(String) }
def to_json(options = nil)
attributes = {
"homebrew_version" => homebrew_version,
"used_options" => used_options.as_flags,
"unused_options" => unused_options.as_flags,
"built_as_bottle" => built_as_bottle,
"poured_from_bottle" => poured_from_bottle,
"loaded_from_api" => loaded_from_api,
"installed_as_dependency" => installed_as_dependency,
"installed_on_request" => installed_on_request,
"changed_files" => changed_files&.map(&:to_s),
"time" => time,
"source_modified_time" => source_modified_time.to_i,
"stdlib" => stdlib&.to_s,
"compiler" => compiler.to_s,
"aliases" => aliases,
"runtime_dependencies" => runtime_dependencies,
"source" => source,
"arch" => arch,
"built_on" => built_on,
"homebrew_version" => homebrew_version,
"used_options" => used_options.as_flags,
"unused_options" => unused_options.as_flags,
"built_as_bottle" => built_as_bottle,
"poured_from_bottle" => poured_from_bottle,
"loaded_from_api" => loaded_from_api,
"loaded_from_internal_api" => loaded_from_internal_api,
"installed_as_dependency" => installed_as_dependency,
"installed_on_request" => installed_on_request,
"changed_files" => changed_files&.map(&:to_s),
"time" => time,
"source_modified_time" => source_modified_time.to_i,
"stdlib" => stdlib&.to_s,
"compiler" => compiler.to_s,
"aliases" => aliases,
"runtime_dependencies" => runtime_dependencies,
"source" => source,
"arch" => arch,
"built_on" => built_on,
}
attributes.delete("stdlib") if attributes["stdlib"].blank?
@@ -552,7 +556,12 @@ class Tab < AbstractTab # rubocop:todo Style/OneClassPerFile
"Built from source"
end
s << "using the formulae.brew.sh API" if loaded_from_api
if loaded_from_internal_api
s << "using the internal formulae.brew.sh API"
elsif loaded_from_api
s << "using the formulae.brew.sh API"
end
s << Time.at(time).strftime("on %Y-%m-%d at %H:%M:%S") if time
unless used_options.empty?
+10
View File
@@ -2347,6 +2347,16 @@ RSpec.describe Formula do
expect(f.specified_path).to eq(Homebrew::API::Formula.cached_json_file_path)
end
end
context "when loaded from the internal API" do
before do
allow(f).to receive(:loaded_from_internal_api?).and_return(true)
end
it "returns the internal API path" do
expect(f.specified_path).to eq(Homebrew::API::Internal.cached_formula_json_file_path)
end
end
end
describe "#preserve_rpath" do
+1
View File
@@ -529,6 +529,7 @@ RSpec.describe Formulary do
formula = described_class.factory(formula_name)
expect(formula).to be_a(Formula)
expect(formula.loaded_from_api?).to be true
expect(formula.loaded_from_internal_api?).to be false
expected_hash = formula_json_contents[formula_name]
expect(formula.to_hash_with_variations).to eq(expected_hash)
@@ -11,6 +11,7 @@
"built_as_bottle": false,
"poured_from_bottle": true,
"loaded_from_api": false,
"loaded_from_internal_api": false,
"installed_as_dependency": false,
"installed_on_request": true,
"changed_files": [
+26 -4
View File
@@ -36,6 +36,12 @@ RSpec.describe Tab do
end
end
matcher :be_loaded_from_internal_api do
match do |actual|
actual.loaded_from_internal_api == true
end
end
subject(:tab) do
described_class.new(
"homebrew_version" => HOMEBREW_VERSION,
@@ -88,6 +94,7 @@ RSpec.describe Tab do
expect(tab).not_to be_installed_as_dependency
expect(tab).not_to be_installed_on_request
expect(tab).not_to be_loaded_from_api
expect(tab).not_to be_loaded_from_internal_api
expect(tab).to be_stable
expect(tab).not_to be_head
expect(tab.tap).to be_nil
@@ -257,6 +264,7 @@ RSpec.describe Tab do
expect(tab).not_to be_installed_as_dependency
expect(tab).to be_installed_on_request
expect(tab).not_to be_loaded_from_api
expect(tab).not_to be_loaded_from_internal_api
end
describe "::from_file" do
@@ -275,6 +283,7 @@ RSpec.describe Tab do
expect(tab).not_to be_installed_as_dependency
expect(tab).to be_installed_on_request
expect(tab).not_to be_loaded_from_api
expect(tab).not_to be_loaded_from_internal_api
expect(tab).to be_stable
expect(tab).not_to be_head
expect(tab.tap.name).to eq("homebrew/core")
@@ -305,6 +314,7 @@ RSpec.describe Tab do
expect(tab).not_to be_installed_as_dependency
expect(tab).to be_installed_on_request
expect(tab).not_to be_loaded_from_api
expect(tab).not_to be_loaded_from_internal_api
expect(tab).to be_stable
expect(tab).not_to be_head
expect(tab.tap.name).to eq("homebrew/core")
@@ -329,6 +339,7 @@ RSpec.describe Tab do
expect(tab).not_to be_installed_as_dependency
expect(tab).not_to be_installed_on_request
expect(tab).not_to be_loaded_from_api
expect(tab).not_to be_loaded_from_internal_api
expect(tab).to be_stable
expect(tab).not_to be_head
expect(tab.tap.name).to eq("homebrew/core")
@@ -510,10 +521,11 @@ RSpec.describe Tab do
it "returns install information for the Tab" do
tab = described_class.new(
poured_from_bottle: true,
loaded_from_api: true,
time: 1_720_189_863,
used_options: %w[--with-foo --without-bar],
poured_from_bottle: true,
loaded_from_api: true,
loaded_from_internal_api: false,
time: 1_720_189_863,
used_options: %w[--with-foo --without-bar],
)
output = "Poured from bottle using the formulae.brew.sh API on #{time_string} " \
"with: --with-foo --without-bar"
@@ -535,11 +547,21 @@ RSpec.describe Tab do
expect(tab.to_s).to include("using the formulae.brew.sh API")
end
it "includes 'using the internal formulae.brew.sh API' if the formula was installed from the internal API" do
tab = described_class.new(loaded_from_api: true, loaded_from_internal_api: true)
expect(tab.to_s).to include("using the internal formulae.brew.sh API")
end
it "does not include 'using the formulae.brew.sh API' if the formula was not installed from the API" do
tab = described_class.new(loaded_from_api: false)
expect(tab.to_s).not_to include("using the formulae.brew.sh API")
end
it "doesn't include 'using the internal formulae.brew.sh API' if the formula wasn't installed via internal API" do
tab = described_class.new(loaded_from_api: true, loaded_from_internal_api: false)
expect(tab.to_s).not_to include("using the internal formulae.brew.sh API")
end
it "includes the time value if specified" do
tab = described_class.new(time: 1_720_189_863)
expect(tab.to_s).to include("on #{time_string}")