Skip to content
Open
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
10 changes: 7 additions & 3 deletions Library/Homebrew/test_bot.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

require "test_bot/step"
Expand All @@ -19,18 +19,21 @@ module Homebrew
module TestBot
module_function

GIT = "/usr/bin/git"
GIT = T.let("/usr/bin/git", String)

HOMEBREW_TAP_REGEX = %r{^([\w-]+)/homebrew-([\w-]+)$}
HOMEBREW_TAP_REGEX = T.let(%r{^([\w-]+)/homebrew-([\w-]+)$}, Regexp)

sig { params(args: Cmd::TestBotCmd::Args).returns(T::Boolean) }
def cleanup?(args)
args.cleanup? || GitHub::Actions.env_set?
end

sig { params(args: Cmd::TestBotCmd::Args).returns(T::Boolean) }
def local?(args)
args.local? || GitHub::Actions.env_set?
end

sig { params(tap: T.nilable(String)).returns(T.nilable(Tap)) }
def resolve_test_tap(tap = nil)
return Tap.fetch(tap) if tap

Expand All @@ -52,6 +55,7 @@ def resolve_test_tap(tap = nil)
end
end

sig { params(args: Cmd::TestBotCmd::Args).void }
def run!(args)
$stdout.sync = true
$stderr.sync = true
Expand Down
5 changes: 4 additions & 1 deletion Library/Homebrew/test_bot/bottles_fetch.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

module Homebrew
module TestBot
class BottlesFetch < TestFormulae
sig { returns(T::Array[String]) }
attr_accessor :testing_formulae

sig { params(args: Homebrew::CLI::Args).void }
def run!(args:)
info_header "Testing formulae:"
puts testing_formulae
Expand All @@ -19,6 +21,7 @@ def run!(args:)

private

sig { params(formula_name: String, args: Homebrew::CLI::Args).void }
def fetch_bottles!(formula_name, args:)
test_header(:BottlesFetch, method: "fetch_bottles!(#{formula_name})")

Expand Down
4 changes: 3 additions & 1 deletion Library/Homebrew/test_bot/cleanup_after.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

module Homebrew
module TestBot
class CleanupAfter < TestCleanup
sig { params(args: Homebrew::Cmd::TestBotCmd::Args).void }
def run!(args:)
if ENV["HOMEBREW_GITHUB_ACTIONS"].present? && ENV["GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED"].blank? &&
# don't need to do post-build cleanup unless testing test-bot itself.
Expand All @@ -27,6 +28,7 @@ def run!(args:)

private

sig { void }
def pkill_if_needed
pgrep = ["pgrep", "-f", HOMEBREW_CELLAR.to_s]

Expand Down
7 changes: 3 additions & 4 deletions Library/Homebrew/test_bot/cleanup_before.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

module Homebrew
module TestBot
class CleanupBefore < TestCleanup
sig { params(args: Homebrew::Cmd::TestBotCmd::Args).void }
def run!(args:)
test_header(:CleanupBefore)

if tap.to_s != CoreTap.instance.name && CoreTap.instance.installed?
reset_if_needed(CoreTap.instance.path.to_s)
end
reset_if_needed(CoreTap.instance.path) if tap.to_s != CoreTap.instance.name && CoreTap.instance.installed?

Pathname.glob("*.bottle*.*").each(&:unlink)

Expand Down
99 changes: 73 additions & 26 deletions Library/Homebrew/test_bot/formulae.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

module Homebrew
module TestBot
class Formulae < TestFormulae
attr_writer :testing_formulae, :added_formulae, :deleted_formulae

sig { params(testing_formulae: T::Array[String]).returns(T::Array[String]) }
attr_writer :testing_formulae

sig { params(added_formulae: T::Array[String]).returns(T::Array[String]) }
attr_writer :added_formulae

sig { params(deleted_formulae: T::Array[String]).returns(T::Array[String]) }
attr_writer :deleted_formulae

sig {
params(
tap: T.nilable(Tap),
git: String,
dry_run: T::Boolean,
fail_fast: T::Boolean,
verbose: T::Boolean,
output_paths: T::Hash[Symbol, Pathname],
).void
}
def initialize(tap:, git:, dry_run:, fail_fast:, verbose:, output_paths:)
super(tap:, git:, dry_run:, fail_fast:, verbose:)

