Merge pull request #21176 from Homebrew/use-formula-struct

Use `FormulaStruct` when loading formulae from the API
This commit is contained in:
Rylan Polster
2025-12-10 16:10:39 +00:00
committed by GitHub
10 changed files with 492 additions and 199 deletions
+1
View File
@@ -5,6 +5,7 @@ require "api/analytics"
require "api/cask"
require "api/formula"
require "api/internal"
require "api/formula_struct"
require "base64"
require "utils/output"
+143
View File
@@ -5,6 +5,7 @@ require "cachable"
require "api"
require "api/source_download"
require "download_queue"
require "autobump_constants"
module Homebrew
module API
@@ -157,6 +158,148 @@ module Homebrew
Homebrew::API.write_names_file!(all_formulae.keys, "formula", regenerate:)
Homebrew::API.write_aliases_file!(all_aliases, "formula", regenerate:)
end
sig { params(hash: T::Hash[String, T.untyped]).returns(FormulaStruct) }
def self.generate_formula_struct_hash(hash)
hash = Homebrew::API.merge_variations(hash).dup
if (caveats = hash["caveats"])
hash["caveats"] = Formulary.replace_placeholders(caveats)
end
hash["bottle_checksums"] = begin
files = hash.dig("bottle", "stable", "files") || {}
files.map do |tag, bottle_spec|
{
cellar: Formulary.convert_to_string_or_symbol(bottle_spec.fetch("cellar")),
tag.to_sym => bottle_spec.fetch("sha256"),
}
end
end
hash["bottle_rebuild"] = hash.dig("bottle", "stable", "rebuild")
conflicts_with = hash["conflicts_with"] || []
conflicts_with_reasons = hash["conflicts_with_reasons"] || []
hash["conflicts"] = conflicts_with.zip(conflicts_with_reasons).map do |name, reason|
if reason.present?
[name, { because: reason }]
else
[name, {}]
end
end
if (deprecation_date = hash["deprecation_date"])
hash["deprecate_args"] = {
date: deprecation_date,
because: DeprecateDisable.to_reason_string_or_symbol(hash["deprecation_reason"],
type: :formula),
replacement_formula: hash["deprecation_replacement_formula"],
replacement_cask: hash["deprecation_replacement_cask"],
}
end
if (disable_date = hash["disable_date"])
hash["disable_args"] = {
date: disable_date,
because: DeprecateDisable.to_reason_string_or_symbol(hash["disable_reason"], type: :formula),
replacement_formula: hash["disable_replacement_formula"],
replacement_cask: hash["disable_replacement_cask"],
}
end
hash["head_dependency_hash"] = hash["head_dependencies"]
hash["head_url_args"] = begin
url = hash.dig("urls", "head", "url")
specs = {
branch: hash.dig("urls", "head", "branch"),
using: hash.dig("urls", "head", "using")&.to_sym,
}.compact_blank
[url, specs]
end
if (keg_only_hash = hash["keg_only_reason"])
reason = Formulary.convert_to_string_or_symbol(keg_only_hash.fetch("reason"))
explanation = keg_only_hash["explanation"]
hash["keg_only_args"] = [reason, explanation].compact
end
hash["license"] = SPDX.string_to_license_expression(hash["license"])
hash["link_overwrite_paths"] = hash["link_overwrite"]
if (reason = hash["no_autobump_message"])
reason = reason.to_sym if NO_AUTOBUMP_REASONS_LIST.key?(reason.to_sym)
hash["no_autobump_args"] = { because: reason }
end
if (condition = hash["pour_bottle_only_if"])
hash["pour_bottle_args"] = { only_if: condition.to_sym }
end
hash["requirements_array"] = hash["requirements"]
hash["ruby_source_checksum"] = hash.dig("ruby_source_checksum", "sha256")
if (service_hash = hash["service"])
service_hash = Homebrew::Service.from_hash(service_hash)
hash["service_run_args"], hash["service_run_kwargs"] = case (run = service_hash[:run])
when Hash
[[], run]
when Array, String
[[run], {}]
else
[[], {}]
end
hash["service_name_args"] = service_hash[:name]
hash["service_args"] = service_hash.filter_map do |key, arg|
[key.to_sym, arg] if key != :name && key != :run
end
end
hash["stable_checksum"] = hash.dig("urls", "stable", "checksum")
hash["stable_dependency_hash"] = {
"dependencies" => hash["dependencies"] || [],
"build_dependencies" => hash["build_dependencies"] || [],
"test_dependencies" => hash["test_dependencies"] || [],
"recommended_dependencies" => hash["recommended_dependencies"] || [],
"optional_dependencies" => hash["optional_dependencies"] || [],
"uses_from_macos" => hash["uses_from_macos"] || [],
"uses_from_macos_bounds" => hash["uses_from_macos_bounds"] || [],
}
hash["stable_url_args"] = begin
url = hash.dig("urls", "stable", "url")
specs = {
tag: hash.dig("urls", "stable", "tag"),
revision: hash.dig("urls", "stable", "revision"),
using: hash.dig("urls", "stable", "using")&.to_sym,
}.compact_blank
[url, specs]
end
hash["stable_version"] = hash.dig("versions", "stable")
# Should match FormulaStruct::PREDICATES
hash["bottle_present"] = hash["bottle"].present?
hash["deprecated_present"] = hash["deprecation_date"].present?
hash["disabled_present"] = hash["disable_date"].present?
hash["head_present"] = hash.dig("urls", "head").present?
hash["keg_only_present"] = hash["keg_only_reason"].present?
hash["no_autobump_message_present"] = hash["no_autobump_message"].present?
hash["pour_bottle_present"] = hash["pour_bottle_only_if"].present?
hash["service_present"] = hash["service"].present?
hash["service_run_present"] = hash.dig("service", "run").present?
hash["service_name_present"] = hash.dig("service", "name").present?
hash["stable_present"] = hash.dig("urls", "stable").present?
FormulaStruct.from_hash(hash)
end
end
end
end
+202
View File
@@ -0,0 +1,202 @@
# typed: strict
# frozen_string_literal: true
require "service"
require "utils/spdx"
module Homebrew
module API
class FormulaStruct < T::Struct
PREDICATES = [
:bottle,
:deprecated,
:disabled,
:head,
:keg_only,
:no_autobump_message,
:pour_bottle,
:service,
:service_run,
:service_name,
:stable,
].freeze
# `:codesign` and custom requirement classes are not supported.
API_SUPPORTED_REQUIREMENTS = [:arch, :linux, :macos, :maximum_macos, :xcode].freeze
private_constant :API_SUPPORTED_REQUIREMENTS
DependencyArgs = T.type_alias do
T.any(
# Formula name: "foo"
String,
# Formula name and dependency type: { "foo" => :build }
T::Hash[String, Symbol],
)
end
RequirementArgs = T.type_alias do
T.any(
# Requirement name: :macos
Symbol,
# Requirement name and other info: { macos: :build }
T::Hash[Symbol, T::Array[T.anything]],
)
end
UsesFromMacOSArgs = T.type_alias do
[
T.any(
# Formula name: "foo"
String,
# Formula name and dependency type: { "foo" => :build }
# Formula name, dependency type, and version bounds: { "foo" => :build, since: :catalina }
T::Hash[T.any(String, Symbol), T.any(Symbol, T::Array[Symbol])],
),
# If the first argument is only a name, this argument contains the version bounds: { since: :catalina }
T::Hash[Symbol, Symbol],
]
end
PREDICATES.each do |predicate_name|
present_method_name = :"#{predicate_name}_present"
predicate_method_name = :"#{predicate_name}?"
const present_method_name, T::Boolean, default: false
define_method(predicate_method_name) do
send(present_method_name)
end
end
# Changes to this struct must be mirrored in Homebrew::API::Formula.generate_formula_struct_hash
const :aliases, T::Array[String], default: []
const :bottle, T::Hash[String, T.anything], default: {}
const :bottle_checksums, T::Array[T::Hash[String, T.anything]], default: []
const :bottle_rebuild, Integer, default: 0
const :caveats, T.nilable(String)
const :conflicts, T::Array[[String, T::Hash[Symbol, String]]], default: []
const :deprecate_args, T::Hash[Symbol, T.nilable(T.any(String, Symbol))], default: {}
const :desc, String
const :disable_args, T::Hash[Symbol, T.nilable(T.any(String, Symbol))], default: {}
const :head_url_args, [String, T::Hash[Symbol, T.anything]]
const :homepage, String
const :keg_only_args, T::Array[T.any(String, Symbol)], default: []
const :license, SPDX::LicenseExpression
const :link_overwrite_paths, T::Array[String], default: []
const :no_autobump_args, T::Hash[Symbol, T.any(String, Symbol)], default: {}
const :oldnames, T::Array[String], default: []
const :post_install_defined, T::Boolean, default: true
const :pour_bottle_args, T::Hash[Symbol, Symbol], default: {}
const :revision, Integer, default: 0
const :ruby_source_checksum, String
const :ruby_source_path, String
const :service_args, T::Array[[Symbol, BasicObject]], default: []
const :service_name_args, T::Hash[Symbol, String], default: {}
const :service_run_args, T::Array[Homebrew::Service::RunParam], default: []
const :service_run_kwargs, T::Hash[Symbol, Homebrew::Service::RunParam], default: {}
const :stable_checksum, T.nilable(String)
const :stable_url_args, [String, T::Hash[Symbol, T.anything]]
const :stable_version, String
const :tap_git_head, String
const :version_scheme, Integer, default: 0
const :versioned_formulae, T::Array[String], default: []
sig { returns(T::Array[T.any(DependencyArgs, RequirementArgs)]) }
def head_dependencies
spec_dependencies(:head) + spec_requirements(:head)
end
sig { returns(T::Array[T.any(DependencyArgs, RequirementArgs)]) }
def stable_dependencies
spec_dependencies(:stable) + spec_requirements(:stable)
end
sig { returns(T::Array[UsesFromMacOSArgs]) }
def head_uses_from_macos
spec_uses_from_macos(:head)
end
sig { returns(T::Array[UsesFromMacOSArgs]) }
def stable_uses_from_macos
spec_uses_from_macos(:stable)
end
private
const :stable_dependency_hash, T::Hash[String, T::Array[String]], default: {}
const :head_dependency_hash, T::Hash[String, T::Array[String]], default: {}
const :requirements_array, T::Array[T::Hash[String, T.untyped]], default: []
sig { params(spec: Symbol).returns(T::Array[DependencyArgs]) }
def spec_dependencies(spec)
deps_hash = send("#{spec}_dependency_hash")
dependencies = deps_hash.fetch("dependencies", [])
dependencies + [:build, :test, :recommended, :optional].filter_map do |type|
deps_hash["#{type}_dependencies"]&.map do |dep|
{ dep => type }
end
end.flatten(1)
end
sig { params(spec: Symbol).returns(T::Array[UsesFromMacOSArgs]) }
def spec_uses_from_macos(spec)
deps_hash = send("#{spec}_dependency_hash")
zipped_array = deps_hash["uses_from_macos"]&.zip(deps_hash["uses_from_macos_bounds"])
return [] unless zipped_array
zipped_array.map do |entry, bounds|
bounds ||= {}
bounds = bounds.transform_keys(&:to_sym).transform_values(&:to_sym)
if entry.is_a?(Hash)
# The key is the dependency name, the value is the dep type. Only the type should be a symbol
entry = entry.deep_transform_values(&:to_sym)
# When passing both a dep type and bounds, uses_from_macos expects them both in the first argument
entry = entry.merge(bounds)
[entry, {}]
else
[entry, bounds]
end
end
end
sig { params(spec: Symbol).returns(T::Array[RequirementArgs]) }
def spec_requirements(spec)
requirements_array.filter_map do |req|
next unless req["specs"].include?(spec.to_s)
req_name = req["name"].to_sym
next if API_SUPPORTED_REQUIREMENTS.exclude?(req_name)
req_version = case req_name
when :arch
req["version"]&.to_sym
when :macos, :maximum_macos
MacOSVersion::SYMBOLS.key(req["version"])
else
req["version"]
end
req_tags = []
req_tags << req_version if req_version.present?
req_tags += req["contexts"]&.map do |tag|
case tag
when String
tag.to_sym
when Hash
tag.deep_transform_keys(&:to_sym)
else
tag
end
end
if req_tags.empty?
req_name
else
{ req_name => req_tags }
end
end
end
end
end
end
+1 -1
View File
@@ -160,7 +160,7 @@ class DependencyCollector
def parse_symbol_spec(spec, tags)
# When modifying this list of supported requirements, consider
# whether `Formulary::API_SUPPORTED_REQUIREMENTS` should also be changed.
# whether `FormulaStruct::API_SUPPORTED_REQUIREMENTS` should also be changed.
case spec
when :arch then ArchRequirement.new(tags)
when :codesign then CodesignRequirement.new(tags)
+4 -4
View File
@@ -3602,14 +3602,14 @@ class Formula
# @see https://spdx.github.io/spdx-spec/latest/annexes/spdx-license-expressions/ SPDX license expression guide
# @api public
sig {
params(args: T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
.returns(T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
params(args: T.nilable(SPDX::LicenseExpression))
.returns(T.nilable(SPDX::LicenseExpression))
}
def license(args = nil)
if args.nil?
@licenses
else
@licenses = T.let(args, T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
@licenses = T.let(args, T.nilable(SPDX::LicenseExpression))
end
end
@@ -3936,7 +3936,7 @@ class Formula
# ```
#
# @api public
sig { params(block: T.nilable(T.proc.returns(SoftwareSpec))).returns(T.untyped) }
sig { params(block: T.nilable(T.proc.void)).returns(T.untyped) }
def stable(&block)
return T.must(@stable) unless block
+54 -177
View File
@@ -26,10 +26,6 @@ module Formulary
ALLOWED_URL_SCHEMES = %w[file].freeze
private_constant :ALLOWED_URL_SCHEMES
# `:codesign` and custom requirement classes are not supported.
API_SUPPORTED_REQUIREMENTS = [:arch, :linux, :macos, :maximum_macos, :xcode].freeze
private_constant :API_SUPPORTED_REQUIREMENTS
# Enable the factory cache.
#
# @api internal
@@ -227,251 +223,132 @@ module Formulary
mod.const_set(:BUILD_FLAGS, flags)
class_name = class_s(name)
json_formula = Homebrew::API.merge_variations(json_formula_with_variations)
caveats_string = (replace_placeholders(json_formula["caveats"]) if json_formula["caveats"])
uses_from_macos_names = json_formula.fetch("uses_from_macos", []).map do |dep|
next dep unless dep.is_a? Hash
dep.keys.first
end
requirements = {}
json_formula["requirements"]&.map do |req|
req_name = req["name"].to_sym
next if API_SUPPORTED_REQUIREMENTS.exclude?(req_name)
req_version = case req_name
when :arch
req["version"]&.to_sym
when :macos, :maximum_macos
MacOSVersion::SYMBOLS.key(req["version"])
else
req["version"]
end
req_tags = []
req_tags << req_version if req_version.present?
req_tags += req["contexts"]&.map do |tag|
case tag
when String
tag.to_sym
when Hash
tag.deep_transform_keys(&:to_sym)
else
tag
end
end
spec_hash = req_tags.empty? ? req_name : { req_name => req_tags }
specs = req["specs"]
specs ||= ["stable", "head"] # backwards compatibility
specs.each do |spec|
requirements[spec.to_sym] ||= []
requirements[spec.to_sym] << spec_hash
end
end
add_deps = lambda do |spec|
T.bind(self, SoftwareSpec)
dep_json = json_formula.fetch("#{spec}_dependencies", json_formula)
dep_json["dependencies"]&.each do |dep|
# Backwards compatibility check - uses_from_macos used to be a part of dependencies on Linux
next if !json_formula.key?("uses_from_macos_bounds") && uses_from_macos_names.include?(dep) &&
!Homebrew::SimulateSystem.simulating_or_running_on_macos?
depends_on dep
end
[:build, :test, :recommended, :optional].each do |type|
dep_json["#{type}_dependencies"]&.each do |dep|
# Backwards compatibility check - uses_from_macos used to be a part of dependencies on Linux
next if !json_formula.key?("uses_from_macos_bounds") && uses_from_macos_names.include?(dep) &&
!Homebrew::SimulateSystem.simulating_or_running_on_macos?
depends_on dep => type
end
end
dep_json["uses_from_macos"]&.each_with_index do |dep, index|
bounds = dep_json.fetch("uses_from_macos_bounds", [])[index].dup || {}
bounds.deep_transform_keys!(&:to_sym)
bounds.deep_transform_values!(&:to_sym)
if dep.is_a?(Hash)
uses_from_macos dep.deep_transform_values(&:to_sym).merge(bounds)
else
uses_from_macos dep, bounds
end
end
end
formula_struct = Homebrew::API::Formula.generate_formula_struct_hash(json_formula_with_variations)
klass = Class.new(::Formula) do
@loaded_from_api = T.let(true, T.nilable(T::Boolean))
@api_source = T.let(json_formula_with_variations, T.nilable(T::Hash[String, T.untyped]))
desc json_formula["desc"]
homepage json_formula["homepage"]
license SPDX.string_to_license_expression(json_formula["license"])
revision json_formula.fetch("revision", 0)
version_scheme json_formula.fetch("version_scheme", 0)
desc formula_struct.desc
homepage formula_struct.homepage
license formula_struct.license
revision formula_struct.revision
version_scheme formula_struct.version_scheme
if (urls_stable = json_formula["urls"]["stable"].presence)
if formula_struct.stable?
stable do
url_spec = {
tag: urls_stable["tag"],
revision: urls_stable["revision"],
using: urls_stable["using"]&.to_sym,
}.compact
url urls_stable["url"], **url_spec
version json_formula["versions"]["stable"]
sha256 urls_stable["checksum"] if urls_stable["checksum"].present?
url(*formula_struct.stable_url_args)
version formula_struct.stable_version
if (checksum = formula_struct.stable_checksum)
sha256 checksum
end
instance_exec(:stable, &add_deps)
requirements[:stable]&.each do |req|
depends_on req
formula_struct.stable_dependencies.each do |dep|
depends_on dep
end
formula_struct.stable_uses_from_macos.each do |args|
uses_from_macos(*args)
end
end
end
if (urls_head = json_formula["urls"]["head"].presence)
if formula_struct.head?
head do
url_spec = {
branch: urls_head["branch"],
using: urls_head["using"]&.to_sym,
}.compact
url urls_head["url"], **url_spec
url(*formula_struct.head_url_args)
instance_exec(:head, &add_deps)
requirements[:head]&.each do |req|
depends_on req
formula_struct.head_dependencies.each do |dep|
depends_on dep
end
formula_struct.head_uses_from_macos.each do |args|
uses_from_macos(*args)
end
end
end
if (because = json_formula["no_autobump_msg"])
because = because.to_sym if NO_AUTOBUMP_REASONS_LIST.key?(because.to_sym)
no_autobump!(because:)
end
no_autobump!(**formula_struct.no_autobump_args) if formula_struct.no_autobump_message?
bottles_stable = json_formula["bottle"]["stable"].presence
if bottles_stable
if formula_struct.bottle?
bottle do
if Homebrew::EnvConfig.bottle_domain == HOMEBREW_BOTTLE_DEFAULT_DOMAIN
root_url HOMEBREW_BOTTLE_DEFAULT_DOMAIN
else
root_url Homebrew::EnvConfig.bottle_domain
end
rebuild bottles_stable["rebuild"]
bottles_stable["files"].each do |tag, bottle_spec|
cellar = Formulary.convert_to_string_or_symbol bottle_spec["cellar"]
sha256 cellar:, tag.to_sym => bottle_spec["sha256"]
rebuild formula_struct.bottle_rebuild
formula_struct.bottle_checksums.each do |args|
sha256(**args)
end
end
end
if (pour_bottle_only_if = json_formula["pour_bottle_only_if"])
pour_bottle? only_if: pour_bottle_only_if.to_sym
pour_bottle?(**formula_struct.pour_bottle_args) if formula_struct.pour_bottle?
keg_only(*formula_struct.keg_only_args) if formula_struct.keg_only?
deprecate!(**formula_struct.deprecate_args) if formula_struct.deprecated?
disable!(**formula_struct.disable_args) if formula_struct.disabled?
formula_struct.conflicts.each do |name, args|
conflicts_with(name, **args)
end
if (keg_only_reason = json_formula["keg_only_reason"].presence)
reason = Formulary.convert_to_string_or_symbol keg_only_reason["reason"]
keg_only reason, keg_only_reason["explanation"]
end
if (deprecation_date = json_formula["deprecation_date"].presence)
reason = DeprecateDisable.to_reason_string_or_symbol json_formula["deprecation_reason"], type: :formula
replacement_formula = json_formula["deprecation_replacement_formula"]
replacement_cask = json_formula["deprecation_replacement_cask"]
deprecate! date: deprecation_date, because: reason, replacement_formula:, replacement_cask:
end
if (disable_date = json_formula["disable_date"].presence)
reason = DeprecateDisable.to_reason_string_or_symbol json_formula["disable_reason"], type: :formula
replacement_formula = json_formula["disable_replacement_formula"]
replacement_cask = json_formula["disable_replacement_cask"]
disable! date: disable_date, because: reason, replacement_formula:, replacement_cask:
end
json_formula["conflicts_with"]&.each_with_index do |conflict, index|
conflicts_with conflict, because: json_formula.dig("conflicts_with_reasons", index)
end
json_formula["link_overwrite"]&.each do |overwrite_path|
link_overwrite overwrite_path
formula_struct.link_overwrite_paths.each do |path|
link_overwrite path
end
define_method(:install) do
raise NotImplementedError, "Cannot build from source from abstract formula."
end
@post_install_defined_boolean = T.let(json_formula["post_install_defined"], T.nilable(T::Boolean))
@post_install_defined_boolean = true if @post_install_defined_boolean.nil? # Backwards compatibility
@post_install_defined_boolean = T.let(formula_struct.post_install_defined, T.nilable(T::Boolean))
define_method(:post_install_defined?) do
self.class.instance_variable_get(:@post_install_defined_boolean)
end
if (service_hash = json_formula["service"].presence)
service_hash = Homebrew::Service.from_hash(service_hash)
if formula_struct.service?
service do
T.bind(self, Homebrew::Service)
run(*formula_struct.service_run_args, **formula_struct.service_run_kwargs) if formula_struct.service_run?
name(**formula_struct.service_name_args) if formula_struct.service_name?
if (run_params = service_hash.delete(:run).presence)
case run_params
when Hash
run(**run_params)
when Array, String
run run_params
end
end
if (name_params = service_hash.delete(:name).presence)
name(**name_params)
end
service_hash.each do |key, arg|
formula_struct.service_args.each do |key, arg|
public_send(key, arg)
end
end
end
@caveats_string = T.let(caveats_string, T.nilable(String))
@caveats_string = T.let(formula_struct.caveats, T.nilable(String))
define_method(:caveats) do
self.class.instance_variable_get(:@caveats_string)
end
@tap_git_head_string = T.let(json_formula["tap_git_head"], T.nilable(String))
@tap_git_head_string = T.let(formula_struct.tap_git_head, T.nilable(String))
define_method(:tap_git_head) do
self.class.instance_variable_get(:@tap_git_head_string)
end
@oldnames_array = T.let(json_formula["oldnames"] || [json_formula["oldname"]].compact, T.nilable(T::Array[String]))
@oldnames_array = T.let(formula_struct.oldnames, T.nilable(T::Array[String]))
define_method(:oldnames) do
self.class.instance_variable_get(:@oldnames_array)
end
@aliases_array = T.let(json_formula.fetch("aliases", []), T.nilable(T::Array[String]))
@aliases_array = T.let(formula_struct.aliases, T.nilable(T::Array[String]))
define_method(:aliases) do
self.class.instance_variable_get(:@aliases_array)
end
@versioned_formulae_array = T.let(json_formula.fetch("versioned_formulae", []), T.nilable(T::Array[String]))
@versioned_formulae_array = T.let(formula_struct.versioned_formulae, T.nilable(T::Array[String]))
define_method(:versioned_formulae_names) do
self.class.instance_variable_get(:@versioned_formulae_array)
end
@ruby_source_path_string = T.let(json_formula["ruby_source_path"], T.nilable(String))
@ruby_source_path_string = T.let(formula_struct.ruby_source_path, T.nilable(String))
define_method(:ruby_source_path) do
self.class.instance_variable_get(:@ruby_source_path_string)
end
@ruby_source_checksum_string = T.let(json_formula.dig("ruby_source_checksum", "sha256"), T.nilable(String))
@ruby_source_checksum_string ||= json_formula["ruby_source_sha256"]
@ruby_source_checksum_string = T.let(formula_struct.ruby_source_checksum, T.nilable(String))
define_method(:ruby_source_checksum) do
checksum = self.class.instance_variable_get(:@ruby_source_checksum_string)
Checksum.new(checksum) if checksum
@@ -0,0 +1,41 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Homebrew::API::FormulaStruct`.
# Please instead update this file by running `bin/tapioca dsl Homebrew::API::FormulaStruct`.
class Homebrew::API::FormulaStruct
sig { returns(T::Boolean) }
def bottle?; end
sig { returns(T::Boolean) }
def deprecated?; end
sig { returns(T::Boolean) }
def disabled?; end
sig { returns(T::Boolean) }
def head?; end
sig { returns(T::Boolean) }
def keg_only?; end
sig { returns(T::Boolean) }
def no_autobump_message?; end
sig { returns(T::Boolean) }
def pour_bottle?; end
sig { returns(T::Boolean) }
def service?; end
sig { returns(T::Boolean) }
def service_name?; end
sig { returns(T::Boolean) }
def service_run?; end
sig { returns(T::Boolean) }
def stable?; end
end
@@ -0,0 +1,25 @@
# typed: strict
# frozen_string_literal: true
require_relative "../../../global"
require "api/formula_struct"
module Tapioca
module Compilers
class ApiStructs < Tapioca::Dsl::Compiler
ConstantType = type_member { { fixed: T.class_of(T::Struct) } }
sig { override.returns(T::Enumerable[T::Module[T.anything]]) }
def self.gather_constants = [::Homebrew::API::FormulaStruct]
sig { override.void }
def decorate
root.create_class(T.must(constant.name)) do |klass|
::Homebrew::API::FormulaStruct::PREDICATES.each do |predicate_name|
klass.create_method("#{predicate_name}?", return_type: "T::Boolean")
end
end
end
end
end
end
+13 -5
View File
@@ -362,6 +362,7 @@ RSpec.describe Formulary do
"download" => nil,
"version" => "1.0",
"contexts" => ["build"],
"specs" => ["stable"],
},
],
"conflicts_with" => ["conflicting_formula"],
@@ -377,21 +378,28 @@ RSpec.describe Formulary do
},
"ruby_source_path" => "Formula/#{formula_name}.rb",
"ruby_source_checksum" => { "sha256" => "ABCDEFGHIJKLMNOPQRSTUVWXYZ" },
"tap_git_head" => "0000000000000000000000000000000000000000",
}.merge(extra_items),
}
end
let(:deprecate_json) do
{
"deprecation_date" => "2022-06-15",
"deprecation_reason" => "repo_archived",
"deprecated" => true,
"deprecation_date" => "2022-06-15",
"deprecation_reason" => "repo_archived",
"deprecation_replacement_formula" => nil,
"deprecation_replacement_cask" => nil,
}
end
let(:disable_json) do
{
"disable_date" => "2022-06-15",
"disable_reason" => "requires something else",
"disabled" => true,
"disable_date" => "2022-06-15",
"disable_reason" => "requires something else",
"disable_replacement_formula" => nil,
"disable_replacement_cask" => nil,
}
end
@@ -419,7 +427,7 @@ RSpec.describe Formulary do
{
"variations" => {
"x86_64_linux" => {
"dependencies" => ["dep", "uses_from_macos_dep"],
"dependencies" => ["dep"],
},
},
}
+8 -12
View File
@@ -154,19 +154,15 @@ module SPDX
end
end
sig {
params(
string: T.nilable(String),
).returns(
T.nilable(
T.any(
String,
Symbol,
T::Hash[T.any(String, Symbol), T.untyped],
),
),
LicenseExpression = T.type_alias do
T.any(
String,
Symbol,
T::Hash[T.any(String, Symbol), T.anything],
)
}
end
sig { params(string: T.nilable(String)).returns(T.nilable(LicenseExpression)) }
def string_to_license_expression(string)
return if string.blank?