Skip to content

Commit 00d8ae2

Browse files
committed
Fix spurious test-bot missing :all bottle warning
Signed-off-by: Patrick Linnane <patrick@linnane.io>
1 parent 3d58a45 commit 00d8ae2

2 files changed

Lines changed: 183 additions & 42 deletions

File tree

Library/Homebrew/test/test_bot/formulae_spec.rb

Lines changed: 152 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -101,54 +101,84 @@
101101
end
102102

103103
describe "#annotate_missing_all_bottle" do
104+
sig { params(formula_path: Pathname, tag: Utils::Bottles::Tag, sha256: String).void }
105+
def write_platform_bottle_formula(formula_path, tag, sha256)
106+
formula_path.dirname.mkpath
107+
formula_path.write <<~RUBY
108+
class Foo < Formula
109+
desc "Foo"
110+
homepage "https://example.com"
111+
url "foo-1.0"
112+
sha256 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
113+
114+
bottle do
115+
sha256 cellar: :any_skip_relocation, #{tag.to_sym}: "#{sha256}"
116+
end
117+
end
118+
RUBY
119+
end
120+
121+
sig {
122+
params(
123+
tap_path: Pathname,
124+
tag: T.any(String, Utils::Bottles::Tag),
125+
sha256: String,
126+
cellar: String,
127+
).void
128+
}
129+
def write_bottle_json(tap_path, tag, sha256, cellar: "any_skip_relocation")
130+
(tap_path/"foo--1.0.#{tag}.bottle.json").write JSON.generate(
131+
"foo" => {
132+
"bottle" => {
133+
"cellar" => cellar,
134+
"tags" => {
135+
tag.to_s => {
136+
"sha256" => sha256,
137+
},
138+
},
139+
},
140+
},
141+
)
142+
end
143+
144+
sig { params(formula_path: Pathname).returns(Formula) }
145+
def all_bottle_formula(formula_path)
146+
formula("foo", path: formula_path) do
147+
T.bind(self, T.class_of(Formula))
148+
url "foo-1.0"
149+
bottle do
150+
sha256 cellar: :any_skip_relocation,
151+
all: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
152+
end
153+
end
154+
end
155+
156+
sig { params(tap_path: Pathname, tmpdir: String).returns(Homebrew::TestBot::Formulae) }
157+
def formulae_test_bot(tap_path, tmpdir)
158+
described_class.new(
159+
tap: instance_double(Tap, path: tap_path), git: "git", dry_run: true, fail_fast: false, verbose: false,
160+
output_paths: {
161+
bottle: Pathname.new("#{tmpdir}/bottle.txt"),
162+
linkage: Pathname.new("#{tmpdir}/linkage.txt"),
163+
skipped_or_failed_formulae: Pathname.new("#{tmpdir}/skipped.txt"),
164+
}
165+
)
166+
end
167+
104168
it "writes a warning annotation for a platform-specific bottle replacing an all bottle" do
105169
Dir.mktmpdir do |tmpdir|
106170
tag = Utils::Bottles.tag
107171
sha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
172+
other_tag = (tag.to_s == "arm64_tahoe") ? "tahoe" : "arm64_tahoe"
173+
other_sha256 = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
108174
tap_path = Pathname(tmpdir)
109175
formula_path = tap_path/"Formula/foo.rb"
110-
formula_path.dirname.mkpath
111-
formula_path.write <<~RUBY
112-
class Foo < Formula
113-
desc "Foo"
114-
homepage "https://example.com"
115-
url "foo-1.0"
116-
sha256 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
117-
118-
bottle do
119-
sha256 cellar: :any_skip_relocation, #{tag.to_sym}: "#{sha256}"
120-
end
121-
end
122-
RUBY
123-
(tap_path/"foo--1.0.#{tag}.bottle.json").write JSON.generate(
124-
"foo" => {
125-
"bottle" => {
126-
"tags" => {
127-
tag.to_s => {
128-
"cellar" => "any_skip_relocation",
129-
"sha256" => sha256,
130-
},
131-
},
132-
},
133-
},
134-
)
176+
write_platform_bottle_formula(formula_path, tag, sha256)
177+
write_bottle_json(tap_path, tag, sha256)
178+
write_bottle_json(tap_path, other_tag, other_sha256)
135179

