Merge pull request #16638 from apainintheneck/load-internal-json-v3

Load internal json v3
This commit is contained in:
Kevin 2024-02-28 19:46:14 -08:00 committed by GitHub
commit 154a21706a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 455 additions and 35 deletions

View File

@ -184,6 +184,11 @@ module Homebrew
Tap.fetch(org, repo)
end
sig { returns(T::Boolean) }
def self.internal_json_v3?
ENV["HOMEBREW_INTERNAL_JSON_V3"].present?
end
end
# @api private

View File

@ -39,19 +39,24 @@ module Homebrew
sig { returns(T::Boolean) }
def self.download_and_cache_data!
json_formulae, updated = Homebrew::API.fetch_json_api_file "formula.jws.json"
if Homebrew::API.internal_json_v3?
json_formulae, updated = Homebrew::API.fetch_json_api_file "internal/v3/homebrew-core.jws.json"
overwrite_cache! T.cast(json_formulae, T::Hash[String, T.untyped])
else
json_formulae, updated = Homebrew::API.fetch_json_api_file "formula.jws.json"
cache["aliases"] = {}
cache["renames"] = {}
cache["formulae"] = json_formulae.to_h do |json_formula|
json_formula["aliases"].each do |alias_name|
cache["aliases"][alias_name] = json_formula["name"]
end
(json_formula["oldnames"] || [json_formula["oldname"]].compact).each do |oldname|
cache["renames"][oldname] = json_formula["name"]
end
cache["aliases"] = {}
cache["renames"] = {}
cache["formulae"] = json_formulae.to_h do |json_formula|
json_formula["aliases"].each do |alias_name|
cache["aliases"][alias_name] = json_formula["name"]
end
(json_formula["oldnames"] || [json_formula["oldname"]].compact).each do |oldname|
cache["renames"][oldname] = json_formula["name"]
end
[json_formula["name"], json_formula.except("name")]
[json_formula["name"], json_formula.except("name")]
end
end
updated
@ -88,6 +93,28 @@ module Homebrew
cache["renames"]
end
sig { returns(Hash) }
def self.tap_migrations
# Not sure that we need to reload here.
unless cache.key?("tap_migrations")
json_updated = download_and_cache_data!
write_names_and_aliases(regenerate: json_updated)
end
cache["tap_migrations"]
end
sig { returns(String) }
def self.tap_git_head
# Note sure we need to reload here.
unless cache.key?("tap_git_head")
json_updated = download_and_cache_data!
write_names_and_aliases(regenerate: json_updated)
end
cache["tap_git_head"]
end
sig { params(regenerate: T::Boolean).void }
def self.write_names_and_aliases(regenerate: false)
download_and_cache_data! unless cache.key?("formulae")

View File

@ -58,14 +58,6 @@ module Homebrew
Formulary.enable_factory_cache!
Formula.generating_hash!
homebrew_core_tap_hash = {
"tap_git_head" => tap.git_head,
"aliases" => tap.alias_table,
"renames" => tap.formula_renames,
"tap_migrations" => tap.tap_migrations,
"formulae" => {},
}
tap.formula_names.each do |name|
formula = Formulary.factory(name)
name = formula.name
@ -77,15 +69,12 @@ module Homebrew
File.write("api/formula/#{name}.json", FORMULA_JSON_TEMPLATE)
File.write("formula/#{name}.html", html_template_name)
end
homebrew_core_tap_hash["formulae"][formula.name] =
formula.to_hash_with_variations(hash_method: :to_api_hash)
rescue
onoe "Error while generating data for formula '#{name}'."
raise
end
homebrew_core_tap_json = JSON.generate(homebrew_core_tap_hash)
homebrew_core_tap_json = JSON.generate(tap.to_api_hash)
File.write("api/internal/v3/homebrew-core.json", homebrew_core_tap_json) unless args.dry_run?
canonical_json = JSON.pretty_generate(tap.formula_renames.merge(tap.alias_table))
File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run?

View File

@ -11,4 +11,11 @@ module Cachable
def clear_cache
cache.clear
end
private
sig { params(hash: T::Hash[T.untyped, T.untyped]).void }
def overwrite_cache!(hash)
@cache = hash
end
end

View File

