diff --git a/Library/Homebrew/cmd/fetch.rb b/Library/Homebrew/cmd/fetch.rb index 6bd826c48d25e..256493720e0bd 100644 --- a/Library/Homebrew/cmd/fetch.rb +++ b/Library/Homebrew/cmd/fetch.rb @@ -47,6 +47,8 @@ class FetchCmd < AbstractCommand switch "--force-bottle", description: "Download a bottle if it exists for the current or newest version of macOS, " \ "even if it would not be used during installation." + switch "--only-manifests", + description: "Only download GitHub Packages manifests." switch "--[no-]quarantine", description: "Disable/enable quarantining of downloads (default: enabled).", env: :cask_opts_quarantine @@ -62,6 +64,7 @@ class FetchCmd < AbstractCommand conflicts "--cask", "--build-bottle" conflicts "--cask", "--force-bottle" conflicts "--cask", "--bottle-tag" + conflicts "--cask", "--only-manifests" conflicts "--formula", "--cask" conflicts "--os", "--bottle-tag" conflicts "--arch", "--bottle-tag" @@ -173,7 +176,7 @@ def run if (manifest_resource = bottle.github_packages_manifest_resource) fetch_downloadable(manifest_resource) end - fetch_downloadable(bottle) + fetch_downloadable(bottle) unless args.only_manifests? rescue Interrupt raise rescue => e @@ -187,7 +190,7 @@ def run end end - next if fetched_bottle + next if fetched_bottle || args.only_manifests? fetch_downloadable(formula.resource) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 48749f0ac3186..90f520983f3df 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2623,6 +2623,13 @@ def bottle_hash(compact_for_api: false) end file_hash["sha256"] = checksum + if (bottle = bottle_for_tag(tag)) + bottle_size = bottle.bottle_size + installed_size = bottle.installed_size + file_hash["bottle_size"] = bottle_size if bottle_size + file_hash["installed_size"] = installed_size if installed_size + end + hash["files"][tag.to_sym] = file_hash end hash diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 67ae6b78ad7c7..03c6e42861573 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -350,7 +350,8 @@ def self.load_formula_from_api(name, flags:) rebuild bottles_stable["rebuild"] bottles_stable["files"].each do |tag, bottle_spec| cellar = Formulary.convert_to_string_or_symbol bottle_spec["cellar"] - sha256 cellar:, tag.to_sym => bottle_spec["sha256"] + sizes = bottle_spec.slice("bottle_size", "installed_size").transform_keys!(&:to_sym) + sha256 cellar:, tag.to_sym => bottle_spec["sha256"], **sizes end end end diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index 51cc555231f76..05c3834742f4b 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -306,6 +306,7 @@ class Error < RuntimeError; end def initialize(bottle) super("#{bottle.name}_bottle_manifest") @bottle = bottle + @manifest_annotations = nil end def verify_download_integrity(_filename) @@ -314,6 +315,29 @@ def verify_download_integrity(_filename) end def tab + tab = manifest_annotations["sh.brew.tab"] + raise Error, "Couldn't find tab from manifest." if tab.blank? + + begin + JSON.parse(tab) + rescue JSON::ParserError + raise Error, "Couldn't parse tab JSON." + end + end + + def bottle_size + manifest_annotations["sh.brew.bottle.size"]&.to_i + end + + def installed_size + manifest_annotations["sh.brew.bottle.installed_size"]&.to_i + end + + private + + def manifest_annotations + return @manifest_annotations if @manifest_annotations.present? + json = begin JSON.parse(cached_download.read) rescue JSON::ParserError @@ -336,14 +360,7 @@ def tab end raise Error, "Couldn't find manifest matching bottle checksum." if manifest_annotations.blank? - tab = manifest_annotations["sh.brew.tab"] - raise Error, "Couldn't find tab from manifest." if tab.blank? - - begin - JSON.parse(tab) - rescue JSON::ParserError - raise Error, "Couldn't parse tab JSON." - end + @manifest_annotations = manifest_annotations end end diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 5c4bd6f911c49..30c810cb92dc7 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -367,6 +367,8 @@ def initialize(formula, spec, tag = nil) @tag = tag_spec.tag @cellar = tag_spec.cellar @rebuild = spec.rebuild + @bottle_size = tag_spec.bottle_size + @installed_size = tag_spec.installed_size @resource.version(formula.pkg_version.to_s) @resource.checksum = tag_spec.checksum @@ -436,6 +438,22 @@ def tab_attributes {} end + sig { returns(T.nilable(Integer)) } + def bottle_size + return @bottle_size unless @bottle_size.nil? + return unless (resource = github_packages_manifest_resource)&.downloaded? + + resource.bottle_size + end + + sig { returns(T.nilable(Integer)) } + def installed_size + return @installed_size unless @installed_size.nil? + return unless (resource = github_packages_manifest_resource)&.downloaded? + + resource.installed_size + end + sig { returns(Filename) } def filename Filename.create(resource.owner, @tag, @spec.rebuild) @@ -596,7 +614,7 @@ def sha256(hash) cellar ||= tag.default_cellar - collector.add(tag, checksum: Checksum.new(digest), cellar:) + collector.add(tag, checksum: Checksum.new(digest), cellar:, **hash.slice(:bottle_size, :installed_size)) end sig { diff --git a/Library/Homebrew/utils/bottles.rb b/Library/Homebrew/utils/bottles.rb index 73c6b014bee52..5c572f874d118 100644 --- a/Library/Homebrew/utils/bottles.rb +++ b/Library/Homebrew/utils/bottles.rb @@ -265,10 +265,18 @@ class TagSpecification sig { returns(T.any(Symbol, String)) } attr_reader :cellar - def initialize(tag:, checksum:, cellar:) + sig { returns(T.nilable(Integer)) } + attr_reader :bottle_size + + sig { returns(T.nilable(Integer)) } + attr_reader :installed_size + + def initialize(tag:, checksum:, cellar:, bottle_size: nil, installed_size: nil) @tag = tag @checksum = checksum @cellar = cellar + @bottle_size = bottle_size + @installed_size = installed_size end def ==(other) @@ -294,9 +302,17 @@ def ==(other) end alias eql? == - sig { params(tag: Utils::Bottles::Tag, checksum: Checksum, cellar: T.any(Symbol, String)).void } - def add(tag, checksum:, cellar:) - spec = Utils::Bottles::TagSpecification.new(tag:, checksum:, cellar:) + sig { + params( + tag: Utils::Bottles::Tag, + checksum: Checksum, + cellar: T.any(Symbol, String), + bottle_size: T.nilable(Integer), + installed_size: T.nilable(Integer), + ).void + } + def add(tag, checksum:, cellar:, bottle_size: nil, installed_size: nil) + spec = Utils::Bottles::TagSpecification.new(tag:, checksum:, cellar:, bottle_size:, installed_size:) @tag_specs[tag] = spec end