136-
old_formula = formula("foo", path: formula_path) do
137-
T.bind(self, T.class_of(Formula))
138-
url "foo-1.0"
139-
bottle do
140-
sha256 cellar: :any_skip_relocation,
141-
all: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
142-
end
143-
end
144-
formulae = described_class.new(
145-
tap: instance_double(Tap, path: tap_path), git: "git", dry_run: true, fail_fast: false, verbose: false,
146-
output_paths: {
147-
bottle: Pathname.new("#{tmpdir}/bottle.txt"),
148-
linkage: Pathname.new("#{tmpdir}/linkage.txt"),
149-
skipped_or_failed_formulae: Pathname.new("#{tmpdir}/skipped.txt"),
150-
}
151-
)
180+
old_formula = all_bottle_formula(formula_path)
181+
formulae = formulae_test_bot(tap_path, tmpdir)
152182

153183
with_env(GITHUB_ACTIONS: "true", GITHUB_WORKSPACE: tap_path.to_s) do
154184
expect { formulae.send(:annotate_missing_all_bottle, old_formula, bottle_dir: tap_path) }
@@ -162,6 +192,87 @@ class Foo < Formula
162192
end
163193
end
164194
end
195+
196+
it "does not write a warning annotation when local JSON already has an all bottle" do
197+
Dir.mktmpdir do |tmpdir|
198+
tag = Utils::Bottles.tag
199+
sha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
200+
all_sha256 = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
201+
tap_path = Pathname(tmpdir)
202+
formula_path = tap_path/"Formula/foo.rb"
203+
write_platform_bottle_formula(formula_path, tag, sha256)
204+
write_bottle_json(tap_path, tag, sha256)
205+
write_bottle_json(tap_path, "all", all_sha256)
206+
207+
old_formula = all_bottle_formula(formula_path)
208+
formulae = formulae_test_bot(tap_path, tmpdir)
209+
210+
with_env(GITHUB_ACTIONS: "true", GITHUB_WORKSPACE: tap_path.to_s) do
211+
expect { formulae.send(:annotate_missing_all_bottle, old_formula, bottle_dir: tap_path) }
212+
.not_to output.to_stdout
213+
end
214+
end
215+
end
216+
217+
it "does not write a warning annotation for a single platform-specific bottle" do
218+
Dir.mktmpdir do |tmpdir|
219+
tag = Utils::Bottles.tag
220+
sha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
221+
tap_path = Pathname(tmpdir)
222+
formula_path = tap_path/"Formula/foo.rb"
223+
write_platform_bottle_formula(formula_path, tag, sha256)
224+
write_bottle_json(tap_path, tag, sha256)
225+
226+
old_formula = all_bottle_formula(formula_path)
227+
formulae = formulae_test_bot(tap_path, tmpdir)
228+
229+
with_env(GITHUB_ACTIONS: "true", GITHUB_WORKSPACE: tap_path.to_s) do
230+
expect { formulae.send(:annotate_missing_all_bottle, old_formula, bottle_dir: tap_path) }
231+
.not_to output.to_stdout
232+
end
233+
end
234+
end
235+
236+
it "writes a warning annotation when matching checksums have different cellars" do
237+
Dir.mktmpdir do |tmpdir|
238+
tag = Utils::Bottles.tag
239+
other_tag = (tag.to_s == "arm64_tahoe") ? "tahoe" : "arm64_tahoe"
240+
sha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
241+
tap_path = Pathname(tmpdir)
242+
formula_path = tap_path/"Formula/foo.rb"
243+
write_platform_bottle_formula(formula_path, tag, sha256)
244+
write_bottle_json(tap_path, tag, sha256)
245+
write_bottle_json(tap_path, other_tag, sha256, cellar: "any")
246+
247+
old_formula = all_bottle_formula(formula_path)
248+
formulae = formulae_test_bot(tap_path, tmpdir)
249+
250+
with_env(GITHUB_ACTIONS: "true", GITHUB_WORKSPACE: tap_path.to_s) do
251+
expect { formulae.send(:annotate_missing_all_bottle, old_formula, bottle_dir: tap_path) }
252+
.to output(/title=foo: missing :all bottle::.*sha256 `#{sha256}`/).to_stdout
253+
end
254+
end
255+
end
256+
257+
it "does not write a warning annotation when platform bottles can become an all bottle" do
258+
Dir.mktmpdir do |tmpdir|
259+
tag = Utils::Bottles.tag
260+
other_tag = (tag.to_s == "arm64_tahoe") ? "tahoe" : "arm64_tahoe"
261+
sha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
262+
tap_path = Pathname(tmpdir)
263+
formula_path = tap_path/"Formula/foo.rb"
264+
write_platform_bottle_formula(formula_path, tag, sha256)
265+
[tag.to_s, other_tag].each { |bottle_tag| write_bottle_json(tap_path, bottle_tag, sha256) }
266+
267+
old_formula = all_bottle_formula(formula_path)
268+
formulae = formulae_test_bot(tap_path, tmpdir)
269+
270+
with_env(GITHUB_ACTIONS: "true", GITHUB_WORKSPACE: tap_path.to_s) do
271+
expect { formulae.send(:annotate_missing_all_bottle, old_formula, bottle_dir: tap_path) }
272+
.not_to output.to_stdout
273+
end
274+
end
275+
end
165276
end
166277

