Skip to content

Enable strict typing in Homebrew::TestBot, redux#21539

Merged
MikeMcQuaid merged 3 commits intomainfrom
strict-typing-test-bot
Feb 9, 2026
Merged

Enable strict typing in Homebrew::TestBot, redux#21539
MikeMcQuaid merged 3 commits intomainfrom
strict-typing-test-bot

Conversation

@dduugg
Copy link
Member

@dduugg dduugg commented Feb 7, 2026

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) with your changes locally?

  • AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes.

See #21506 for the previous attempt.

I've added fixes for the resulting CI failures observed by @botantony and @chenrui333 in a second commit. I've also included vibe-coded specs to prevent future regressions (confirming that the specs fail without the fixes in that commit).

The test coverage is light for TestBot Test subclasses. I can add vibe-coded specs for the other classes, if reviewers prefer. I would like to self-merge at a time that I can be available to fix other potential test failures.

Copilot AI review requested due to automatic review settings February 7, 2026 17:35
- test_formulae: use [] instead of fetch for @downloaded_artifacts so new
  SHAs use the hash default (empty array) and avoid KeyError
- junit: require rexml/document, xmldecl, cdata in #initialize so REXML
  is loaded before use (fixes uninitialized constant on Linux CI)
- test_cleanup: pass HOMEBREW_REPOSITORY.to_s to checkout_branch_if_needed,
  reset_if_needed, clean_if_needed (fixes Pathname type error when tap set)
- Add test_formulae_spec to prevent KeyError regression for new SHA access
- Add junit_spec to prevent REXML NameError regression
- Add test_cleanup_spec to prevent Pathname regression
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR switches Homebrew::TestBot internals over to typed: strict Sorbet typing, updating method signatures and call sites accordingly, and adds a few regression specs to keep CI failures from recurring.

