Load formulae from the internal API with HOMEBREW_USE_INTERNAL_API

This commit is contained in:
Rylan Polster 2025-06-04 22:30:47 -04:00
parent 0b67caf718
commit 8ec57eab2b
No known key found for this signature in database
7 changed files with 122 additions and 2 deletions

View File

@ -4,6 +4,7 @@
require "api/analytics" require "api/analytics"
require "api/cask" require "api/cask"
require "api/formula" require "api/formula"
require "api/internal"
require "base64" require "base64"
module Homebrew module Homebrew

View File

@ -0,0 +1,72 @@
# typed: strict
# frozen_string_literal: true
require "extend/cachable"
require "api/download"
require "formula_stub"
module Homebrew
module API
# Helper functions for using the JSON internal API.
module Internal
extend Cachable
private_class_method :cache
sig { returns(String) }
def self.endpoint
"internal/formula.#{SimulateSystem.current_tag}.jws.json"
end
sig { params(name: String).returns(T::Hash[String, T.untyped]) }
def self.formula(name)
tag = Utils::Bottles.tag
formula_stub = Homebrew::FormulaStub.from_array formula_stub(name)
bottle_specification = BottleSpecification.new
bottle_specification.tap = Homebrew::DEFAULT_REPOSITORY
bottle_specification.rebuild formula_stub.rebuild
bottle_specification.sha256 tag.to_sym => formula_stub.sha256
bottle = Bottle.new(formula_stub, bottle_specification, tag)
bottle_manifest_resource = T.must(bottle.github_packages_manifest_resource)
begin
bottle_manifest_resource.fetch
bottle_manifest_resource.formula_json
rescue Resource::BottleManifest::Error
opoo "Falling back to API fetch for #{name}"
Homebrew::API.fetch "formula/#{name}.json"
end
end
sig { returns(Pathname) }
def self.cached_json_file_path
HOMEBREW_CACHE_API/endpoint
end
sig { returns(T::Boolean) }
def self.download_and_cache_data!
json_formula_stubs, updated = Homebrew::API.fetch_json_api_file endpoint
cache["formula_stubs"] = {}
cache["all_formula_stubs"] = json_formula_stubs
updated
end
private_class_method :download_and_cache_data!
sig { params(name: String).returns([String, String, Integer, T.nilable(String)]) }
def self.formula_stub(name)
download_and_cache_data! unless cache.key?("all_formula_stubs")
return cache["formula_stubs"][name] if cache["formula_stubs"].key?(name)
cache["all_formula_stubs"].find do |stub|
next false if stub["name"] != name
cache["formula_stubs"][name] = stub
true
end
end
end
end
end

View File

@ -7,7 +7,11 @@ class Bottle
class Filename class Filename
attr_reader :name, :version, :tag, :rebuild attr_reader :name, :version, :tag, :rebuild
sig { params(formula: Formula, tag: Utils::Bottles::Tag, rebuild: Integer).returns(T.attached_class) } sig {
params(formula: T.any(Formula, Homebrew::FormulaStub),
tag: Utils::Bottles::Tag,
rebuild: Integer).returns(T.attached_class)
}
def self.create(formula, tag, rebuild) def self.create(formula, tag, rebuild)
new(formula.name, formula.pkg_version, tag, rebuild) new(formula.name, formula.pkg_version, tag, rebuild)
end end

View File

@ -0,0 +1,24 @@
# typed: strict
# frozen_string_literal: true
require "pkg_version"
module Homebrew
# A stub for a formula, with only the information needed to fetch the bottle manifest.
class FormulaStub < T::Struct
const :name, String
const :pkg_version, PkgVersion
const :rebuild, Integer
const :sha256, T.nilable(String)
sig { params(array: [String, String, Integer, T.nilable(String)]).returns(FormulaStub) }
def self.from_array(array)
new(
name: array[0],
pkg_version: PkgVersion.parse(array[1]),
rebuild: array[2],
sha256: array[3],
)
end
end
end

View File

@ -165,7 +165,11 @@ module Formulary
mod.const_set(:BUILD_FLAGS, flags) mod.const_set(:BUILD_FLAGS, flags)
class_name = class_s(name) class_name = class_s(name)
json_formula = Homebrew::API::Formula.all_formulae[name] json_formula = if ENV.fetch("HOMEBREW_USE_INTERNAL_API", false).present?
Homebrew::API::Internal.formula(name)
else
Homebrew::API::Formula.all_formulae[name]
end
raise FormulaUnavailableError, name if json_formula.nil? raise FormulaUnavailableError, name if json_formula.nil?
json_formula = Homebrew::API.merge_variations(json_formula) json_formula = Homebrew::API.merge_variations(json_formula)

View File

@ -312,6 +312,9 @@ class GitHubPackages
SPDX.license_expression_to_string(:cannot_represent) SPDX.license_expression_to_string(:cannot_represent)
end end
formula_path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"]
formula = Formulary.factory(formula_path)
formula_annotations_hash = { formula_annotations_hash = {
"com.github.package.type" => GITHUB_PACKAGE_TYPE, "com.github.package.type" => GITHUB_PACKAGE_TYPE,
"org.opencontainers.image.created" => created_date, "org.opencontainers.image.created" => created_date,
@ -325,6 +328,7 @@ class GitHubPackages
"org.opencontainers.image.url" => bottle_hash["formula"]["homepage"], "org.opencontainers.image.url" => bottle_hash["formula"]["homepage"],
"org.opencontainers.image.vendor" => org, "org.opencontainers.image.vendor" => org,
"org.opencontainers.image.version" => version, "org.opencontainers.image.version" => version,
"sh.brew.formula.json" => formula.to_hash_with_variations,
}.compact_blank }.compact_blank
manifests = [] manifests = []
end end

View File

@ -329,6 +329,17 @@ class Resource
end end
end end
def formula_json
formula_json = manifest_annotations["sh.brew.formula.json"]
raise Error, "Couldn't find formula JSON from manifest." if formula_json.blank?
begin
JSON.parse(formula_json)
rescue JSON::ParserError
raise Error, "Couldn't parse formula JSON."
end
end
sig { returns(T.nilable(Integer)) } sig { returns(T.nilable(Integer)) }
def bottle_size def bottle_size
manifest_annotations["sh.brew.bottle.size"]&.to_i manifest_annotations["sh.brew.bottle.size"]&.to_i