@ -251,8 +251,8 @@ module Formulary
desc json_formula["desc"]
homepage json_formula["homepage"]
license SPDX.string_to_license_expression(json_formula["license"])
revision json_formula["revision"]
version_scheme json_formula["version_scheme"]
revision json_formula.fetch("revision", 0)
version_scheme json_formula.fetch("version_scheme", 0)
if (urls_stable = json_formula["urls"]["stable"].presence)
stable do
@ -262,7 +262,7 @@ module Formulary
using: urls_stable["using"]&.to_sym,
}.compact
url urls_stable["url"], **url_spec
version json_formula["versions"]["stable"]
version Homebrew::API.internal_json_v3? ? json_formula["version"] : json_formula["versions"]["stable"]
sha256 urls_stable["checksum"] if urls_stable["checksum"].present?
instance_exec(:stable, &add_deps)
@ -289,7 +289,13 @@ module Formulary
end
end
if (bottles_stable = json_formula["bottle"]["stable"].presence)
bottles_stable = if Homebrew::API.internal_json_v3?
json_formula["bottle"]
else
json_formula["bottle"]["stable"]
end.presence
if bottles_stable
bottle do
if Homebrew::EnvConfig.bottle_domain == HOMEBREW_BOTTLE_DEFAULT_DOMAIN
root_url HOMEBREW_BOTTLE_DEFAULT_DOMAIN
@ -373,19 +379,26 @@ module Formulary
&.gsub(HOMEBREW_HOME_PLACEHOLDER, Dir.home)
end
@tap_git_head_string = json_formula["tap_git_head"]
@tap_git_head_string = if Homebrew::API.internal_json_v3?
Homebrew::API::Formula.tap_git_head
else
json_formula["tap_git_head"]
end
def tap_git_head
self.class.instance_variable_get(:@tap_git_head_string)
end
@oldnames_array = json_formula["oldnames"] || [json_formula["oldname"]].compact
def oldnames
self.class.instance_variable_get(:@oldnames_array)
end
unless Homebrew::API.internal_json_v3?
@oldnames_array = json_formula["oldnames"] || [json_formula["oldname"]].compact
def oldnames
self.class.instance_variable_get(:@oldnames_array)
end
@aliases_array = json_formula.fetch("aliases", [])
def aliases
self.class.instance_variable_get(:@aliases_array)
@aliases_array = json_formula.fetch("aliases", [])
def aliases
self.class.instance_variable_get(:@aliases_array)
end
end
@versioned_formulae_array = json_formula.fetch("versioned_formulae", [])

View File

@ -126,6 +126,7 @@ class Tap
@formula_dir = nil
@cask_dir = nil
@command_dir = nil
@formula_names = nil
@formula_files = nil
@cask_files = nil
@alias_dir = nil
@ -1101,6 +1102,8 @@ class CoreTap < AbstractCoreTap
@tap_migrations ||= if Homebrew::EnvConfig.no_install_from_api?
ensure_installed!
super
elsif Homebrew::API.internal_json_v3?
Homebrew::API::Formula.tap_migrations
else
migrations, = Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json",
stale_seconds: TAP_MIGRATIONS_STALE_SECONDS
@ -1188,6 +1191,23 @@ class CoreTap < AbstractCoreTap
hash[name] = Pathname(new_path) if existing_path.nil? || existing_path.to_s.length < new_path.length
end
end
sig { returns(T::Hash[String, T.untyped]) }
def to_api_hash
formulae_api_hash = formula_names.to_h do |name|
formula = Formulary.factory(name)
formula_hash = formula.to_hash_with_variations(hash_method: :to_api_hash)
[name, formula_hash]
end
{
"tap_git_head" => git_head,
"aliases" => alias_table,
"renames" => formula_renames,
"tap_migrations" => tap_migrations,
"formulae" => formulae_api_hash,
}
end
end
# A specialized {Tap} class for homebrew-cask.

View File