@built_formulae = []
@bottle_checksums = {}
@bottle_output_path = output_paths[:bottle]
@linkage_output_path = output_paths[:linkage]
@skipped_or_failed_formulae_output_path = output_paths[:skipped_or_failed_formulae]
@built_formulae = T.let([], T::Array[String])
@bottle_checksums = T.let({}, T::Hash[Pathname, String])
@bottle_output_path = T.let(T.must(output_paths[:bottle]), Pathname)
@linkage_output_path = T.let(T.must(output_paths[:linkage]), Pathname)
@skipped_or_failed_formulae_output_path = T.let(
T.must(output_paths[:skipped_or_failed_formulae]),
Pathname,
)

@testing_formulae = T.let([], T::Array[String])
@added_formulae = T.let([], T::Array[String])
@deleted_formulae = T.let([], T::Array[String])

@tested_formulae_count = T.let(0, Integer)
@testing_formulae_count = T.let(0, Integer)
end

sig { params(args: Cmd::TestBotCmd::Args).void }
def run!(args:)
test_header(:Formulae)

Expand Down Expand Up @@ -44,7 +72,6 @@ def run!(args:)
# #run! modifies `@testing_formulae`, so we need to track this separately.
@testing_formulae_count = @testing_formulae.count
perform_bash_cleanup = @testing_formulae.include?("bash")
@tested_formulae_count = 0

sorted_formulae.each do |f|
verify_local_bottles
Expand Down Expand Up @@ -87,6 +114,7 @@ def run!(args:)

private

sig { params(deps: T::Array[Dependency]).void }
def tap_needed_taps(deps)
deps.each { |d| d.to_formula.recursive_dependencies }
rescue TapFormulaUnavailableError => e
Expand All @@ -97,13 +125,15 @@ def tap_needed_taps(deps)
retry
end

sig { void }
def install_ca_certificates_if_needed
return if DevelopmentTools.ca_file_handles_most_https_certificates?

test "brew", "install", "--formulae", "ca-certificates",
env: { "HOMEBREW_DEVELOPER" => nil }
end

sig { params(formula: Formula, formula_name: String, args: Cmd::TestBotCmd::Args).void }
def setup_formulae_deps_instances(formula, formula_name, args:)
conflicts = formula.conflicts
formula_recursive_dependencies = formula.recursive_dependencies.map(&:to_formula)
Expand Down Expand Up @@ -157,8 +187,8 @@ def setup_formulae_deps_instances(formula, formula_name, args:)
end

dependencies -= installed
@unchanged_dependencies = dependencies - @testing_formulae
unless @unchanged_dependencies.empty?
@unchanged_dependencies = T.let(dependencies - @testing_formulae, T.nilable(T::Array[Dependency]))
if @unchanged_dependencies.present?
test "brew", "fetch", "--formulae", "--retry",
*@unchanged_dependencies
end
Expand Down Expand Up @@ -188,17 +218,19 @@ def setup_formulae_deps_instances(formula, formula_name, args:)
Utils.safe_popen_read("brew", "deps", "--formula", "--include-test", formula_name)
.split("\n")
build_dependencies = dependencies - runtime_or_test_dependencies
@unchanged_build_dependencies = build_dependencies - @testing_formulae
@unchanged_build_dependencies = T.let(build_dependencies - @testing_formulae, T.nilable(T::Array[Dependency]))
end

sig { params(formula: Formula).void }
def cleanup_bottle_etc_var(formula)
# Restore etc/var files from bottle so dependents can use them.
formula.install_etc_var
end

sig { returns(T::Boolean) }
def verify_local_bottles
# Portable Ruby bottles are handled differently.
return if testing_portable_ruby?
return false if testing_portable_ruby?

# Setting `HOMEBREW_DISABLE_LOAD_FORMULA` probably doesn't do anything here but let's set it just to be safe.
with_env(HOMEBREW_DISABLE_LOAD_FORMULA: "1") do
Expand Down Expand Up @@ -245,9 +277,10 @@ def verify_local_bottles
end
end

sig { params(formula: Formula, new_formula: T::Boolean, args: Cmd::TestBotCmd::Args).void }
def bottle_reinstall_formula(formula, new_formula, args:)
unless build_bottle?(formula, args:)
@bottle_filename = nil
@bottle_filename = T.let(nil, T.nilable(Pathname))
return
end

Expand Down Expand Up @@ -282,16 +315,20 @@ def bottle_reinstall_formula(formula, new_formula, args:)
return
end

