-
-
Notifications
You must be signed in to change notification settings - Fork 10.7k
test_bot: Bump some files to Sorbet typed: strict
#21010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- There's one left (as of 2025-11-09), `test_bot/test.rb` which shows >70 errors when making `strict`, so I'm leaving that one for future us!
There was a problem hiding this 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 migrates the test-bot codebase from typed: true to typed: strict mode in Sorbet, adding comprehensive type annotations throughout the codebase to enable stricter type checking.
Key changes:
- Upgraded Sorbet typing level from
truetostrictacross all test-bot files - Added explicit type signatures (
sig) to all methods, attributes, and instance variables - Used
T.letto explicitly type all instance variable initializations - Added
T.must()calls where needed to satisfy Sorbet's strict null checking
Reviewed Changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| Library/Homebrew/test_bot/test_runner.rb | Added strict typing with method signatures and instance variable types; includes an initialize method for module (potential issue) |
| Library/Homebrew/test_bot/test_formulae.rb | Added comprehensive type annotations for attributes, methods, and complex instance variables including hashes and arrays |
| Library/Homebrew/test_bot/test_cleanup.rb | Added type signatures and converted repository parameters to use .to_s for git commands |
| Library/Homebrew/test_bot/test.rb | Added passed? helper method and upgraded to strict typing |
| Library/Homebrew/test_bot/tap_syntax.rb | Added method signature and upgraded to strict typing |
| Library/Homebrew/test_bot/step.rb | Added extensive type annotations for all attributes and methods; includes type assertions with T.must() |
| Library/Homebrew/test_bot/setup.rb | Added method signature with T.self_type return type |
| Library/Homebrew/test_bot/junit.rb | Added type signatures and moved require statement to top of file |
| Library/Homebrew/test_bot/formulae_detect.rb | Added comprehensive type signatures including complex parameter lists |
| Library/Homebrew/test_bot/formulae_dependents.rb | Added type signatures and extensive use of T.must() for nullable variables |
| Library/Homebrew/test_bot/formulae.rb | Added type annotations and fixed array wrapping pattern from Array.new to literal syntax |
| Library/Homebrew/test_bot/cleanup_before.rb | Added signature and simplified conditional logic |
| Library/Homebrew/test_bot/cleanup_after.rb | Added method signature and upgraded to strict typing |
| Library/Homebrew/test_bot/bottles_fetch.rb | Added type signatures for accessor and methods |
| Library/Homebrew/test_bot.rb | Added type annotations for constants and module methods |
Comments suppressed due to low confidence (1)
Library/Homebrew/test_bot/setup.rb:21
- The
run!method signature declares a return type ofT.self_type, but the method doesn't have an explicit return statement. In Ruby, methods return the value of the last expression, which in this case would be the result of the lasttestcall (either"brew", "doctor", "--debug", verbose: trueor"brew", "doctor").
The method should either explicitly return self to match the signature, or the return type should be changed to match what test returns (which appears to be a Step object based on the Test class implementation).
sig { params(args: Homebrew::CLI::Args).returns(T.self_type) }
def run!(args:)
test_header(:Setup)
test "brew", "install-bundler-gems", "--add-groups=ast,audit,bottle,formula_test,livecheck,style"
# Always output `brew config` output even when it doesn't fail.
test "brew", "config", verbose: true
if ENV["HOMEBREW_TEST_BOT_VERBOSE_DOCTOR"]
test "brew", "doctor", "--debug", verbose: true
else
test "brew", "doctor"
end
end
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| sig { returns(Float) } | ||
| def time | ||
| end_time - start_time | ||
| (T.must(end_time) - start_time).to_f |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent use of T.must(). Only end_time is wrapped in T.must(), but start_time is not. Both start_time and end_time have the same type signature (T.nilable(Time)), so they should be treated consistently. Either both should be wrapped in T.must() or the method should ensure both are set before being called.
| (T.must(end_time) - start_time).to_f | |
| (T.must(end_time) - T.must(start_time)).to_f |
| @repository = T.let(repository, T.nilable(Pathname)) | ||
|
|
||
| @name = T.let(command[1]&.delete("-"), T.nilable(String)) | ||
| @status = T.let(:running, Symbol) |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type annotation for @status is incorrect. It's initialized to :running (a Symbol), but the type signature declares it as T.nilable(Symbol). However, the attr_reader's signature on line 19 says T.nilable(Symbol), which means it can be nil, but the initialization on line 47 declares it as just Symbol (not nilable).
This inconsistency will cause type checking issues. The attr_reader signature should match the T.let declaration, or vice versa.
| @status = T.let(:running, Symbol) | |
| @status = T.let(:running, T.nilable(Symbol)) |
| end | ||
| end | ||
|
|
||
| sig { params(name: String).returns([Pathname, T.nilable(Integer)]) } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type annotation [Pathname, T.nilable(Integer)] is using incorrect syntax. In Sorbet, tuples are represented using T::Array with fixed sizes, not raw array syntax. This should be T::Array[T.any(Pathname, T.nilable(Integer))] or use a custom type alias.
The correct Sorbet syntax for a 2-tuple would be creating a custom type, or if this truly returns an array with a pathname at index 0 and an integer at index 1, consider using a more explicit structure like a hash or custom class.
| sig { void } | ||
| def initialize | ||
| @skipped_or_failed_formulae_output_path = T.let( | ||
| Pathname.new("skipped_or_failed_formulae-PLACEHOLDER.txt"), | ||
| Pathname, | ||
| ) | ||
| end |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TestRunner module uses module_function, which means all methods are module methods (class-level). Module methods cannot have instance variables like @skipped_or_failed_formulae_output_path. This initialize method will never be called, and the instance variable won't be accessible in other methods.
Consider refactoring this to use a class instead of a module with module_function, or handle @skipped_or_failed_formulae_output_path without relying on instance variables.
| end | ||
| end | ||
|
|
||
| sig { params(formula: Formula, formula_name: String, args: Cmd::TestBotCmd::Args).returns([[Formula], [Formula], [Formula]]) } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type annotation [[Formula], [Formula], [Formula]] is invalid Sorbet syntax. This appears to be attempting to declare a 3-tuple of Formula arrays, but the syntax is incorrect.
The correct syntax would be T::Array[T::Array[Formula]] if returning an array of arrays, or if you want to indicate three separate arrays in a specific structure, consider using a more explicit return type like a hash with named keys or a custom type.
| sig { params(formula: Formula, formula_name: String, args: Cmd::TestBotCmd::Args).returns([[Formula], [Formula], [Formula]]) } | |
| sig { params(formula: Formula, formula_name: String, args: Cmd::TestBotCmd::Args).returns(T::Array[T::Array[Formula]]) } |
| FileUtils.rm_f "#{repository}/.git/gc.log" | ||
| end | ||
|
|
||
| sig { params(repository: String).void } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type inconsistency: clean_if_needed has a signature declaring repository: String, but it's called from other methods that pass Pathname objects (e.g., line 141 passes a Pathname to default_origin_ref which then uses git commands expecting a path).
This should be Pathname to match the other similar methods in this class (reset_if_needed, checkout_branch_if_needed, cleanup_git_meta, and prune_if_needed all use Pathname).
| sig { params(repository: String).void } | |
| sig { params(repository: Pathname).void } |
| sig { params(tested_formulae: T::Array[String]).returns(T::Array[String]) } | ||
| attr_writer :tested_formulae | ||
|
|
||
| sig { params(args: Cmd::TestBotCmd::Args).returns(T.anything) } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type T.anything is overly broad and defeats the purpose of strict typing. The method should have a more specific return type.
Looking at the method implementation, it appears to return nil in several branches (lines 51, 56) but otherwise runs to completion without an explicit return, which would return the result of the last statement. Consider using void if no meaningful value is returned, or specify the actual return type if one is needed.
| sig { params(args: Cmd::TestBotCmd::Args).returns(T.anything) } | |
| sig { params(args: Cmd::TestBotCmd::Args).returns(void) } |
brew stylewith your changes locally?brew typecheckwith your changes locally?brew testswith your changes locally?typed: strictin all (non-package) files in Homebrew organisation #17297.test_bot/test.rbwhich shows >70 errors when moved tostrict(as there's a lot ofT.nilable(String)orPathnamefor things like args), so I'm leaving that one for future us who are maybe less tired.