@ -0,0 +1,144 @@
# frozen_string_literal: true
RSpec.describe "Internal Tap JSON -- Formula" do
let(:internal_tap_json) { File.read(TEST_FIXTURE_DIR/"internal_tap_json/homebrew-core.json").chomp }
let(:tap_git_head) { "9977471165641744a829d3e494fa563407503297" }
context "when generating JSON", :needs_macos do
before do
cp_r(TEST_FIXTURE_DIR/"internal_tap_json/homebrew-core", Tap::TAP_DIRECTORY/"homebrew")
# NOTE: Symlinks can't be copied recursively so we create them manually here.
(Tap::TAP_DIRECTORY/"homebrew/homebrew-core").tap do |core_tap|
mkdir(core_tap/"Aliases")
ln_s(core_tap/"Formula/f/fennel.rb", core_tap/"Aliases/fennel-lang")
ln_s(core_tap/"Formula/p/ponyc.rb", core_tap/"Aliases/ponyc-lang")
end
end
it "creates the expected hash" do
api_hash = CoreTap.instance.to_api_hash
api_hash["tap_git_head"] = tap_git_head # tricky to mock
expect(JSON.pretty_generate(api_hash)).to eq(internal_tap_json)
end
end
context "when loading JSON" do
before do
ENV["HOMEBREW_INTERNAL_JSON_V3"] = "1"
ENV.delete("HOMEBREW_NO_INSTALL_FROM_API")
allow(Homebrew::API).to receive(:fetch_json_api_file)
.with("internal/v3/homebrew-core.jws.json")
.and_return([JSON.parse(internal_tap_json), false])
# `Tap.reverse_tap_migrations_renames` looks for renames in every
# tap so `CoreCaskTap.tap_migrations` gets called and tries to
# fetch stuff from the API. This just avoids errors.
allow(Homebrew::API).to receive(:fetch_json_api_file)
.with("cask_tap_migrations.jws.json", anything)
.and_return([{}, false])
# To allow `formula_names.txt` to be written to the cache.
(HOMEBREW_CACHE/"api").mkdir
Homebrew::API::Formula.clear_cache
end
after do
Homebrew::API::Formula.clear_cache
end
it "loads tap aliases" do
expect(CoreTap.instance.alias_table).to eq({
"fennel-lang" => "fennel",
"ponyc-lang" => "ponyc",
})
end
it "loads formula renames" do
expect(CoreTap.instance.formula_renames).to eq({
"advancemenu" => "advancemame",
"amtk" => "libgedit-amtk",
"annie" => "lux",
"antlr2" => "antlr@2",
"romanesco" => "fennel",
})
end
it "loads tap migrations" do
expect(CoreTap.instance.tap_migrations).to eq({
"adobe-air-sdk" => "homebrew/cask",
"android-ndk" => "homebrew/cask",
"android-platform-tools" => "homebrew/cask",
"android-sdk" => "homebrew/cask",
"app-engine-go-32" => "homebrew/cask/google-cloud-sdk",
})
end
it "loads tap git head" do
expect(Homebrew::API::Formula.tap_git_head)
.to eq(tap_git_head)
end
context "when loading formulae" do
let(:fennel_metadata) do
{
"dependencies" => ["lua"],
"desc" => "Lua Lisp Language",
"full_name" => "fennel",
"homepage" => "https://fennel-lang.org",
"license" => "MIT",
"name" => "fennel",
"ruby_source_path" => "Formula/f/fennel.rb",
"tap" => "homebrew/core",
"tap_git_head" => tap_git_head,
"versions" => { "bottle"=>true, "head"=>nil, "stable"=>"1.4.0" },
}
end
let(:ponyc_metadata) do
{
"desc" => "Object-oriented, actor-model, capabilities-secure programming language",
"full_name" => "ponyc",
"homepage" => "https://www.ponylang.io/",
"license" => "BSD-2-Clause",
"name" => "ponyc",
"ruby_source_path" => "Formula/p/ponyc.rb",
"tap" => "homebrew/core",
"tap_git_head" => tap_git_head,
# TODO: improve this API before we ship internal API v3 to users
"uses_from_macos" => [{ "llvm"=>[:build, :test] }, "zlib"],
"uses_from_macos_bounds" => [{}, {}],
"versions" => { "bottle"=>true, "head"=>nil, "stable"=>"0.58.1" },
}
end
it "loads fennel" do
fennel = Formulary.factory("fennel")
expect(fennel.to_hash).to include(**fennel_metadata)
end
it "loads fennel from rename" do
fennel = Formulary.factory("romanesco")
expect(fennel.to_hash).to include(**fennel_metadata)
end
it "loads fennel from alias" do
fennel = Formulary.factory("fennel-lang")
expect(fennel.to_hash).to include(**fennel_metadata)
end
it "loads ponyc" do
ponyc = Formulary.factory("ponyc")
expect(ponyc.to_hash).to include(**ponyc_metadata)
end
it "loads ponyc from alias" do
ponyc = Formulary.factory("ponyc-lang")
expect(ponyc.to_hash).to include(**ponyc_metadata)
end
end
end
end