@bottle_filename = Pathname.new(
bottle_step.output
.gsub(%r{.*(\./\S+#{HOMEBREW_BOTTLES_EXTNAME_REGEX}).*}om, '\1'),
@bottle_filename = T.let(
Pathname.new(
bottle_step.output.gsub(%r{.*(\./\S+#{HOMEBREW_BOTTLES_EXTNAME_REGEX}).*}om, '\1'),
),
T.nilable(Pathname),
)
@bottle_json_filename = Pathname.new(
@bottle_filename.to_s.gsub(/\.(\d+\.)?tar\.gz$/, ".json"),

@bottle_json_filename = T.let(
Pathname.new(@bottle_filename.to_s.gsub(/\.(\d+\.)?tar\.gz$/, ".json")),
T.nilable(Pathname),
)

@bottle_checksums[@bottle_filename.realpath] = @bottle_filename.sha256
@bottle_checksums[@bottle_json_filename.realpath] = @bottle_json_filename.sha256
@bottle_checksums[T.must(@bottle_filename).realpath] = T.must(@bottle_filename).sha256
@bottle_checksums[T.must(@bottle_json_filename).realpath] = T.must(@bottle_json_filename).sha256

@bottle_output_path.write(bottle_step.output, mode: "a")

Expand All @@ -304,9 +341,9 @@ def bottle_reinstall_formula(formula, new_formula, args:)

@testing_formulae.delete(formula.name)

unless @unchanged_build_dependencies.empty?
if @unchanged_build_dependencies.present?
test "brew", "uninstall", "--formulae", "--force", "--ignore-dependencies", *@unchanged_build_dependencies
@unchanged_dependencies -= @unchanged_build_dependencies
@unchanged_dependencies -= @unchanged_build_dependencies if @unchanged_dependencies.present?
end

verify_attestations = if formula.name == "gh"
Expand All @@ -320,6 +357,7 @@ def bottle_reinstall_formula(formula, new_formula, args:)
env: { "HOMEBREW_VERIFY_ATTESTATIONS" => verify_attestations }
end

sig { params(formula: Formula, args: Cmd::TestBotCmd::Args).returns(T::Boolean) }
def build_bottle?(formula, args:)
# Build and runtime dependencies must be bottled on the current OS,
# but accept an older compatible bottle for test dependencies.
Expand All @@ -334,6 +372,7 @@ def build_bottle?(formula, args:)
!args.build_from_source?
end

sig { params(formula: Formula).void }
def livecheck(formula)
return unless formula.livecheck_defined?
return if formula.livecheck.skip?
Expand Down Expand Up @@ -395,6 +434,7 @@ def livecheck(formula)
end
end

sig { params(formula_name: String, args: Cmd::TestBotCmd::Args).void }
def formula!(formula_name, args:)
cleanup_during!(@testing_formulae, args:)

Expand Down Expand Up @@ -595,7 +635,9 @@ def formula!(formula_name, args:)
if failed_linkage_or_test_messages.present?
if @bottle_filename
failed_dir = @bottle_filename.dirname/"failed"
moved_artifacts = [@bottle_filename, @bottle_json_filename].map(&:realpath)
moved_artifacts = [@bottle_filename, @bottle_json_filename].map do |path|
T.must(path).realpath
end
failed_dir.install moved_artifacts

moved_artifacts.each do |old_location|
Expand All @@ -613,13 +655,14 @@ def formula!(formula_name, args:)
end
ensure
@tested_formulae_count += 1
cleanup_bottle_etc_var(formula) if cleanup?(args)
cleanup_bottle_etc_var(T.must(formula)) if cleanup?(args)

if @unchanged_dependencies.present?
test "brew", "uninstall", "--formulae", "--force", "--ignore-dependencies", *@unchanged_dependencies
end
end

sig { params(formula_name: String).void }
def portable_formula!(formula_name)
test_header(:Formulae, method: "portable_formula!(#{formula_name})")

Expand Down Expand Up @@ -655,6 +698,7 @@ def portable_formula!(formula_name)
test "brew", "bottle", "--skip-relocation", "--json", "--no-rebuild", formula_name
end

sig { params(formula_name: String).void }
def deleted_formula!(formula_name)
test_header(:Formulae, method: "deleted_formula!(#{formula_name})")

Expand All @@ -667,8 +711,11 @@ def deleted_formula!(formula_name)
formula_name
end

sig { returns(T::Boolean) }
def testing_portable_ruby?
tap&.core_tap? && @testing_formulae.include?("portable-ruby")
return false unless tap&.core_tap?

@testing_formulae.include?("portable-ruby")
end
end
end
Expand Down
Loading
Loading