Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Library/Homebrew/api/formula_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def self.from_hash(formula_hash)
const :keg_only_args, T::Array[T.any(String, Symbol)], default: []
const :license, SPDX::LicenseExpression
const :link_overwrite_paths, T::Array[String], default: []
const :local_patch_checksums, T::Hash[String, String], default: {}
const :no_autobump_args, T::Hash[Symbol, T.any(String, Symbol)], default: {}
const :oldnames, T::Array[String], default: []
const :post_install_defined, T::Boolean, default: false
Expand Down
16 changes: 16 additions & 0 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3091,6 +3091,21 @@ def ruby_source_checksum
Checksum.new(Digest::SHA256.file(path).hexdigest) if path.exist?
end

# API generation requires every patch checksum; other metadata consumers can omit unreadable patches.
sig { returns(T::Hash[String, String]) }
def local_patch_checksums
[stable, head].compact.flat_map do |spec|
(spec.patches + spec.resources.values.flat_map(&:patches)).grep(LocalPatch).filter_map do |patch|
[patch.file.to_s, Digest::SHA256.hexdigest(patch.contents)]
rescue ArgumentError, SystemCallError => e
raise if Formula.generating_hash?

opoo "#{full_name}: cannot checksum #{patch.file}: #{e}"
Comment thread
MikeMcQuaid marked this conversation as resolved.
nil
end
end.to_h
end
Comment thread
MikeMcQuaid marked this conversation as resolved.

sig { params(dependables: T::Hash[Symbol, T.untyped]).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
def merge_spec_dependables(dependables)
# We have a hash of specs names (stable/head) to dependency lists.
Expand Down Expand Up @@ -3126,6 +3141,7 @@ def to_hash
},
"urls" => urls_hash,
"patches" => serialized_patches,
"local_patch_checksums" => local_patch_checksums,
"revision" => revision,
"version_scheme" => version_scheme,
"compatibility_version" => compatibility_version,
Expand Down
4 changes: 4 additions & 0 deletions Library/Homebrew/formulary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ def self.load_formula_from_struct!(name, formula_struct, api_source:, tap_git_he
checksum = self.class.instance_variable_get(:@ruby_source_checksum_string)
Checksum.new(checksum) if checksum
end

define_method(:local_patch_checksums) do
formula_struct.local_patch_checksums
end
end

mod.const_set(class_name, klass)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@
expect(struct.stable_patches).to eq hash["patches"]
end

it "preserves local patch checksums through internal API serialisation" do
hash["local_patch_checksums"] = { "Patches/foo.diff" => TEST_SHA256 }

expect(struct.serialize["local_patch_checksums"])
.to eq(hash["local_patch_checksums"])
end

context "when input contains placeholders" do
let(:caveats_with_placeholder) { "Message about $HOMEBREW_CELLAR/foo/1.0.0/bin/foo" }
let(:log_path_with_placeholder) { "$HOMEBREW_PREFIX/var/log/foo.log" }
Expand Down
75 changes: 75 additions & 0 deletions Library/Homebrew/test/formula_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,81 @@ def service_name = "custom.override.name"
end

describe "#to_hash patches" do
before do
allow_any_instance_of(LocalPatch).to receive(:contents).and_return("patch contents")
end

it "includes checksums for stable, HEAD and resource patch files" do
f = formula "foo" do
T.bind(self, T.class_of(Formula))
url "foo-1.0"
patch do
file "Patches/stable.diff"
end
resource "bar" do
url "bar-1.0"
patch do
file "Patches/resource.diff"
end
end
head do
url "https://example.com/foo.git"
patch do
file "Patches/head.diff"
end
resource "baz" do
url "baz-1.0"
patch do
file "Patches/head-resource.diff"
end
end
end
end

expect(f.to_hash["local_patch_checksums"]).to eq(
%w[stable resource head head-resource].to_h do |name|
["Patches/#{name}.diff", Digest::SHA256.hexdigest("patch contents")]
end,
)
end

shared_examples "an unreadable local patch" do |error|
let(:f) do
formula "foo" do
T.bind(self, T.class_of(Formula))
url "foo-1.0"
patch do
file "Patches/unreadable.diff"
end
patch do
file "Patches/valid.diff"
end
end
end

before do
allow(f.patchlist.fetch(0)).to receive(:contents).and_raise(error, "unreadable patch")
end

it "serialises checksums for the remaining patches" do
expect(f.to_hash["local_patch_checksums"])
.to eq("Patches/valid.diff" => Digest::SHA256.hexdigest("patch contents"))
end

it "warns about the omitted checksum" do
expect { f.to_hash }.to output(%r{cannot checksum Patches/unreadable.diff}).to_stderr
end

it "rejects unreadable patches during API generation" do
allow(described_class).to receive(:generating_hash?).and_return(true)

expect { f.to_hash }.to raise_error(error, /unreadable patch/)
end
end

it_behaves_like "an unreadable local patch", ArgumentError
it_behaves_like "an unreadable local patch", Errno::EACCES

it "serialises an external patch" do
f = formula "foo" do
T.bind(self, T.class_of(Formula))
Expand Down
8 changes: 8 additions & 0 deletions Library/Homebrew/test/formulary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,14 @@ def formula_json_contents(extra_items = {})
expect(formula.loaded_from_internal_api?).to be true
end

it "loads local patch checksums from the API" do
allow(Homebrew::API::Formula).to receive(:all_formulae)
.and_return formula_json_contents("local_patch_checksums" => { "Patches/foo.diff" => TEST_SHA256 })

expect(described_class.factory(formula_name).local_patch_checksums)
.to eq("Patches/foo.diff" => TEST_SHA256)
end

it "returns a Formula with a compatibility_version" do
allow(Homebrew::API::Formula).to receive(:all_formulae)
.and_return formula_json_contents("compatibility_version" => 2)
Expand Down
Loading