From b44ec56267366ce22fc90459bcd2f671b8d01748 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 5 Jun 2025 02:08:29 -0400 Subject: [PATCH 1/4] Reduce the internal formula API to contain the bare minimum data --- Library/Homebrew/dev-cmd/generate-cask-api.rb | 4 +--- Library/Homebrew/dev-cmd/generate-formula-api.rb | 12 ++++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/dev-cmd/generate-cask-api.rb b/Library/Homebrew/dev-cmd/generate-cask-api.rb index 48bcfdbd40..9f639aec52 100644 --- a/Library/Homebrew/dev-cmd/generate-cask-api.rb +++ b/Library/Homebrew/dev-cmd/generate-cask-api.rb @@ -75,9 +75,7 @@ module Homebrew Homebrew::API.merge_variations(cask, bottle_tag:) end - unless args.dry_run? - File.write("api/internal/cask.#{bottle_tag}.json", JSON.pretty_generate(variation_casks)) - end + File.write("api/internal/cask.#{bottle_tag}.json", JSON.generate(variation_casks)) unless args.dry_run? end end end diff --git a/Library/Homebrew/dev-cmd/generate-formula-api.rb b/Library/Homebrew/dev-cmd/generate-formula-api.rb index 04a2077f9d..041b815f2d 100644 --- a/Library/Homebrew/dev-cmd/generate-formula-api.rb +++ b/Library/Homebrew/dev-cmd/generate-formula-api.rb @@ -70,11 +70,19 @@ module Homebrew OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag| variation_formulae = all_formulae.map do |_, formula| - Homebrew::API.merge_variations(formula, bottle_tag:) + formula = Homebrew::API.merge_variations(formula, bottle_tag:) + + version = Version.new(formula.dig("versions", "stable")) + pkg_version = PkgVersion.new(version, formula["revision"]) + rebuild = formula.dig("bottle", "stable", "rebuild") || 0 + sha256 = formula.dig("bottle", "stable", "files", :all, "sha256") + sha256 ||= formula.dig("bottle", "stable", "files", bottle_tag.to_sym, "sha256") + + [formula["name"], pkg_version.to_s, rebuild, sha256] end unless args.dry_run? - File.write("api/internal/formula.#{bottle_tag}.json", JSON.pretty_generate(variation_formulae)) + File.write("api/internal/formula.#{bottle_tag}.json", JSON.generate(variation_formulae)) end end end From e47bc2fb86dbe037b8f566c7a19305ce704c7ae8 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 5 Jun 2025 03:15:44 -0400 Subject: [PATCH 2/4] Use hashes of arrays instead --- Library/Homebrew/dev-cmd/generate-formula-api.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/generate-formula-api.rb b/Library/Homebrew/dev-cmd/generate-formula-api.rb index 041b815f2d..a9afe47ee7 100644 --- a/Library/Homebrew/dev-cmd/generate-formula-api.rb +++ b/Library/Homebrew/dev-cmd/generate-formula-api.rb @@ -69,7 +69,7 @@ module Homebrew File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run? OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag| - variation_formulae = all_formulae.map do |_, formula| + variation_formulae = all_formulae.to_h do |name, formula| formula = Homebrew::API.merge_variations(formula, bottle_tag:) version = Version.new(formula.dig("versions", "stable")) @@ -78,7 +78,7 @@ module Homebrew sha256 = formula.dig("bottle", "stable", "files", :all, "sha256") sha256 ||= formula.dig("bottle", "stable", "files", bottle_tag.to_sym, "sha256") - [formula["name"], pkg_version.to_s, rebuild, sha256] + [name, [pkg_version.to_s, rebuild, sha256]] end unless args.dry_run? From e83dc1cb3c16812a61aac37f3594cbf760c7013b Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 5 Jun 2025 11:32:47 -0400 Subject: [PATCH 3/4] Fallback to older bottle hashes when viable --- .../Homebrew/dev-cmd/generate-formula-api.rb | 32 ++++++++++- .../test/dev-cmd/generate-formula-api_spec.rb | 54 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/generate-formula-api.rb b/Library/Homebrew/dev-cmd/generate-formula-api.rb index a9afe47ee7..747f838e17 100644 --- a/Library/Homebrew/dev-cmd/generate-formula-api.rb +++ b/Library/Homebrew/dev-cmd/generate-formula-api.rb @@ -75,8 +75,7 @@ module Homebrew version = Version.new(formula.dig("versions", "stable")) pkg_version = PkgVersion.new(version, formula["revision"]) rebuild = formula.dig("bottle", "stable", "rebuild") || 0 - sha256 = formula.dig("bottle", "stable", "files", :all, "sha256") - sha256 ||= formula.dig("bottle", "stable", "files", bottle_tag.to_sym, "sha256") + sha256 = newest_bottle_sha256(formula, bottle_tag) [name, [pkg_version.to_s, rebuild, sha256]] end @@ -88,6 +87,35 @@ module Homebrew end end + sig { + params(formula_json: T::Hash[String, T.untyped], bottle_tag: Utils::Bottles::Tag).returns(T.nilable(String)) + } + def newest_bottle_sha256(formula_json, bottle_tag) + available_tags = formula_json.dig("bottle", "stable", "files")&.keys&.map(&:to_sym) + return unless available_tags + + return formula_json.dig("bottle", "stable", "files", :all, "sha256") if available_tags.include? :all + + if available_tags.include? bottle_tag.to_sym + return formula_json.dig("bottle", "stable", "files", bottle_tag.to_sym, "sha256") + end + + return unless bottle_tag.macos? + + # If the actual tag is not available, find the newest tag with matching arch that's older than the actual tag + newest_viable_macos_tag = available_tags.filter_map do |tag_sym| + tag = Utils::Bottles::Tag.from_symbol(tag_sym) + next unless tag.macos? + next if tag.arch != bottle_tag.arch + next if tag.to_macos_version > bottle_tag.to_macos_version + + tag + end.max_by(&:to_macos_version) + return unless newest_viable_macos_tag + + formula_json.dig("bottle", "stable", "files", newest_viable_macos_tag.to_sym, "sha256") + end + private sig { params(title: String).returns(String) } diff --git a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb index 74136d6645..675d937ed7 100644 --- a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb +++ b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb @@ -5,4 +5,58 @@ require "dev-cmd/generate-formula-api" RSpec.describe Homebrew::DevCmd::GenerateFormulaApi do it_behaves_like "parseable arguments" + + describe "#newest_bottle_sha256" do + subject(:generate_formula_api) { described_class.new [] } + + def make_json(bottles) + json = { + "bottle" => { + "stable" => { + "files" => {}, + }, + }, + } + bottles.each do |tag, sha256| + json["bottle"]["stable"]["files"][tag] = { "sha256" => sha256 } + end + json + end + + expected_sha256s = { + arm64_sequoia: "abc123", + arm64_sonoma: "abc123", + arm64_ventura: "ghi789", + arm64_monterey: nil, + sequoia: "jkl012", + sonoma: "jkl012", + ventura: "mno345", + monterey: "mno345", + x86_64_linux: "pqr678", + arm64_linux: nil, + }.transform_keys do |tag| + Utils::Bottles::Tag.from_symbol(tag) + end + + let(:all_json) { make_json all: "abc123" } + let(:standard_json) do + make_json arm64_sonoma: "abc123", + arm64_ventura: "ghi789", + sonoma: "jkl012", + big_sur: "mno345", + x86_64_linux: "pqr678" + end + + it "returns the sha256 for the :all tag on all systems" do + expected_sha256s.each_key do |tag| + expect(generate_formula_api.newest_bottle_sha256(all_json, tag)).to eq("abc123") + end + end + + expected_sha256s.each_key do |tag| + it "returns the corrent sha256 for #{tag}" do + expect(generate_formula_api.newest_bottle_sha256(standard_json, tag)).to eq(expected_sha256s[tag]) + end + end + end end From 73b64390b3d296f78ec2331e00ac74282c6143f4 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Thu, 5 Jun 2025 12:38:15 -0400 Subject: [PATCH 4/4] Use existing `Utils::Bottles::Collector#specification_for` method --- .../Homebrew/dev-cmd/generate-formula-api.rb | 38 +++---------- .../Homebrew/extend/os/mac/utils/bottles.rb | 2 +- .../test/dev-cmd/generate-formula-api_spec.rb | 54 ------------------- 3 files changed, 9 insertions(+), 85 deletions(-) diff --git a/Library/Homebrew/dev-cmd/generate-formula-api.rb b/Library/Homebrew/dev-cmd/generate-formula-api.rb index 747f838e17..5741ce6872 100644 --- a/Library/Homebrew/dev-cmd/generate-formula-api.rb +++ b/Library/Homebrew/dev-cmd/generate-formula-api.rb @@ -75,7 +75,14 @@ module Homebrew version = Version.new(formula.dig("versions", "stable")) pkg_version = PkgVersion.new(version, formula["revision"]) rebuild = formula.dig("bottle", "stable", "rebuild") || 0 - sha256 = newest_bottle_sha256(formula, bottle_tag) + + bottle_collector = Utils::Bottles::Collector.new + formula.dig("bottle", "stable", "files")&.each do |tag, data| + tag = Utils::Bottles::Tag.from_symbol(tag) + bottle_collector.add tag, checksum: Checksum.new(data["sha256"]), cellar: :any + end + + sha256 = bottle_collector.specification_for(bottle_tag)&.checksum&.to_s [name, [pkg_version.to_s, rebuild, sha256]] end @@ -87,35 +94,6 @@ module Homebrew end end - sig { - params(formula_json: T::Hash[String, T.untyped], bottle_tag: Utils::Bottles::Tag).returns(T.nilable(String)) - } - def newest_bottle_sha256(formula_json, bottle_tag) - available_tags = formula_json.dig("bottle", "stable", "files")&.keys&.map(&:to_sym) - return unless available_tags - - return formula_json.dig("bottle", "stable", "files", :all, "sha256") if available_tags.include? :all - - if available_tags.include? bottle_tag.to_sym - return formula_json.dig("bottle", "stable", "files", bottle_tag.to_sym, "sha256") - end - - return unless bottle_tag.macos? - - # If the actual tag is not available, find the newest tag with matching arch that's older than the actual tag - newest_viable_macos_tag = available_tags.filter_map do |tag_sym| - tag = Utils::Bottles::Tag.from_symbol(tag_sym) - next unless tag.macos? - next if tag.arch != bottle_tag.arch - next if tag.to_macos_version > bottle_tag.to_macos_version - - tag - end.max_by(&:to_macos_version) - return unless newest_viable_macos_tag - - formula_json.dig("bottle", "stable", "files", newest_viable_macos_tag.to_sym, "sha256") - end - private sig { params(title: String).returns(String) } diff --git a/Library/Homebrew/extend/os/mac/utils/bottles.rb b/Library/Homebrew/extend/os/mac/utils/bottles.rb index 754aeaec64..9b4c71a914 100644 --- a/Library/Homebrew/extend/os/mac/utils/bottles.rb +++ b/Library/Homebrew/extend/os/mac/utils/bottles.rb @@ -47,7 +47,7 @@ module Utils return if tag_version.blank? tags.find do |candidate| - next if candidate.arch != tag.arch + next if candidate.standardized_arch != tag.standardized_arch candidate.to_macos_version <= tag_version rescue MacOSVersion::Error diff --git a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb index 675d937ed7..74136d6645 100644 --- a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb +++ b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb @@ -5,58 +5,4 @@ require "dev-cmd/generate-formula-api" RSpec.describe Homebrew::DevCmd::GenerateFormulaApi do it_behaves_like "parseable arguments" - - describe "#newest_bottle_sha256" do - subject(:generate_formula_api) { described_class.new [] } - - def make_json(bottles) - json = { - "bottle" => { - "stable" => { - "files" => {}, - }, - }, - } - bottles.each do |tag, sha256| - json["bottle"]["stable"]["files"][tag] = { "sha256" => sha256 } - end - json - end - - expected_sha256s = { - arm64_sequoia: "abc123", - arm64_sonoma: "abc123", - arm64_ventura: "ghi789", - arm64_monterey: nil, - sequoia: "jkl012", - sonoma: "jkl012", - ventura: "mno345", - monterey: "mno345", - x86_64_linux: "pqr678", - arm64_linux: nil, - }.transform_keys do |tag| - Utils::Bottles::Tag.from_symbol(tag) - end - - let(:all_json) { make_json all: "abc123" } - let(:standard_json) do - make_json arm64_sonoma: "abc123", - arm64_ventura: "ghi789", - sonoma: "jkl012", - big_sur: "mno345", - x86_64_linux: "pqr678" - end - - it "returns the sha256 for the :all tag on all systems" do - expected_sha256s.each_key do |tag| - expect(generate_formula_api.newest_bottle_sha256(all_json, tag)).to eq("abc123") - end - end - - expected_sha256s.each_key do |tag| - it "returns the corrent sha256 for #{tag}" do - expect(generate_formula_api.newest_bottle_sha256(standard_json, tag)).to eq(expected_sha256s[tag]) - end - end - end end