Install formulae from JSON files
This commit is contained in:
parent
83aa3ff258
commit
22f986b89a
@ -49,12 +49,14 @@ module Homebrew
|
||||
ignore_unavailable: T.nilable(T::Boolean),
|
||||
method: T.nilable(Symbol),
|
||||
uniq: T::Boolean,
|
||||
load_from_json: 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)
|
||||
def to_formulae_and_casks(only: parent&.only_formula_or_cask, ignore_unavailable: nil, method: nil, uniq: true,
|
||||
load_from_json: false)
|
||||
@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)
|
||||
load_formula_or_cask(name, only: only, method: method, load_from_json: load_from_json)
|
||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError,
|
||||
Cask::CaskUnreadableError
|
||||
@ -88,7 +90,7 @@ module Homebrew
|
||||
end.uniq.freeze
|
||||
end
|
||||
|
||||
def load_formula_or_cask(name, only: nil, method: nil)
|
||||
def load_formula_or_cask(name, only: nil, method: nil, load_from_json: false)
|
||||
unreadable_error = nil
|
||||
|
||||
if only != :cask
|
||||
@ -121,6 +123,12 @@ module Homebrew
|
||||
# The formula was found, but there's a problem with its implementation
|
||||
unreadable_error ||= e
|
||||
rescue NoSuchKegError, FormulaUnavailableError => e
|
||||
if load_from_json && ENV["HOMEBREW_JSON_CORE"].present? && !CoreTap.instance.installed? &&
|
||||
Utils::BottleAPI.bottle_available?(name)
|
||||
Utils::BottleAPI.download_bottles(name)
|
||||
retry
|
||||
end
|
||||
|
||||
raise e if only == :formula
|
||||
end
|
||||
end
|
||||
|
||||
@ -154,8 +154,10 @@ module Homebrew
|
||||
EOS
|
||||
end
|
||||
|
||||
allow_loading_from_json = ENV["HOMEBREW_JSON_CORE"].present? && !CoreTap.instance.installed?
|
||||
|
||||
begin
|
||||
formulae, casks = args.named.to_formulae_and_casks
|
||||
formulae, casks = args.named.to_formulae_and_casks(load_from_json: allow_loading_from_json)
|
||||
.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?)
|
||||
|
||||
@ -97,8 +97,10 @@ module Homebrew
|
||||
elsif f.head? && outdated_kegs.any? { |k| k.version.to_s == f.pkg_version.to_s }
|
||||
# There is a newer HEAD but the version number has not changed.
|
||||
"latest HEAD"
|
||||
else
|
||||
elsif f.tap.present?
|
||||
f.pkg_version.to_s
|
||||
else
|
||||
Utils::BottleAPI.latest_pkg_version(f.name).to_s
|
||||
end
|
||||
|
||||
outdated_versions = outdated_kegs.group_by { |keg| Formulary.from_keg(keg).full_name }
|
||||
|
||||
@ -84,6 +84,19 @@ module Homebrew
|
||||
def reinstall
|
||||
args = reinstall_args.parse
|
||||
|
||||
if ENV["HOMEBREW_JSON_CORE"].present? && !CoreTap.instance.installed?
|
||||
args.named.each do |name|
|
||||
formula = Formulary.factory(name)
|
||||
next unless formula.any_version_installed?
|
||||
next if formula.tap.present? && !formula.tap.installed?
|
||||
next unless Utils::BottleAPI.bottle_available?(name)
|
||||
|
||||
Utils::BottleAPI.download_bottles(name)
|
||||
rescue FormulaUnavailableError
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
formulae, casks = args.named.to_formulae_and_casks(method: :resolve)
|
||||
.partition { |o| o.is_a?(Formula) }
|
||||
|
||||
|
||||
@ -159,6 +159,20 @@ module Homebrew
|
||||
puts pinned.map { |f| "#{f.full_specified_name} #{f.pkg_version}" } * ", "
|
||||
end
|
||||
|
||||
if ENV["HOMEBREW_JSON_CORE"].present? && !CoreTap.instance.installed?
|
||||
formulae_to_install.map! do |formula|
|
||||
next formula if formula.tap.present? && formula.tap.installed?
|
||||
next formula unless Utils::BottleAPI.bottle_available?(formula.name)
|
||||
|
||||
Utils::BottleAPI.download_bottles(formula.name)
|
||||
Formulary.factory(formula.name)
|
||||
rescue FormulaUnavailableError
|
||||
formula
|
||||
end
|
||||
end
|
||||
|
||||
opoo formulae_to_install
|
||||
|
||||
if formulae_to_install.empty?
|
||||
oh1 "No packages to upgrade"
|
||||
else
|
||||
|
||||
@ -1325,6 +1325,11 @@ class Formula
|
||||
Formula.cache[:outdated_kegs][cache_key] ||= begin
|
||||
all_kegs = []
|
||||
current_version = T.let(false, T::Boolean)
|
||||
latest_version = if tap.present?
|
||||
pkg_version
|
||||
else
|
||||
Utils::BottleAPI.latest_pkg_version name
|
||||
end
|
||||
|
||||
installed_kegs.each do |keg|
|
||||
all_kegs << keg
|
||||
@ -1332,8 +1337,8 @@ class Formula
|
||||
next if version.head?
|
||||
|
||||
tab = Tab.for_keg(keg)
|
||||
next if version_scheme > tab.version_scheme && pkg_version != version
|
||||
next if version_scheme == tab.version_scheme && pkg_version > version
|
||||
next if version_scheme > tab.version_scheme && latest_version != version
|
||||
next if version_scheme == tab.version_scheme && latest_version > version
|
||||
|
||||
# don't consider this keg current if there's a newer formula available
|
||||
next if follow_installed_alias? && new_formula_available?
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
require "time"
|
||||
|
||||
require "utils/analytics"
|
||||
require "utils/bottle_api"
|
||||
require "utils/curl"
|
||||
require "utils/fork"
|
||||
require "utils/formatter"
|
||||
|
||||
103
Library/Homebrew/utils/bottle_api.rb
Normal file
103
Library/Homebrew/utils/bottle_api.rb
Normal file
@ -0,0 +1,103 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "github_packages"
|
||||
|
||||
module Utils
|
||||
# Helper functions for using the Bottle JSON API.
|
||||
#
|
||||
# @api private
|
||||
module BottleAPI
|
||||
extend T::Sig
|
||||
|
||||
module_function
|
||||
|
||||
FORMULAE_BREW_SH_BOTTLE_API_DOMAIN = if OS.mac?
|
||||
"https://formulae.brew.sh/api/bottle"
|
||||
else
|
||||
"https://formulae.brew.sh/api/bottle-linux"
|
||||
end.freeze
|
||||
|
||||
FORMULAE_BREW_SH_VERSIONS_API_URL = if OS.mac?
|
||||
"https://formulae.brew.sh/api/versions.json"
|
||||
else
|
||||
"https://formulae.brew.sh/api/versions-linux.json"
|
||||
end.freeze
|
||||
|
||||
GITHUB_PACKAGES_SHA256_REGEX = %r{#{GitHubPackages::URL_REGEX}.*/blobs/sha256:(?<sha256>\h{64})$}.freeze
|
||||
|
||||
sig { params(name: String).returns(Hash) }
|
||||
def fetch(name)
|
||||
return @cache[name] if @cache.present? && @cache.key?(name)
|
||||
|
||||
api_url = "#{FORMULAE_BREW_SH_BOTTLE_API_DOMAIN}/#{name}.json"
|
||||
output = Utils::Curl.curl_output("--fail", api_url)
|
||||
raise ArgumentError, "No JSON file found at #{Tty.underline}#{api_url}#{Tty.reset}" unless output.success?
|
||||
|
||||
@cache ||= {}
|
||||
@cache[name] = JSON.parse(output.stdout)
|
||||
rescue JSON::ParserError
|
||||
raise ArgumentError, "Invalid JSON file: #{Tty.underline}#{api_url}#{Tty.reset}"
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(PkgVersion) }
|
||||
def latest_pkg_version(name)
|
||||
@formula_versions ||= begin
|
||||
output = Utils::Curl.curl_output("--fail", FORMULAE_BREW_SH_VERSIONS_API_URL)
|
||||
JSON.parse(output.stdout)
|
||||
end
|
||||
PkgVersion.new(@formula_versions[name]["version"], @formula_versions[name]["revision"])
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T::Boolean) }
|
||||
def bottle_available?(name)
|
||||
fetch name
|
||||
true
|
||||
rescue ArgumentError
|
||||
false
|
||||
end
|
||||
|
||||
sig { params(name: String).void }
|
||||
def download_bottles(name)
|
||||
hash = fetch(name)
|
||||
bottle_tag = Utils::Bottles.tag.to_s
|
||||
|
||||
odie "No bottle availabe for current OS" unless hash["bottles"].key? bottle_tag
|
||||
|
||||
download_bottle(hash, bottle_tag)
|
||||
|
||||
hash["dependencies"].each do |dep_hash|
|
||||
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: Symbol).void }
|
||||
def download_bottle(hash, tag)
|
||||
bottle = hash["bottles"][tag]
|
||||
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`.
|
||||
Formulary.map_formula_name_to_local_bottle_path hash["name"], resource.downloader.cached_location
|
||||
end
|
||||
end
|
||||
end
|
||||
7
Library/Homebrew/utils/bottle_api.rbi
Normal file
7
Library/Homebrew/utils/bottle_api.rbi
Normal file
@ -0,0 +1,7 @@
|
||||
# typed: strict
|
||||
|
||||
module Utils
|
||||
module BottleAPI
|
||||
include Kernel
|
||||
end
|
||||
end
|
||||
@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "BREW" "1" "June 2021" "Homebrew" "brew"
|
||||
.TH "BREW" "1" "July 2021" "Homebrew" "brew"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBbrew\fR \- The Missing Package Manager for macOS (or Linux)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user