Changes:

  • Convert Library/Homebrew/test_bot/** classes/modules from typed: true to typed: strict, adding sigs and T.let annotations.
  • Remove now-unnecessary RBI shims for TestRunner and TestFormulae.
  • Add/adjust RSpec coverage for TestFormulae artifact downloading and Junit XML generation, and update existing Setup spec to use the correct args type.

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Library/Homebrew/test_bot/test_runner.rbi Removed RBI shim for TestRunner.
Library/Homebrew/test_bot/test_runner.rb Converted to typed: strict; added signatures and stricter typing around test orchestration.
Library/Homebrew/test_bot/test_formulae.rbi Removed RBI shim for TestFormulae.
Library/Homebrew/test_bot/test_formulae.rb Converted to typed: strict; added signatures/annotations and adjusted nil-handling.
Library/Homebrew/test_bot/test_cleanup.rb Converted to typed: strict; added signatures and tightened string conversions for command execution.
Library/Homebrew/test_bot/test.rb Converted to typed: strict; added signatures and typed ivars for step tracking/execution.
Library/Homebrew/test_bot/tap_syntax.rb Converted to typed: strict; added signatures and tap nil-handling.
Library/Homebrew/test_bot/step.rb Converted to typed: strict; added signatures/typed ivars and tightened step execution typing.
Library/Homebrew/test_bot/setup.rb Converted to typed: strict; added method signature.
Library/Homebrew/test_bot/junit.rb Converted to typed: strict; added signatures and ensured REXML is required before use.
Library/Homebrew/test_bot/formulae_detect.rb Converted to typed: strict; added signatures and adjusted tap/git handling.
Library/Homebrew/test_bot/formulae_dependents.rb Converted to typed: strict; added signatures and typed state for dependent testing.
Library/Homebrew/test_bot/formulae.rb Converted to typed: strict; added signatures/typed ivars and tightened step access (fetch(-1), T.must).
Library/Homebrew/test_bot/cleanup_before.rb Converted to typed: strict; added method signature.
Library/Homebrew/test_bot/cleanup_after.rb Converted to typed: strict; added method signatures.
Library/Homebrew/test_bot/bottles_fetch.rb Added signatures for accessor and helpers.
Library/Homebrew/test_bot.rb Converted to typed: strict; added signatures for module functions.
Library/Homebrew/test/test_bot/test_formulae_spec.rb New regression spec for artifact download bookkeeping behavior.
Library/Homebrew/test/test_bot/setup_spec.rb Updated requires/args type for the strict-typed TestBot command classes.
Library/Homebrew/test/test_bot/junit_spec.rb New regression specs ensuring REXML is loaded and XML output includes failures correctly.
Files not reviewed (2)
  • Library/Homebrew/test_bot/test_formulae.rbi: Language not supported
  • Library/Homebrew/test_bot/test_runner.rbi: Language not supported
Comments suppressed due to low confidence (2)

Library/Homebrew/test_bot/test_formulae.rb:304

  • Utils.safe_popen_read(git, ...) passes git and repository without ensuring they’re Strings. With typed: strict, git is T.nilable(String) (from Test) and repository is a Pathname, so this will fail Sorbet and may misbehave if git is ever nil. Please use T.must(git)/git.to_s and repository.to_s here (and in other Utils.*popen_read call sites in this class).
          bottle_header = "Bottle cache hit"
          bottle_commit_details = if @fetched_refs&.include?(bottle_revision)
            Utils.safe_popen_read(git, "-C", repository, "show", "--format=reference", bottle_revision)
          else

Library/Homebrew/test_bot/test.rb:95

  • Test#test takes a splat (def test(*arguments, ...)) but the sig types arguments as a single String. With Sorbet, the rest arg should be typed as T::Array[String] (and ideally ensure the array contains strings before passing to Step.new). As-is, this will fail type-checking and can hide non-String arguments flowing into Step/system_command.
      sig {
        params(
          arguments:        String,
          named_args:       T.nilable(T.any(String, T::Array[String])),
          env:              T::Hash[String, String],
          verbose:          T::Boolean,
          ignore_failures:  T::Boolean,
          report_analytics: T::Boolean,
        ).returns(Step)
      }
      def test(*arguments, named_args: nil, env: {}, verbose: @verbose, ignore_failures: false,
               report_analytics: false)
        step = Step.new(
          arguments,
          named_args:,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@botantony botantony enabled auto-merge February 8, 2026 06:22
@botantony botantony disabled auto-merge February 8, 2026 06:22
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Will merge once we've released 5.0.14.

@MikeMcQuaid MikeMcQuaid added this pull request to the merge queue Feb 9, 2026
Merged via the queue into main with commit ae8a1c3 Feb 9, 2026
37 checks passed
@MikeMcQuaid MikeMcQuaid deleted the strict-typing-test-bot branch February 9, 2026 09:30
@daeho-ro
Copy link
Member

daeho-ro commented Feb 9, 2026

There is a type error similar before,

Error: Parameter 'tag': Expected type Symbol, got type Utils::Bottles::Tag with hash 2207702117044937912

@botantony
Copy link
Member

Got new error when testing deps:

Error: Parameter 'arguments': Expected type String, got type Pathname with value #<Pathname:/github/home/bot....0.x86_64_linux.bottle.tar.gz>
Caller: /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:227
Definition: /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test.rb:91 (Homebrew::TestBot::Test#test)
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/configuration.rb:293:in 'T::Configuration.call_validation_error_handler_default'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/configuration.rb:300:in 'T::Configuration.call_validation_error_handler'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:322:in 'T::Private::Methods::CallValidation.report_error'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:230:in 'block in T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/signature.rb:222:in 'block in T::Private::Methods::Signature#each_args_value_type'
<internal:numeric>:257:in 'Integer#times'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/signature.rb:221:in 'T::Private::Methods::Signature#each_args_value_type'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:227:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::Test#_on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test_formulae.rb:295:in 'Homebrew::TestBot::TestFormulae#install_formula_from_bottle!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::TestFormulae#_on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/formulae_dependents.rb:89:in 'block in Homebrew::TestBot::FormulaeDependents#install_formulae_if_needed_from_bottles!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/formulae_dependents.rb:85:in 'Array#each'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/formulae_dependents.rb:85:in 'Homebrew::TestBot::FormulaeDependents#install_formulae_if_needed_from_bottles!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::FormulaeDependents#_on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/formulae_dependents.rb:45:in 'Homebrew::TestBot::FormulaeDependents#run!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::FormulaeDependents#_on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test_runner.rb:275:in 'Homebrew::TestBot::TestRunner.run_tests'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::TestRunner._on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test_runner.rb:75:in 'block in Homebrew::TestBot::TestRunner.run!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test_runner.rb:63:in 'Array#each'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot/test_runner.rb:63:in 'Homebrew::TestBot::TestRunner.run!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::TestBot::TestRunner._on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test_bot.rb:140:in 'Homebrew::TestBot.run!'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/dev-cmd/test-bot.rb:126:in 'Homebrew::Cmd::TestBotCmd#run'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'UnboundMethod#bind_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/call_validation.rb:282:in 'T::Private::Methods::CallValidation.validate_call'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle/ruby/3.4.0/gems/sorbet-runtime-0.6.12908/lib/types/private/methods/_methods.rb:259:in 'block in Homebrew::Cmd::TestBotCmd#_on_method_added'
/home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/brew.rb:102:in '<main>'

https://github.com/Homebrew/homebrew-core/actions/runs/21823005299/job/62963158249?pr=266550

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants