2021-07-05 11:43:34 -04:00
|
|
|
# typed: true
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require "github_packages"
|
|
|
|
|
|
|
|
# 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-formulae.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(T.nilable(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
|
|
|
|
|
|
|
|
return unless @formula_versions.key? name
|
|
|
|
|
2021-07-06 09:33:41 -04:00
|
|
|
version = Version.new(@formula_versions[name]["version"])
|
|
|
|
revision = @formula_versions[name]["revision"]
|
|
|
|
PkgVersion.new(version, revision)
|
2021-07-05 11:43:34 -04:00
|
|
|
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 fetch_bottles(name)
|
|
|
|
hash = fetch(name)
|
|
|
|
bottle_tag = Utils::Bottles.tag.to_s
|
|
|
|
|
2021-07-14 10:14:48 -04:00
|
|
|
odie "No bottle available for current OS" if !hash["bottles"].key?(bottle_tag) && !hash["bottles"].key?("all")
|
2021-07-05 11:43:34 -04:00
|
|
|
|
|
|
|
download_bottle(hash, bottle_tag)
|
|
|
|
|
|
|
|
hash["dependencies"].each do |dep_hash|
|
2021-07-14 02:10:38 -04:00
|
|
|
existing_formula = begin
|
|
|
|
Formulary.factory dep_hash["name"]
|
|
|
|
rescue FormulaUnavailableError
|
2021-07-14 10:19:29 -04:00
|
|
|
# The formula might not exist if it's not installed and homebrew/core isn't tapped
|
2021-07-14 02:10:38 -04:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
next if existing_formula.present? && existing_formula.latest_version_installed?
|
|
|
|
|
2021-07-05 11:43:34 -04:00
|
|
|
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]
|
2021-07-14 10:14:48 -04:00
|
|
|
bottle ||= hash["bottles"]["all"]
|
2021-07-05 11:43:34 -04:00
|
|
|
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
|