View File

@ -0,0 +1,116 @@
{
"tap_git_head": "9977471165641744a829d3e494fa563407503297",
"aliases": {
"fennel-lang": "fennel",
"ponyc-lang": "ponyc"
},
"renames": {
"advancemenu": "advancemame",
"amtk": "libgedit-amtk",
"annie": "lux",
"antlr2": "antlr@2",
"romanesco": "fennel"
},
"tap_migrations": {
"adobe-air-sdk": "homebrew/cask",
"android-ndk": "homebrew/cask",
"android-platform-tools": "homebrew/cask",
"android-sdk": "homebrew/cask",
"app-engine-go-32": "homebrew/cask/google-cloud-sdk"
},
"formulae": {
"fennel": {
"desc": "Lua Lisp Language",
"license": "MIT",
"homepage": "https://fennel-lang.org",
"urls": {
"stable": {
"url": "https://github.com/bakpakin/Fennel/archive/refs/tags/1.4.0.tar.gz",
"checksum": "161eb7f17f86e95de09070214d042fb25372f71ad266f451431f3109e87965c7"
}
},
"post_install_defined": false,
"ruby_source_path": "Formula/f/fennel.rb",
"ruby_source_sha256": "5856e655fd1cea11496d67bc27fb14fee5cfbdea63c697c3773c7f247581197d",
"dependencies": [
"lua"
],
"version": "1.4.0",
"bottle": {
"files": {
"all": {
"cellar": ":any_skip_relocation",
"sha256": "f46028597883cbc38864c61bd3fa402da9cb90ce97415d51a7b5279bc17f7bd0"
}
}
}
},
"ponyc": {
"desc": "Object-oriented, actor-model, capabilities-secure programming language",
"license": "BSD-2-Clause",
"homepage": "https://www.ponylang.io/",
"urls": {
"stable": {
"url": "https://github.com/ponylang/ponyc.git",
"tag": "0.58.1",
"revision": "fe3895eb4af494bf36d7690641bdfb5755db8350"
}
},
"post_install_defined": false,
"ruby_source_path": "Formula/p/ponyc.rb",
"ruby_source_sha256": "81d51c25d18710191beb62f9f380bae3d878aad815a65ec1ee2a3b132c1fadb3",
"build_dependencies": [
"cmake",
"python@3.12"
],
"uses_from_macos": [
{
"llvm": [
"build",
"test"
]
},
"zlib"
],
"uses_from_macos_bounds": [
{
},
{
}
],
"version": "0.58.1",
"bottle": {
"files": {
"arm64_sonoma": {
"cellar": ":any_skip_relocation",
"sha256": "e3aecfcf02aea56d53d82691e2ad7a780f771023d7070271bfce96b17439a34d"
},
"arm64_ventura": {
"cellar": ":any_skip_relocation",
"sha256": "6ff83717191e16e4f852fb3be8f838afba312cc39e601bb5cebd2a618a328658"
},
"arm64_monterey": {
"cellar": ":any_skip_relocation",
"sha256": "25c91bce200583a96f4cea34f31393c8f10eadcab363cc7d4d864d15f5f97e25"
},
"sonoma": {
"cellar": ":any_skip_relocation",
"sha256": "5f4c550ce33e2970e0ada18a409755fa62936181289a21c15582ff80343866b6"
},
"ventura": {
"cellar": ":any_skip_relocation",
"sha256": "f26c799f45013685da779bf2008ebe1907f9b3a93d5f260ce271a3f3b628da50"
},
"monterey": {
"cellar": ":any_skip_relocation",
"sha256": "1cff10d068b36b18b253d235424c4f5aef71ff9ee44f2522c4b041dd4383ec30"
},
"x86_64_linux": {
"cellar": ":any_skip_relocation",
"sha256": "ab49318d75eed3ee932c8e5add22f252ec0c852aad94945022877f926e93899f"
}
}
}
}
}
}

View File

