Merge pull request #12936 from Bo98/api-offline
Improve consistency between Git and API formula handling
This commit is contained in:
commit
d23dba67ca
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "api/analytics"
|
||||
require "api/bottle"
|
||||
require "api/cask"
|
||||
require "api/cask-source"
|
||||
require "api/formula"
|
||||
@ -21,6 +20,7 @@ module Homebrew
|
||||
module_function
|
||||
|
||||
API_DOMAIN = "https://formulae.brew.sh/api"
|
||||
HOMEBREW_CACHE_API = (HOMEBREW_CACHE/"api").freeze
|
||||
|
||||
sig { params(endpoint: String, json: T::Boolean).returns(T.any(String, Hash)) }
|
||||
def fetch(endpoint, json: true)
|
||||
|
@ -1,96 +0,0 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "github_packages"
|
||||
|
||||
module Homebrew
|
||||
module API
|
||||
# Helper functions for using the bottle JSON API.
|
||||
#
|
||||
# @api private
|
||||
module Bottle
|
||||
class << self
|
||||
extend T::Sig
|
||||
|
||||
sig { returns(String) }
|
||||
def bottle_api_path
|
||||
"bottle"
|
||||
end
|
||||
alias generic_bottle_api_path bottle_api_path
|
||||
|
||||
GITHUB_PACKAGES_SHA256_REGEX = %r{#{GitHubPackages::URL_REGEX}.*/blobs/sha256:(?<sha256>\h{64})$}.freeze
|
||||
|
||||
sig { params(name: String).returns(Hash) }
|
||||
def fetch(name)
|
||||
name = name.sub(%r{^homebrew/core/}, "")
|
||||
Homebrew::API.fetch "#{bottle_api_path}/#{name}.json"
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T::Boolean) }
|
||||
def available?(name)
|
||||
fetch name
|
||||
true
|
||||
rescue ArgumentError
|
||||
false
|
||||
end
|
||||
|
||||
sig { params(name: String).void }
|
||||
def fetch_bottles(name)
|
||||
hash = fetch(name)
|
||||
bottle_tag = Utils::Bottles.tag.to_s
|
||||
|
||||
if !hash["bottles"].key?(bottle_tag) && !hash["bottles"].key?("all")
|
||||
odie "No bottle available for #{name} on the current OS"
|
||||
end
|
||||
|
||||
download_bottle(hash, bottle_tag)
|
||||
|
||||
hash["dependencies"].each do |dep_hash|
|
||||
existing_formula = begin
|
||||
Formulary.factory dep_hash["name"]
|
||||
rescue FormulaUnavailableError
|
||||
# The formula might not exist if it's not installed and homebrew/core isn't tapped
|
||||
nil
|
||||
end
|
||||
|
||||
next if existing_formula.present? && existing_formula.latest_version_installed?
|
||||
|
||||
download_bottle(dep_hash, bottle_tag)
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(url: String).returns(T.nilable(String)) }
|
||||
def checksum_from_url(url)
|
||||
match = url.match GITHUB_PACKAGES_SHA256_REGEX
|
||||
return if match.blank?
|
||||
|
||||
match[:sha256]
|
||||
end
|
||||
|
||||
sig { params(hash: Hash, tag: String).void }
|
||||
def download_bottle(hash, tag)
|
||||
bottle = hash["bottles"][tag]
|
||||
bottle ||= hash["bottles"]["all"]
|
||||
return if bottle.blank?
|
||||
|
||||
sha256 = bottle["sha256"] || checksum_from_url(bottle["url"])
|
||||
bottle_filename = ::Bottle::Filename.new(hash["name"], hash["pkg_version"], tag, hash["rebuild"])
|
||||
|
||||
resource = Resource.new hash["name"]
|
||||
resource.url bottle["url"]
|
||||
resource.sha256 sha256
|
||||
resource.version hash["pkg_version"]
|
||||
resource.downloader.resolved_basename = bottle_filename
|
||||
|
||||
resource.fetch
|
||||
|
||||
# Map the name of this formula to the local bottle path to allow the
|
||||
# formula to be loaded by passing just the name to `Formulary::factory`.
|
||||
[hash["name"], "homebrew/core/#{hash["name"]}"].each do |name|
|
||||
Formulary.map_formula_name_to_local_bottle_path name, resource.downloader.cached_location
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -16,10 +16,34 @@ module Homebrew
|
||||
end
|
||||
alias generic_formula_api_path formula_api_path
|
||||
|
||||
sig { returns(String) }
|
||||
def cached_formula_json_file
|
||||
HOMEBREW_CACHE_API/"#{formula_api_path}.json"
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(Hash) }
|
||||
def fetch(name)
|
||||
Homebrew::API.fetch "#{formula_api_path}/#{name}.json"
|
||||
end
|
||||
|
||||
sig { returns(Array) }
|
||||
def all_formulae
|
||||
@all_formulae ||= begin
|
||||
curl_args = %w[--compressed --silent https://formulae.brew.sh/api/formula.json]
|
||||
if cached_formula_json_file.exist?
|
||||
last_modified = cached_formula_json_file.mtime.utc
|
||||
last_modified = last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
curl_args = ["--header", "If-Modified-Since: #{last_modified}", *curl_args]
|
||||
end
|
||||
curl_download(*curl_args, to: HOMEBREW_CACHE_API/"#{formula_api_path}.json", max_time: 5)
|
||||
|
||||
json_formulae = JSON.parse(cached_formula_json_file.read)
|
||||
|
||||
json_formulae.to_h do |json_formula|
|
||||
[json_formula["name"], json_formula.except("name")]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -764,7 +764,7 @@ then
|
||||
export HOMEBREW_DEVELOPER_MODE="1"
|
||||
fi
|
||||
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" && -n "${HOMEBREW_DEVELOPER_COMMAND}" ]]
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" && -n "${HOMEBREW_DEVELOPER_COMMAND}" && "${HOMEBREW_COMMAND}" != "irb" ]]
|
||||
then
|
||||
odie "Developer commands cannot be run while HOMEBREW_INSTALL_FROM_API is set!"
|
||||
elif [[ -n "${HOMEBREW_INSTALL_FROM_API}" && -n "${HOMEBREW_DEVELOPER_MODE}" ]]
|
||||
|
@ -6,7 +6,6 @@ require "cask/config"
|
||||
require "cask/dsl"
|
||||
require "cask/metadata"
|
||||
require "searchable"
|
||||
require "api"
|
||||
|
||||
module Cask
|
||||
# An instance of a cask.
|
||||
@ -166,14 +165,7 @@ module Cask
|
||||
# special case: tap version is not available
|
||||
return [] if version.nil?
|
||||
|
||||
latest_version = if Homebrew::EnvConfig.install_from_api? &&
|
||||
(latest_cask_version = Homebrew::API::Versions.latest_cask_version(token))
|
||||
DSL::Version.new latest_cask_version.to_s
|
||||
else
|
||||
version
|
||||
end
|
||||
|
||||
if latest_version.latest?
|
||||
if version.latest?
|
||||
return versions if (greedy || greedy_latest) && outdated_download_sha?
|
||||
|
||||
return []
|
||||
@ -185,10 +177,10 @@ module Cask
|
||||
current = installed.last
|
||||
|
||||
# not outdated unless there is a different version on tap
|
||||
return [] if current == latest_version
|
||||
return [] if current == version
|
||||
|
||||
# collect all installed versions that are different than tap version and return them
|
||||
installed.reject { |v| v == latest_version }
|
||||
installed.reject { |v| v == version }
|
||||
end
|
||||
|
||||
def outdated_info(greedy, verbose, json, greedy_latest, greedy_auto_updates)
|
||||
|
@ -214,7 +214,15 @@ module Cask
|
||||
FromTapPathLoader,
|
||||
FromPathLoader,
|
||||
].each do |loader_class|
|
||||
return loader_class.new(ref) if loader_class.can_load?(ref)
|
||||
next unless loader_class.can_load?(ref)
|
||||
|
||||
if loader_class == FromTapLoader && Homebrew::EnvConfig.install_from_api? &&
|
||||
ref.start_with?("homebrew/cask/") && !Tap.fetch("homebrew/cask").installed? &&
|
||||
Homebrew::API::CaskSource.available?(ref)
|
||||
return FromContentLoader.new(Homebrew::API::CaskSource.fetch(ref))
|
||||
end
|
||||
|
||||
return loader_class.new(ref)
|
||||
end
|
||||
|
||||
return FromTapPathLoader.new(default_path(ref)) if FromTapPathLoader.can_load?(default_path(ref))
|
||||
@ -231,6 +239,10 @@ module Cask
|
||||
EOS
|
||||
end
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api? && Homebrew::API::CaskSource.available?(ref)
|
||||
return FromContentLoader.new(Homebrew::API::CaskSource.fetch(ref))
|
||||
end
|
||||
|
||||
possible_installed_cask = Cask.new(ref)
|
||||
return FromPathLoader.new(possible_installed_cask.installed_caskfile) if possible_installed_cask.installed?
|
||||
|
||||
|
@ -49,7 +49,8 @@ module Cask
|
||||
begin
|
||||
if (tap_path = CaskLoader.tap_paths(token).first)
|
||||
CaskLoader::FromTapPathLoader.new(tap_path).load(config: config)
|
||||
elsif (caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first)
|
||||
elsif (caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first) &&
|
||||
(!Homebrew::EnvConfig.install_from_api? || !Homebrew::API::CaskSource.available?(token))
|
||||
CaskLoader::FromPathLoader.new(caskroom_path).load(config: config)
|
||||
else
|
||||
CaskLoader.load(token, config: config)
|
||||
|
@ -45,8 +45,8 @@ class CaskDependent
|
||||
end
|
||||
end
|
||||
|
||||
def recursive_dependencies(ignore_missing: false, &block)
|
||||
Dependency.expand(self, ignore_missing: ignore_missing, &block)
|
||||
def recursive_dependencies(&block)
|
||||
Dependency.expand(self, &block)
|
||||
end
|
||||
|
||||
def recursive_requirements(&block)
|
||||
|
@ -45,18 +45,16 @@ module Homebrew
|
||||
# the formula and prints a warning unless `only` is specified.
|
||||
sig {
|
||||
params(
|
||||
only: T.nilable(Symbol),
|
||||
ignore_unavailable: T.nilable(T::Boolean),
|
||||
method: T.nilable(Symbol),
|
||||
uniq: T::Boolean,
|
||||
prefer_loading_from_api: T::Boolean,
|
||||
only: T.nilable(Symbol),
|
||||
ignore_unavailable: T.nilable(T::Boolean),
|
||||
method: T.nilable(Symbol),
|
||||
uniq: T::Boolean,
|
||||
).returns(T::Array[T.any(Formula, Keg, Cask::Cask)])
|
||||
}
|
||||
def to_formulae_and_casks(only: parent&.only_formula_or_cask, ignore_unavailable: nil, method: nil, uniq: true,
|
||||
prefer_loading_from_api: false)
|
||||
def to_formulae_and_casks(only: parent&.only_formula_or_cask, ignore_unavailable: nil, method: nil, uniq: true)
|
||||
@to_formulae_and_casks ||= {}
|
||||
@to_formulae_and_casks[only] ||= downcased_unique_named.flat_map do |name|
|
||||
load_formula_or_cask(name, only: only, method: method, prefer_loading_from_api: prefer_loading_from_api)
|
||||
load_formula_or_cask(name, only: only, method: method)
|
||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
||||
Cask::CaskUnreadableError
|
||||
@ -90,15 +88,10 @@ module Homebrew
|
||||
end.uniq.freeze
|
||||
end
|
||||
|
||||
def load_formula_or_cask(name, only: nil, method: nil, prefer_loading_from_api: false)
|
||||
def load_formula_or_cask(name, only: nil, method: nil)
|
||||
unreadable_error = nil
|
||||
|
||||
if only != :cask
|
||||
if prefer_loading_from_api && Homebrew::EnvConfig.install_from_api? &&
|
||||
Homebrew::API::Bottle.available?(name)
|
||||
Homebrew::API::Bottle.fetch_bottles(name)
|
||||
end
|
||||
|
||||
begin
|
||||
formula = case method
|
||||
when nil, :factory
|
||||
@ -129,16 +122,11 @@ module Homebrew
|
||||
end
|
||||
|
||||
if only != :formula
|
||||
if prefer_loading_from_api && Homebrew::EnvConfig.install_from_api? &&
|
||||
Homebrew::API::CaskSource.available?(name)
|
||||
contents = Homebrew::API::CaskSource.fetch(name)
|
||||
end
|
||||
|
||||
want_keg_like_cask = [:latest_kegs, :default_kegs, :kegs].include?(method)
|
||||
|
||||
begin
|
||||
config = Cask::Config.from_args(@parent) if @cask_options
|
||||
cask = Cask::CaskLoader.load(contents || name, config: config)
|
||||
cask = Cask::CaskLoader.load(name, config: config)
|
||||
|
||||
if unreadable_error.present?
|
||||
onoe <<~EOS
|
||||
|
@ -66,26 +66,18 @@ module Homebrew
|
||||
args = fetch_args.parse
|
||||
|
||||
bucket = if args.deps?
|
||||
args.named.to_formulae_and_casks(prefer_loading_from_api: true).flat_map do |formula_or_cask|
|
||||
args.named.to_formulae_and_casks.flat_map do |formula_or_cask|
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
f = formula_or_cask
|
||||
|
||||
deps = if Homebrew::EnvConfig.install_from_api?
|
||||
f.recursive_dependencies do |_, dependency|
|
||||
Dependency.prune if EnvConfig.install_from_api? && (dependency.build? || dependency.test?)
|
||||
end
|
||||
else
|
||||
f.recursive_dependencies
|
||||
end
|
||||
|
||||
[f, *deps.map(&:to_formula)]
|
||||
[f, *f.recursive_dependencies.map(&:to_formula)]
|
||||
else
|
||||
formula_or_cask
|
||||
end
|
||||
end
|
||||
else
|
||||
args.named.to_formulae_and_casks(prefer_loading_from_api: true)
|
||||
args.named.to_formulae_and_casks
|
||||
end.uniq
|
||||
|
||||
puts "Fetching: #{bucket * ", "}" if bucket.size > 1
|
||||
|
@ -252,16 +252,7 @@ module Homebrew
|
||||
def info_formula(f, args:)
|
||||
specs = []
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api? && Homebrew::API::Bottle.available?(f.name)
|
||||
info = Homebrew::API::Bottle.fetch(f.name)
|
||||
|
||||
latest_version = info["pkg_version"].split("_").first
|
||||
bottle_exists = info["bottles"].key?(Utils::Bottles.tag.to_s) || info["bottles"].key?("all")
|
||||
|
||||
s = "stable #{latest_version}"
|
||||
s += " (bottled)" if bottle_exists
|
||||
specs << s
|
||||
elsif (stable = f.stable)
|
||||
if (stable = f.stable)
|
||||
s = "stable #{stable.version}"
|
||||
s += " (bottled)" if stable.bottled? && f.pour_bottle?
|
||||
specs << s
|
||||
|
@ -167,7 +167,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
begin
|
||||
formulae, casks = args.named.to_formulae_and_casks(prefer_loading_from_api: true)
|
||||
formulae, casks = args.named.to_formulae_and_casks
|
||||
.partition { |formula_or_cask| formula_or_cask.is_a?(Formula) }
|
||||
rescue FormulaOrCaskUnavailableError, Cask::CaskUnavailableError => e
|
||||
retry if Tap.install_default_cask_tap_if_necessary(force: args.cask?)
|
||||
|
@ -98,10 +98,7 @@ module Homebrew
|
||||
if verbose?
|
||||
outdated_kegs = f.outdated_kegs(fetch_head: args.fetch_HEAD?)
|
||||
|
||||
current_version = if !f.head? && Homebrew::EnvConfig.install_from_api? &&
|
||||
(f.core_formula? || f.tap.blank?)
|
||||
Homebrew::API::Versions.latest_formula_version(f.name)&.to_s || f.pkg_version.to_s
|
||||
elsif f.alias_changed? && !f.latest_formula.latest_version_installed?
|
||||
current_version = if f.alias_changed? && !f.latest_formula.latest_version_installed?
|
||||
latest = f.latest_formula
|
||||
"#{latest.name} (#{latest.pkg_version})"
|
||||
elsif f.head? && outdated_kegs.any? { |k| k.version.to_s == f.pkg_version.to_s }
|
||||
|
@ -88,22 +88,6 @@ module Homebrew
|
||||
def reinstall
|
||||
args = reinstall_args.parse
|
||||
|
||||
# We need to use the bottle API instead of just using the formula file
|
||||
# from an installed keg because it will not contain bottle information.
|
||||
# As a consequence, `brew reinstall` will also upgrade outdated formulae
|
||||
if Homebrew::EnvConfig.install_from_api?
|
||||
args.named.each do |name|
|
||||
formula = Formulary.factory(name)
|
||||
next unless formula.any_version_installed?
|
||||
next if formula.tap.present? && !formula.core_formula?
|
||||
next unless Homebrew::API::Bottle.available?(name)
|
||||
|
||||
Homebrew::API::Bottle.fetch_bottles(name)
|
||||
rescue FormulaUnavailableError
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
formulae, casks = args.named.to_formulae_and_casks(method: :resolve)
|
||||
.partition { |o| o.is_a?(Formula) }
|
||||
|
||||
|
@ -745,6 +745,20 @@ EOS
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]]
|
||||
then
|
||||
mkdir -p "${HOMEBREW_CACHE}/api"
|
||||
# TODO: use --header If-Modified-Since
|
||||
curl \
|
||||
"${CURL_DISABLE_CURLRC_ARGS[@]}" \
|
||||
--fail --compressed --silent --max-time 5 \
|
||||
--location --remote-time --output "${HOMEBREW_CACHE}/api/formula.json" \
|
||||
--user-agent "${HOMEBREW_USER_AGENT_CURL}" \
|
||||
"https://formulae.brew.sh/api/formula.json"
|
||||
# TODO: we probably want to print an error if this fails.
|
||||
# TODO: set HOMEBREW_UPDATED or HOMEBREW_UPDATE_FAILED
|
||||
fi
|
||||
|
||||
safe_cd "${HOMEBREW_REPOSITORY}"
|
||||
|
||||
# HOMEBREW_UPDATE_AUTO wasn't modified in subshell.
|
||||
|
@ -161,19 +161,6 @@ module Homebrew
|
||||
puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
|
||||
end
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api?
|
||||
formulae_to_install.map! do |formula|
|
||||
next formula if formula.head?
|
||||
next formula if formula.tap.present? && !formula.core_formula?
|
||||
next formula unless Homebrew::API::Bottle.available?(formula.name)
|
||||
|
||||
Homebrew::API::Bottle.fetch_bottles(formula.name)
|
||||
Formulary.factory(formula.name)
|
||||
rescue FormulaUnavailableError
|
||||
formula
|
||||
end
|
||||
end
|
||||
|
||||
if formulae_to_install.empty?
|
||||
oh1 "No packages to upgrade"
|
||||
else
|
||||
@ -226,15 +213,6 @@ module Homebrew
|
||||
def upgrade_outdated_casks(casks, args:)
|
||||
return false if args.formula?
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api?
|
||||
casks = casks.map do |cask|
|
||||
next cask if cask.tap.present? && cask.tap != "homebrew/cask"
|
||||
next cask unless Homebrew::API::CaskSource.available?(cask.token)
|
||||
|
||||
Cask::CaskLoader.load Homebrew::API::CaskSource.fetch(cask.token)
|
||||
end
|
||||
end
|
||||
|
||||
Cask::Cmd::Upgrade.upgrade_casks(
|
||||
*casks,
|
||||
force: args.force?,
|
||||
|
@ -46,15 +46,6 @@ class Dependency
|
||||
formula
|
||||
end
|
||||
|
||||
def unavailable_core_formula?
|
||||
to_formula
|
||||
false
|
||||
rescue CoreTapFormulaUnavailableError
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def installed?
|
||||
to_formula.latest_version_installed?
|
||||
end
|
||||
@ -98,7 +89,7 @@ class Dependency
|
||||
# the list.
|
||||
# The default filter, which is applied when a block is not given, omits
|
||||
# optionals and recommendeds based on what the dependent has asked for
|
||||
def expand(dependent, deps = dependent.deps, cache_key: nil, ignore_missing: false, &block)
|
||||
def expand(dependent, deps = dependent.deps, cache_key: nil, &block)
|
||||
# Keep track dependencies to avoid infinite cyclic dependency recursion.
|
||||
@expand_stack ||= []
|
||||
@expand_stack.push dependent.name
|
||||
@ -112,22 +103,20 @@ class Dependency
|
||||
|
||||
deps.each do |dep|
|
||||
next if dependent.name == dep.name
|
||||
# avoid downloading build dependency bottles
|
||||
next if dep.build? && dependent.pour_bottle? && Homebrew::EnvConfig.install_from_api?
|
||||
|
||||
case action(dependent, dep, ignore_missing: ignore_missing, &block)
|
||||
case action(dependent, dep, &block)
|
||||
when :prune
|
||||
next
|
||||
when :skip
|
||||
next if @expand_stack.include? dep.name
|
||||
|
||||
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, ignore_missing: ignore_missing, &block))
|
||||
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, &block))
|
||||
when :keep_but_prune_recursive_deps
|
||||
expanded_deps << dep
|
||||
else
|
||||
next if @expand_stack.include? dep.name
|
||||
|
||||
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, ignore_missing: ignore_missing, &block))
|
||||
expanded_deps.concat(expand(dep.to_formula, cache_key: cache_key, &block))
|
||||
expanded_deps << dep
|
||||
end
|
||||
end
|
||||
@ -139,10 +128,8 @@ class Dependency
|
||||
@expand_stack.pop
|
||||
end
|
||||
|
||||
def action(dependent, dep, ignore_missing: false, &block)
|
||||
def action(dependent, dep, &block)
|
||||
catch(:action) do
|
||||
prune if ignore_missing && dep.unavailable_core_formula?
|
||||
|
||||
if block
|
||||
yield dependent, dep
|
||||
elsif dep.optional? || dep.recommended?
|
||||
|
@ -890,7 +890,7 @@ module Homebrew
|
||||
# Formulae installed with HOMEBREW_INSTALL_FROM_API should not count as deleted formulae
|
||||
# but may not have a tap listed in their tab
|
||||
tap = Tab.for_keg(keg).tap
|
||||
next if (tap.blank? || tap.core_tap?) && Homebrew::API::Bottle.available?(keg.name)
|
||||
next if (tap.blank? || tap.core_tap?) && Homebrew::API::Formula.all_formulae.key?(keg.name)
|
||||
end
|
||||
|
||||
keg.name
|
||||
|
@ -234,13 +234,6 @@ class TapFormulaUnavailableError < FormulaUnavailableError
|
||||
end
|
||||
end
|
||||
|
||||
# Raised when a formula in a the core tap is unavailable.
|
||||
class CoreTapFormulaUnavailableError < TapFormulaUnavailableError
|
||||
def initialize(name)
|
||||
super CoreTap.instance, name
|
||||
end
|
||||
end
|
||||
|
||||
# Raised when a formula in a specific tap does not contain a formula class.
|
||||
class TapFormulaClassUnavailableError < TapFormulaUnavailableError
|
||||
include FormulaClassUnavailableErrorModule
|
||||
|
@ -84,16 +84,9 @@ class Keg
|
||||
def self.bottle_dependencies
|
||||
@bottle_dependencies ||= begin
|
||||
formulae = relocation_formulae
|
||||
if Homebrew::EnvConfig.install_from_api?
|
||||
gcc_hash = Homebrew::API::Formula.fetch(CompilerSelector.preferred_gcc)
|
||||
preferred_gcc_version = Version.new gcc_hash["versions"]["stable"]
|
||||
else
|
||||
gcc = Formulary.factory(CompilerSelector.preferred_gcc)
|
||||
preferred_gcc_version = gcc.version
|
||||
end
|
||||
gcc = Formulary.factory(CompilerSelector.preferred_gcc)
|
||||
if !Homebrew::EnvConfig.simulate_macos_on_linux? &&
|
||||
DevelopmentTools.non_apple_gcc_version("gcc") < preferred_gcc_version.major
|
||||
gcc = Formulary.factory(CompilerSelector.preferred_gcc) if Homebrew::EnvConfig.install_from_api?
|
||||
DevelopmentTools.non_apple_gcc_version("gcc") < gcc.version.to_i
|
||||
formulae += gcc.recursive_dependencies.map(&:name)
|
||||
formulae << gcc.name
|
||||
end
|
||||
|
@ -525,14 +525,7 @@ class Formula
|
||||
# exists and is not empty.
|
||||
# @private
|
||||
def latest_version_installed?
|
||||
latest_prefix = if !head? && Homebrew::EnvConfig.install_from_api? &&
|
||||
(latest_pkg_version = Homebrew::API::Versions.latest_formula_version(name))
|
||||
prefix latest_pkg_version
|
||||
else
|
||||
latest_installed_prefix
|
||||
end
|
||||
|
||||
(dir = latest_prefix).directory? && !dir.children.empty?
|
||||
(dir = latest_installed_prefix).directory? && !dir.children.empty?
|
||||
end
|
||||
|
||||
# If at least one version of {Formula} is installed.
|
||||
@ -1352,11 +1345,6 @@ class Formula
|
||||
Formula.cache[:outdated_kegs][cache_key] ||= begin
|
||||
all_kegs = []
|
||||
current_version = T.let(false, T::Boolean)
|
||||
latest_version = if !head? && Homebrew::EnvConfig.install_from_api? && (core_formula? || tap.blank?)
|
||||
Homebrew::API::Versions.latest_formula_version(name) || pkg_version
|
||||
else
|
||||
pkg_version
|
||||
end
|
||||
|
||||
installed_kegs.each do |keg|
|
||||
all_kegs << keg
|
||||
@ -1364,8 +1352,8 @@ class Formula
|
||||
next if version.head?
|
||||
|
||||
tab = Tab.for_keg(keg)
|
||||
next if version_scheme > tab.version_scheme && latest_version != version
|
||||
next if version_scheme == tab.version_scheme && latest_version > version
|
||||
next if version_scheme > tab.version_scheme && pkg_version != version
|
||||
next if version_scheme == tab.version_scheme && pkg_version > version
|
||||
|
||||
# don't consider this keg current if there's a newer formula available
|
||||
next if follow_installed_alias? && new_formula_available?
|
||||
|
@ -218,11 +218,6 @@ class FormulaInstaller
|
||||
def verify_deps_exist
|
||||
begin
|
||||
compute_dependencies
|
||||
rescue CoreTapFormulaUnavailableError => e
|
||||
raise unless Homebrew::API::Bottle.available? e.name
|
||||
|
||||
Homebrew::API::Bottle.fetch_bottles(e.name)
|
||||
retry
|
||||
rescue TapFormulaUnavailableError => e
|
||||
raise if e.tap.installed?
|
||||
|
||||
|
@ -6,6 +6,8 @@ require "extend/cachable"
|
||||
require "tab"
|
||||
require "utils/bottles"
|
||||
|
||||
require "active_support/core_ext/hash/deep_transform_values"
|
||||
|
||||
# The {Formulary} is responsible for creating instances of {Formula}.
|
||||
# It is not meant to be used directly from formulae.
|
||||
#
|
||||
@ -26,22 +28,32 @@ module Formulary
|
||||
!@factory_cache.nil?
|
||||
end
|
||||
|
||||
def self.formula_class_defined?(path)
|
||||
cache.key?(path)
|
||||
def self.formula_class_defined_from_path?(path)
|
||||
cache.key?(:path) && cache[:path].key?(path)
|
||||
end
|
||||
|
||||
def self.formula_class_get(path)
|
||||
cache.fetch(path)
|
||||
def self.formula_class_defined_from_api?(name)
|
||||
cache.key?(:api) && cache[:api].key?(name)
|
||||
end
|
||||
|
||||
def self.formula_class_get_from_path(path)
|
||||
cache[:path].fetch(path)
|
||||
end
|
||||
|
||||
def self.formula_class_get_from_api(name)
|
||||
cache[:api].fetch(name)
|
||||
end
|
||||
|
||||
def self.clear_cache
|
||||
cache.each do |key, klass|
|
||||
next if key == :formulary_factory
|
||||
cache.each do |type, cached_objects|
|
||||
next if type == :formulary_factory
|
||||
|
||||
namespace = klass.name.deconstantize
|
||||
next if namespace.deconstantize != name
|
||||
cached_objects.each_value do |klass|
|
||||
namespace = klass.name.deconstantize
|
||||
next if namespace.deconstantize != name
|
||||
|
||||
remove_const(namespace.demodulize)
|
||||
remove_const(namespace.demodulize)
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
@ -108,7 +120,95 @@ module Formulary
|
||||
contents = path.open("r") { |f| ensure_utf8_encoding(f).read }
|
||||
namespace = "FormulaNamespace#{Digest::MD5.hexdigest(path.to_s)}"
|
||||
klass = load_formula(name, path, contents, namespace, flags: flags, ignore_errors: ignore_errors)
|
||||
cache[path] = klass
|
||||
cache[:path] ||= {}
|
||||
cache[:path][path] = klass
|
||||
end
|
||||
|
||||
def self.load_formula_from_api(name, flags:)
|
||||
namespace = "FormulaNamespaceAPI#{Digest::MD5.hexdigest(name)}"
|
||||
|
||||
mod = Module.new
|
||||
remove_const(namespace) if const_defined?(namespace)
|
||||
const_set(namespace, mod)
|
||||
|
||||
mod.const_set(:BUILD_FLAGS, flags)
|
||||
|
||||
class_s = Formulary.class_s(name)
|
||||
json_formula = Homebrew::API::Formula.all_formulae[name]
|
||||
|
||||
klass = Class.new(::Formula) do
|
||||
desc json_formula["desc"]
|
||||
homepage json_formula["homepage"]
|
||||
license json_formula["license"]
|
||||
revision json_formula["revision"]
|
||||
version_scheme json_formula["version_scheme"]
|
||||
|
||||
if (urls_stable = json_formula["urls"]["stable"]).present?
|
||||
stable do
|
||||
url urls_stable["url"]
|
||||
version json_formula["versions"]["stable"]
|
||||
end
|
||||
end
|
||||
|
||||
if (bottles_stable = json_formula["bottle"]["stable"]).present?
|
||||
bottle do
|
||||
root_url bottles_stable["root_url"]
|
||||
rebuild bottles_stable["rebuild"]
|
||||
bottles_stable["files"].each do |tag, bottle_spec|
|
||||
cellar = Formulary.convert_to_string_or_symbol bottle_spec["cellar"]
|
||||
sha256 cellar: cellar, tag.to_sym => bottle_spec["sha256"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (keg_only_reason = json_formula["keg_only_reason"]).present?
|
||||
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"]).present?
|
||||
deprecate! date: deprecation_date, because: json_formula["deprecation_reason"]
|
||||
end
|
||||
|
||||
if (disable_date = json_formula["disable_date"]).present?
|
||||
disable! date: disable_date, because: json_formula["disable_reason"]
|
||||
end
|
||||
|
||||
json_formula["build_dependencies"].each do |dep|
|
||||
depends_on dep => :build
|
||||
end
|
||||
|
||||
json_formula["dependencies"].each do |dep|
|
||||
depends_on dep
|
||||
end
|
||||
|
||||
json_formula["recommended_dependencies"].each do |dep|
|
||||
depends_on dep => :recommended
|
||||
end
|
||||
|
||||
json_formula["optional_dependencies"].each do |dep|
|
||||
depends_on dep => :optional
|
||||
end
|
||||
|
||||
json_formula["uses_from_macos"].each do |dep|
|
||||
dep = dep.deep_transform_values(&:to_sym) if dep.is_a?(Hash)
|
||||
uses_from_macos dep
|
||||
end
|
||||
|
||||
def install
|
||||
raise "Cannot build from source from abstract formula."
|
||||
end
|
||||
|
||||
@caveats_string = json_formula["caveats"]
|
||||
def caveats
|
||||
@caveats_string
|
||||
end
|
||||
end
|
||||
|
||||
mod.const_set(class_s, klass)
|
||||
|
||||
cache[:api] ||= {}
|
||||
cache[:api][name] = klass
|
||||
end
|
||||
|
||||
def self.resolve(name, spec: nil, force_bottle: false, flags: [])
|
||||
@ -155,6 +255,12 @@ module Formulary
|
||||
class_name
|
||||
end
|
||||
|
||||
def self.convert_to_string_or_symbol(string)
|
||||
return string[1..].to_sym if string.start_with?(":")
|
||||
|
||||
string
|
||||
end
|
||||
|
||||
# A {FormulaLoader} returns instances of formulae.
|
||||
# Subclasses implement loaders for particular sources of formulae.
|
||||
class FormulaLoader
|
||||
@ -182,8 +288,8 @@ module Formulary
|
||||
end
|
||||
|
||||
def klass(flags:, ignore_errors:)
|
||||
load_file(flags: flags, ignore_errors: ignore_errors) unless Formulary.formula_class_defined?(path)
|
||||
Formulary.formula_class_get(path)
|
||||
load_file(flags: flags, ignore_errors: ignore_errors) unless Formulary.formula_class_defined_from_path?(path)
|
||||
Formulary.formula_class_get_from_path(path)
|
||||
end
|
||||
|
||||
private
|
||||
@ -345,10 +451,6 @@ module Formulary
|
||||
rescue FormulaClassUnavailableError => e
|
||||
raise TapFormulaClassUnavailableError.new(tap, name, e.path, e.class_name, e.class_list), "", e.backtrace
|
||||
rescue FormulaUnavailableError => e
|
||||
if tap.core_tap? && Homebrew::EnvConfig.install_from_api?
|
||||
raise CoreTapFormulaUnavailableError.new(name), "", e.backtrace
|
||||
end
|
||||
|
||||
raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace
|
||||
end
|
||||
|
||||
@ -367,10 +469,6 @@ module Formulary
|
||||
end
|
||||
|
||||
def get_formula(*)
|
||||
if !CoreTap.instance.installed? && Homebrew::EnvConfig.install_from_api?
|
||||
raise CoreTapFormulaUnavailableError, name
|
||||
end
|
||||
|
||||
raise FormulaUnavailableError, name
|
||||
end
|
||||
end
|
||||
@ -392,6 +490,26 @@ module Formulary
|
||||
end
|
||||
end
|
||||
|
||||
# Load formulae from the API.
|
||||
class FormulaAPILoader < FormulaLoader
|
||||
def initialize(name)
|
||||
super name, Formulary.core_path(name)
|
||||
end
|
||||
|
||||
def klass(flags:, ignore_errors:)
|
||||
load_from_api(flags: flags) unless Formulary.formula_class_defined_from_api?(name)
|
||||
Formulary.formula_class_get_from_api(name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_from_api(flags:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{name} from API" if debug?
|
||||
|
||||
Formulary.load_formula_from_api(name, flags: flags)
|
||||
end
|
||||
end
|
||||
|
||||
# Return a {Formula} instance for the given reference.
|
||||
# `ref` is a string containing:
|
||||
#
|
||||
@ -405,12 +523,6 @@ module Formulary
|
||||
)
|
||||
raise ArgumentError, "Formulae must have a ref!" unless ref
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api? &&
|
||||
@formula_name_local_bottle_path_map.present? &&
|
||||
@formula_name_local_bottle_path_map.key?(ref)
|
||||
ref = @formula_name_local_bottle_path_map[ref]
|
||||
end
|
||||
|
||||
cache_key = "#{ref}-#{spec}-#{alias_path}-#{from}"
|
||||
if factory_cached? && cache[:formulary_factory] &&
|
||||
cache[:formulary_factory][cache_key]
|
||||
@ -427,24 +539,6 @@ module Formulary
|
||||
formula
|
||||
end
|
||||
|
||||
# Map a formula name to a local/fetched bottle archive. This mapping will be used by {Formulary::factory}
|
||||
# to allow formulae to be loaded automatically from their local bottle archive without
|
||||
# needing to exist in a tap or be passed as a complete path. For example,
|
||||
# to map `hello` from its bottle archive:
|
||||
# <pre>Formulary.map_formula_name_to_local_bottle_path "hello", HOMEBREW_CACHE/"hello--2.10"
|
||||
# Formulary.factory "hello" # returns the hello formula from the local bottle archive
|
||||
# </pre>
|
||||
# @param formula_name the formula name string to map.
|
||||
# @param local_bottle_path a path pointing to the target bottle archive.
|
||||
def self.map_formula_name_to_local_bottle_path(formula_name, local_bottle_path)
|
||||
unless Homebrew::EnvConfig.install_from_api?
|
||||
raise UsageError, "HOMEBREW_INSTALL_FROM_API not set but required for #{__method__}!"
|
||||
end
|
||||
|
||||
@formula_name_local_bottle_path_map ||= {}
|
||||
@formula_name_local_bottle_path_map[formula_name] = Pathname(local_bottle_path).realpath
|
||||
end
|
||||
|
||||
# Return a {Formula} instance for the given rack.
|
||||
#
|
||||
# @param spec when nil, will auto resolve the formula's spec.
|
||||
@ -539,11 +633,9 @@ module Formulary
|
||||
when URL_START_REGEX
|
||||
return FromUrlLoader.new(ref)
|
||||
when HOMEBREW_TAP_FORMULA_REGEX
|
||||
# If `homebrew/core` is specified and not installed, check whether the formula is already installed.
|
||||
if ref.start_with?("homebrew/core/") && !CoreTap.instance.installed? && Homebrew::EnvConfig.install_from_api?
|
||||
name = ref.split("/", 3).last
|
||||
possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{name}/.brew/#{name}.rb")
|
||||
return FormulaLoader.new(name, possible_keg_formula) if possible_keg_formula.file?
|
||||
return FormulaAPILoader.new(name) if Homebrew::API::Formula.all_formulae.key?(name)
|
||||
end
|
||||
|
||||
return TapLoader.new(ref, from: from)
|
||||
@ -557,6 +649,12 @@ module Formulary
|
||||
possible_alias = CoreTap.instance.alias_dir/ref
|
||||
return AliasLoader.new(possible_alias) if possible_alias.file?
|
||||
|
||||
if !CoreTap.instance.installed? &&
|
||||
Homebrew::EnvConfig.install_from_api? &&
|
||||
Homebrew::API::Formula.all_formulae.key?(ref)
|
||||
return FormulaAPILoader.new(ref)
|
||||
end
|
||||
|
||||
possible_tap_formulae = tap_paths(ref)
|
||||
raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae) if possible_tap_formulae.size > 1
|
||||
|
||||
|
@ -142,8 +142,6 @@ class Tap
|
||||
# The remote repository name of this {Tap}.
|
||||
# e.g. `user/homebrew-repo`
|
||||
def remote_repo
|
||||
raise TapUnavailableError, name unless installed?
|
||||
|
||||
return unless remote
|
||||
|
||||
@remote_repo ||= remote.delete_prefix("https://github.com/")
|
||||
@ -795,6 +793,12 @@ class CoreTap < Tap
|
||||
safe_system HOMEBREW_BREW_FILE, "tap", instance.name
|
||||
end
|
||||
|
||||
def remote
|
||||
super if installed? || !Homebrew::EnvConfig.install_from_api?
|
||||
|
||||
Homebrew::EnvConfig.core_git_remote
|
||||
end
|
||||
|
||||
# CoreTap never allows shallow clones (on request from GitHub).
|
||||
def install(quiet: false, clone_target: nil, force_auto_update: nil, custom_remote: false)
|
||||
remote = Homebrew::EnvConfig.core_git_remote # set by HOMEBREW_CORE_GIT_REMOTE
|
||||
|
@ -1,95 +0,0 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "api"
|
||||
|
||||
describe Homebrew::API::Bottle do
|
||||
let(:bottle_json) {
|
||||
<<~EOS
|
||||
{
|
||||
"name": "hello",
|
||||
"pkg_version": "2.10",
|
||||
"rebuild": 0,
|
||||
"bottles": {
|
||||
"arm64_big_sur": {
|
||||
"url": "https://ghcr.io/v2/homebrew/core/hello/blobs/sha256:b3b083db0807ff92c6e289a298f378198354b7727fb9ba9f4d550b8e08f90a60"
|
||||
},
|
||||
"big_sur": {
|
||||
"url": "https://ghcr.io/v2/homebrew/core/hello/blobs/sha256:69489ae397e4645127aa7773211310f81ebb6c99e1f8e3e22c5cdb55333f5408"
|
||||
},
|
||||
"x86_64_linux": {
|
||||
"url": "https://ghcr.io/v2/homebrew/core/hello/blobs/sha256:e6980196298e0a9cfe4fa4e328a71a1869a4d5e1d31c38442150ed784cfc0e29"
|
||||
}
|
||||
},
|
||||
"dependencies": []
|
||||
}
|
||||
EOS
|
||||
}
|
||||
let(:bottle_hash) { JSON.parse(bottle_json) }
|
||||
|
||||
def mock_curl_output(stdout: "", success: true)
|
||||
curl_output = OpenStruct.new(stdout: stdout, success?: success)
|
||||
allow(Utils::Curl).to receive(:curl_output).and_return curl_output
|
||||
end
|
||||
|
||||
describe "::fetch" do
|
||||
it "fetches the bottle JSON for a formula that exists" do
|
||||
mock_curl_output stdout: bottle_json
|
||||
fetched_hash = described_class.fetch("foo")
|
||||
expect(fetched_hash).to eq bottle_hash
|
||||
end
|
||||
|
||||
it "raises an error if the formula does not exist" do
|
||||
mock_curl_output success: false
|
||||
expect { described_class.fetch("bar") }.to raise_error(ArgumentError, /No file found/)
|
||||
end
|
||||
|
||||
it "raises an error if the bottle JSON is invalid" do
|
||||
mock_curl_output stdout: "foo"
|
||||
expect { described_class.fetch("baz") }.to raise_error(ArgumentError, /Invalid JSON file/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::available?" do
|
||||
it "returns `true` if `fetch` succeeds" do
|
||||
allow(described_class).to receive(:fetch)
|
||||
expect(described_class.available?("foo")).to be true
|
||||
end
|
||||
|
||||
it "returns `false` if `fetch` fails" do
|
||||
allow(described_class).to receive(:fetch).and_raise ArgumentError
|
||||
expect(described_class.available?("foo")).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe "::fetch_bottles" do
|
||||
before do
|
||||
ENV["HOMEBREW_INSTALL_FROM_API"] = "1"
|
||||
allow(described_class).to receive(:fetch).and_return bottle_hash
|
||||
end
|
||||
|
||||
it "fetches bottles if a bottle is available" do
|
||||
allow(Utils::Bottles).to receive(:tag).and_return :arm64_big_sur
|
||||
expect { described_class.fetch_bottles("hello") }.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises an error if no bottle is available" do
|
||||
allow(Utils::Bottles).to receive(:tag).and_return :catalina
|
||||
expect { described_class.fetch_bottles("hello") }.to raise_error(SystemExit)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::checksum_from_url" do
|
||||
let(:sha256) { "b3b083db0807ff92c6e289a298f378198354b7727fb9ba9f4d550b8e08f90a60" }
|
||||
let(:url) { "https://ghcr.io/v2/homebrew/core/hello/blobs/sha256:#{sha256}" }
|
||||
let(:non_ghp_url) { "https://formulae.brew.sh/api/formula/hello.json" }
|
||||
|
||||
it "returns the `sha256` for a GitHub packages URL" do
|
||||
expect(described_class.checksum_from_url(url)).to eq sha256
|
||||
end
|
||||
|
||||
it "returns `nil` for a non-GitHub packages URL" do
|
||||
expect(described_class.checksum_from_url(non_ghp_url)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
@ -201,28 +201,107 @@ describe Formulary do
|
||||
}.to raise_error(TapFormulaAmbiguityError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::map_formula_name_to_local_bottle_path" do
|
||||
before do
|
||||
formula_path.dirname.mkpath
|
||||
formula_path.write formula_content
|
||||
end
|
||||
context "when loading from the API" do
|
||||
def formula_json_contents(extra_items = {})
|
||||
{
|
||||
formula_name => {
|
||||
"desc" => "testball",
|
||||
"homepage" => "https://example.com",
|
||||
"license" => "MIT",
|
||||
"revision" => 0,
|
||||
"version_scheme" => 0,
|
||||
"versions" => { "stable" => "0.1" },
|
||||
"urls" => {
|
||||
"stable" => {
|
||||
"url" => "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz",
|
||||
"tag" => nil,
|
||||
"revision" => nil,
|
||||
},
|
||||
},
|
||||
"bottle" => {
|
||||
"stable" => {
|
||||
"rebuild" => 0,
|
||||
"root_url" => "file://#{bottle_dir}",
|
||||
"files" => {
|
||||
Utils::Bottles.tag.to_s => {
|
||||
"cellar" => ":any",
|
||||
"url" => "file://#{bottle_dir}/#{formula_name}",
|
||||
"sha256" => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"keg_only_reason" => {
|
||||
"reason" => ":provided_by_macos",
|
||||
"explanation" => "",
|
||||
},
|
||||
"build_dependencies" => ["build_dep"],
|
||||
"dependencies" => ["dep"],
|
||||
"recommended_dependencies" => ["recommended_dep"],
|
||||
"optional_dependencies" => ["optional_dep"],
|
||||
"uses_from_macos" => ["uses_from_macos_dep"],
|
||||
"caveats" => "",
|
||||
}.merge(extra_items),
|
||||
}
|
||||
end
|
||||
|
||||
it "maps a reference to a new Formula" do
|
||||
expect {
|
||||
described_class.factory("formula-to-map")
|
||||
}.to raise_error(FormulaUnavailableError)
|
||||
let(:deprecate_json) do
|
||||
{
|
||||
"deprecation_date" => "2022-06-15",
|
||||
"deprecation_reason" => "repo_archived",
|
||||
}
|
||||
end
|
||||
|
||||
ENV["HOMEBREW_INSTALL_FROM_API"] = nil
|
||||
expect {
|
||||
described_class.map_formula_name_to_local_bottle_path "formula-to-map", formula_path
|
||||
}.to raise_error(UsageError, /HOMEBREW_INSTALL_FROM_API not set/)
|
||||
let(:disable_json) do
|
||||
{
|
||||
"disable_date" => "2022-06-15",
|
||||
"disable_reason" => "repo_archived",
|
||||
}
|
||||
end
|
||||
|
||||
ENV["HOMEBREW_INSTALL_FROM_API"] = "1"
|
||||
described_class.map_formula_name_to_local_bottle_path "formula-to-map", formula_path
|
||||
before do
|
||||
allow(described_class).to receive(:loader_for).and_return(described_class::FormulaAPILoader.new(formula_name))
|
||||
end
|
||||
|
||||
expect(described_class.factory("formula-to-map")).to be_kind_of(Formula)
|
||||
it "returns a Formula when given a name" do
|
||||
allow(Homebrew::API::Formula).to receive(:all_formulae).and_return formula_json_contents
|
||||
|
||||
formula = described_class.factory(formula_name)
|
||||
expect(formula).to be_kind_of(Formula)
|
||||
expect(formula.keg_only_reason.reason).to eq :provided_by_macos
|
||||
if OS.mac?
|
||||
expect(formula.deps.count).to eq 4
|
||||
elsif OS.linux?
|
||||
expect(formula.deps.count).to eq 5
|
||||
end
|
||||
expect(formula.uses_from_macos_elements).to eq ["uses_from_macos_dep"]
|
||||
expect {
|
||||
formula.install
|
||||
}.to raise_error("Cannot build from source from abstract formula.")
|
||||
end
|
||||
|
||||
it "returns a deprecated Formula when given a name" do
|
||||
allow(Homebrew::API::Formula).to receive(:all_formulae).and_return formula_json_contents(deprecate_json)
|
||||
|
||||
formula = described_class.factory(formula_name)
|
||||
expect(formula).to be_kind_of(Formula)
|
||||
expect(formula.deprecated?).to be true
|
||||
expect {
|
||||
formula.install
|
||||
}.to raise_error("Cannot build from source from abstract formula.")
|
||||
end
|
||||
|
||||
it "returns a disabled Formula when given a name" do
|
||||
allow(Homebrew::API::Formula).to receive(:all_formulae).and_return formula_json_contents(disable_json)
|
||||
|
||||
formula = described_class.factory(formula_name)
|
||||
expect(formula).to be_kind_of(Formula)
|
||||
expect(formula.disabled?).to be true
|
||||
expect {
|
||||
formula.install
|
||||
}.to raise_error("Cannot build from source from abstract formula.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user