mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Support offline usage under HOMEBREW_INSTALL_FROM_API
This commit is contained in:
parent
b49949dc01
commit
1d36c42fb7
@ -21,6 +21,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)
|
||||
|
@ -20,6 +20,17 @@ module Homebrew
|
||||
def fetch(name)
|
||||
Homebrew::API.fetch "#{formula_api_path}/#{name}.json"
|
||||
end
|
||||
|
||||
sig { returns(Array) }
|
||||
def all_formulae
|
||||
@all_formulae ||= begin
|
||||
json_formulae = JSON.parse((HOMEBREW_CACHE_API/"#{formula_api_path}.json").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}" ]]
|
||||
|
@ -94,11 +94,6 @@ module Homebrew
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -745,6 +745,20 @@ EOS
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]]
|
||||
then
|
||||
mkdir -p "${HOMEBREW_CACHE}/api"
|
||||
# TODO: etags?
|
||||
curl \
|
||||
"${CURL_DISABLE_CURLRC_ARGS[@]}" \
|
||||
--fail --compressed --silent --max-time 5 \
|
||||
--location --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
|
||||
|
@ -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.
|
||||
#
|
||||
@ -44,6 +46,8 @@ module Formulary
|
||||
remove_const(namespace.demodulize)
|
||||
end
|
||||
|
||||
remove_const("FormulaNamespaceAPI")
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
@ -392,6 +396,102 @@ 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:)
|
||||
namespace = "FormulaNamespaceAPI"
|
||||
mod = if Formulary.const_defined?(namespace)
|
||||
Formulary.const_get(namespace)
|
||||
else
|
||||
Formulary.const_set(namespace, Module.new)
|
||||
end
|
||||
|
||||
mod.send(:remove_const, :BUILD_FLAGS) if mod.const_defined?(:BUILD_FLAGS)
|
||||
mod.const_set(:BUILD_FLAGS, flags)
|
||||
|
||||
class_s = Formulary.class_s(name)
|
||||
if mod.const_defined?(class_s)
|
||||
mod.const_get(class_s)
|
||||
else
|
||||
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"]
|
||||
bottles_stable["files"].each do |tag, bottle_spec|
|
||||
cellar = bottle_spec["cellar"]
|
||||
cellar = cellar[1..].to_sym if cellar.start_with?(":")
|
||||
sha256 cellar: cellar, tag.to_sym => bottle_spec["sha256"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (keg_only_reason = json_formula["keg_only_reason"]).present?
|
||||
reason = keg_only_reason["reason"]
|
||||
reason = reason[1..].to_sym if reason.start_with?(":")
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return a {Formula} instance for the given reference.
|
||||
# `ref` is a string containing:
|
||||
#
|
||||
@ -539,11 +639,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 +655,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
|
||||
|
Loading…
x
Reference in New Issue
Block a user