@ -0,0 +1,25 @@
class Fennel < Formula
desc "Lua Lisp Language"
homepage "https://fennel-lang.org"
url "https://github.com/bakpakin/Fennel/archive/refs/tags/1.4.0.tar.gz"
sha256 "161eb7f17f86e95de09070214d042fb25372f71ad266f451431f3109e87965c7"
license "MIT"
bottle do
sha256 cellar: :any_skip_relocation, all: "f46028597883cbc38864c61bd3fa402da9cb90ce97415d51a7b5279bc17f7bd0"
end
depends_on "lua"
def install
system "make"
bin.install "fennel"
lua = Formula["lua"]
(share/"lua"/lua.version.major_minor).install "fennel.lua"
end
test do
assert_match "hello, world!", shell_output("#{bin}/fennel -e '(print \"hello, world!\")'")
end
end

View File

@ -0,0 +1,60 @@
class Ponyc < Formula
desc "Object-oriented, actor-model, capabilities-secure programming language"
homepage "https://www.ponylang.io/"
url "https://github.com/ponylang/ponyc.git",
tag: "0.58.1",
revision: "fe3895eb4af494bf36d7690641bdfb5755db8350"
license "BSD-2-Clause"
bottle do
sha256 cellar: :any_skip_relocation, arm64_sonoma: "e3aecfcf02aea56d53d82691e2ad7a780f771023d7070271bfce96b17439a34d"
sha256 cellar: :any_skip_relocation, arm64_ventura: "6ff83717191e16e4f852fb3be8f838afba312cc39e601bb5cebd2a618a328658"
sha256 cellar: :any_skip_relocation, arm64_monterey: "25c91bce200583a96f4cea34f31393c8f10eadcab363cc7d4d864d15f5f97e25"
sha256 cellar: :any_skip_relocation, sonoma: "5f4c550ce33e2970e0ada18a409755fa62936181289a21c15582ff80343866b6"
sha256 cellar: :any_skip_relocation, ventura: "f26c799f45013685da779bf2008ebe1907f9b3a93d5f260ce271a3f3b628da50"
sha256 cellar: :any_skip_relocation, monterey: "1cff10d068b36b18b253d235424c4f5aef71ff9ee44f2522c4b041dd4383ec30"
sha256 cellar: :any_skip_relocation, x86_64_linux: "ab49318d75eed3ee932c8e5add22f252ec0c852aad94945022877f926e93899f"
end
depends_on "cmake" => :build
depends_on "python@3.12" => :build
uses_from_macos "llvm" => [:build, :test]
uses_from_macos "zlib"
# We use LLVM to work around an error while building bundled `google-benchmark` with GCC
fails_with :gcc do
cause <<-EOS
.../src/gbenchmark/src/thread_manager.h:50:31: error: expected ')' before '(' token
50 | GUARDED_BY(GetBenchmarkMutex()) Result results;
| ^
EOS
end
def install
inreplace "CMakeLists.txt", "PONY_COMPILER=\"${CMAKE_C_COMPILER}\"", "PONY_COMPILER=\"#{ENV.cc}\"" if OS.linux?
ENV["CMAKE_FLAGS"] = "-DCMAKE_OSX_SYSROOT=#{MacOS.sdk_path}" if OS.mac?
ENV["MAKEFLAGS"] = "build_flags=-j#{ENV.make_jobs}"
system "make", "libs"
system "make", "configure"
system "make", "build"
system "make", "install", "DESTDIR=#{prefix}"
end
test do
# ENV["CC"] returns llvm_clang, which does not work in a test block.
ENV.clang
system "#{bin}/ponyc", "-rexpr", "#{prefix}/packages/stdlib"
(testpath/"test/main.pony").write <<~EOS
actor Main
new create(env: Env) =>
env.out.print("Hello World!")
EOS
system "#{bin}/ponyc", "test"
assert_equal "Hello World!", shell_output("./test1").strip
end
end

View File

@ -0,0 +1,7 @@
{
"advancemenu": "advancemame",
"amtk": "libgedit-amtk",
"annie": "lux",
"antlr2": "antlr@2",
"romanesco": "fennel"
}

View File

@ -0,0 +1,7 @@
{
"adobe-air-sdk": "homebrew/cask",
"android-ndk": "homebrew/cask",
"android-platform-tools": "homebrew/cask",
"android-sdk": "homebrew/cask",
"app-engine-go-32": "homebrew/cask/google-cloud-sdk"
}