167278
describe "#testing_portable_ruby?" do

Library/Homebrew/test_bot/formulae.rb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,14 @@ def annotate_missing_all_bottle(formula, bottle_dir: Pathname.pwd)
481481
end
482482
return if sha256_nodes.any? { |sha256_node| bottle_sha256_node_tag.call(sha256_node, :all) }
483483

484-
tag_hash = local_bottle_hash(formula.name, bottle_dir:)&.dig(formula.name, "bottle", "tags", bottle_tag.to_s)
484+
# This predictor only has local JSONs, so mirror the merge-time tag count
485+
# and cellar/checksum dedupe gates; final merge handles version/rebuild.
486+
local_tag_hashes = local_bottle_tag_hashes(formula.name, bottle_dir:)
487+
return if local_tag_hashes.key?("all")
488+
return if local_tag_hashes.count < 2
489+
return if local_tag_hashes.values.uniq { |tag_hash| [tag_hash["cellar"], tag_hash["sha256"]] }.one?
490+
491+
tag_hash = local_tag_hashes[bottle_tag.to_s]
485492
line = sha256_nodes.find do |sha256_node|
486493
bottle_sha256_node_tag.call(sha256_node, bottle_tag.to_sym)
487494
end&.source_range&.line
@@ -510,6 +517,29 @@ def annotate_missing_all_bottle(formula, bottle_dir: Pathname.pwd)
510517
opoo "Failed to determine missing `:all` bottle impact for #{formula.full_name}: #{e}"
511518
end
512519

520+
sig {
521+
params(formula_name: String, bottle_dir: Pathname)
522+
.returns(T::Hash[String, T::Hash[String, T.anything]])
523+
}
524+
def local_bottle_tag_hashes(formula_name, bottle_dir:)
525+
tag_hashes = T.let({}, T::Hash[String, T::Hash[String, T.anything]])
526+
bottle_glob(formula_name, bottle_dir, ".json", bottle_tag: "*").each do |local_bottle_json|
527+
bottle_hash = JSON.parse(local_bottle_json.read).dig(formula_name, "bottle")
528+
next unless bottle_hash.is_a?(Hash)
529+
530+
cellar = bottle_hash["cellar"]
531+
tags = bottle_hash["tags"]
532+
next unless tags.is_a?(Hash)
533+
534+
tags.each do |tag, tag_hash|
535+
next if !tag.is_a?(String) || !tag_hash.is_a?(Hash)
536+
537+
tag_hashes[tag] = tag_hash.merge("cellar" => tag_hash["cellar"] || cellar)
538+
end
539+
end
540+
tag_hashes
541+
end
542+
513543
sig { params(formula: Formula).void }
514544
def livecheck(formula)
515545
return unless formula.livecheck_defined?

0 commit comments

Comments
 (0)