diff --git a/README.md b/README.md index c9effcbb..475e426b 100644 --- a/README.md +++ b/README.md @@ -1,174 +1,190 @@ # Spoom -Useful tools for Sorbet enthusiasts. +Useful tools for Sorbet projects. + +Spoom provides a CLI and a Ruby API to inspect Sorbet projects, improve typing coverage, translate signatures, query Sorbet LSP, and find dead code. ## Installation -Add this line to your application's Gemfile: +Add Spoom to your application's Gemfile: ```ruby -gem 'spoom' +gem "spoom" ``` -And then execute: +Then install it: - $ bundle +```sh +bundle install +``` -Or install it yourself as: +Or install it directly: - $ gem install spoom +```sh +gem install spoom +``` -## Usage +Spoom requires Ruby 3.3 or newer. -`spoom` provides both a CLI and an API to interact with Sorbet. +## Command line interface -### Generate a typing coverage report +Run `spoom help` or `spoom help COMMAND` to list all available commands. -Spoom can create a typing coverage report from Sorbet and Git data: +### Typechecking errors -![Coverage Report](docs/report.png) +`spoom srb tc` runs `srb tc` and can sort, filter, format, and export errors. -After installing the `spoom` gem, run the `timeline` command to collect the history data: +List errors sorted by location: -``` -$ spoom srb coverage timeline --save +```sh +spoom srb tc --sort loc ``` -Then create the HTML page with `report`: +List errors sorted by error code: -``` -$ spoom srb coverage report +```sh +spoom srb tc --sort code ``` -Your report will be generated under `spoom_report.html`. +List only errors with a specific code: -See all the [Typing Coverage](#typing-coverage) CLI commands for more details. +```sh +spoom srb tc --code 7004 +``` -### Command Line Interface +Limit the number of displayed errors: -#### Errors sorting and filtering +```sh +spoom srb tc --limit 10 +``` -List all typechecking errors sorted by location: +Options can be combined: -``` -$ spoom srb tc -s loc +```sh +spoom srb tc --sort code --code 7004 --limit 10 ``` -List all typechecking errors sorted by error code first: +Remove duplicated error lines: -``` -$ spoom srb tc -s code +```sh +spoom srb tc --uniq ``` -List only typechecking errors from a specific error code: +Format each error line: -``` -$ spoom srb tc -c 7004 +```sh +spoom srb tc --format "%C - %F:%L: %M" ``` -List only the first 10 typechecking errors +Format tokens: -``` -$ spoom srb tc -l 10 -``` +* `%C`: error code +* `%F`: file path +* `%L`: line number +* `%M`: error message -These options can be combined: +Hide the final `Errors: X` count: -``` -$ spoom srb tc -s -c 7004 -l 10 +```sh +spoom srb tc --no-count ``` -Remove duplicated error lines: +List only errors from specific files or directories: -``` -$ spoom srb tc -u +```sh +spoom srb tc file1.rb path1/ path2/ ``` -Format each error line: +Write errors to a JUnit XML file: +```sh +spoom srb tc --junit-output-path junit.xml ``` -$ spoom srb tc -f '%C - %F:%L: %M' + +Pass extra options to Sorbet: + +```sh +spoom srb tc --sorbet-options="--typed=true" ``` -Where: +### Typing coverage -* `%C` is the error code -* `%F` is the file the error is from -* `%L` is the line the error is from -* `%M` is the error message +`spoom srb coverage` collects Sorbet coverage metrics and can generate an HTML report from Sorbet and Git data. -Hide the `Errors: X` at the end of the list: +![Coverage Report](docs/report.png) -``` -$ spoom srb tc --no-count +Show a coverage snapshot: + +```sh +spoom srb coverage ``` -List only the errors coming from specific directories or files: +Save a snapshot under `spoom_data/`: +```sh +spoom srb coverage --save ``` -$ spoom srb tc file1.rb path1/ path2/ -``` - -#### Typing coverage -Show metrics about the project contents and the typing coverage: +Save a snapshot under a specific directory: -``` -$ spoom srb coverage +```sh +spoom srb coverage --save my_data/ ``` -Save coverage data under `spoom_data/`: +Show typing coverage evolution based on Git history: -``` -$ spoom srb coverage --save +```sh +spoom srb coverage timeline ``` -Save coverage data under a specific directory: +Replay a specific date range: -``` -$ spoom srb coverage --save my_data/ +```sh +spoom srb coverage timeline --from YYYY-MM-DD --to YYYY-MM-DD ``` -Show typing coverage evolution based on the commits history: +Save timeline snapshots under `spoom_data/`: -``` -$ spoom srb coverage timeline +```sh +spoom srb coverage timeline --save ``` -Show typing coverage evolution based on the commits history between specific dates: +Save timeline snapshots under a specific directory: -``` -$ spoom srb coverage timeline --from YYYY-MM-DD --to YYYY-MM-DD +```sh +spoom srb coverage timeline --save my_data/ ``` -Save the typing coverage evolution as JSON under `spoom_data/`: +Run `bundle install` before collecting each timeline snapshot: -``` -$ spoom srb coverage timeline --save +```sh +spoom srb coverage timeline --bundle-install ``` -Save the typing coverage evolution as JSON in a specific directory: +Generate an HTML coverage report from saved snapshots: -``` -$ spoom srb coverage timeline --save my_data/ +```sh +spoom srb coverage report ``` -Run `bundle install` for each commit of the timeline (may solve errors due to different Sorbet versions): +The report is generated at `spoom_report.html` by default. -``` -$ spoom srb coverage timeline --bundle-install +Generate a report from a custom data directory: + +```sh +spoom srb coverage report --data my_data/ ``` -Generate an HTML typing coverage report: +Change the generated report path: -``` -$ spoom srb coverage report +```sh +spoom srb coverage report --file coverage.html ``` -Change the colors used for strictnesses (useful for colorblind folks): +Change report colors: -``` -$ spoom srb coverage report \ +```sh +spoom srb coverage report \ --color-true "#648ffe" \ --color-false "#fe6002" \ --color-ignore "#feb000" \ @@ -176,123 +192,213 @@ $ spoom srb coverage report \ --color-strong "#6444f1" ``` -Open the HTML typing coverage report: +Open the HTML coverage report: +```sh +spoom srb coverage open ``` -$ spoom srb coverage open + +Open a report at a custom path: + +```sh +spoom srb coverage open coverage.html ``` -#### Change the sigil used in files +### Sorbet sigils -Bump the strictness from all files currently at `typed: false` to `typed: true` where it does not create typechecking errors: +`spoom srb bump` changes `# typed:` sigils when the change does not introduce typechecking errors. -``` -$ spoom srb bump --from false --to true +Bump files from `typed: false` to `typed: true`: + +```sh +spoom srb bump --from false --to true ``` -Bump the strictness from all files currently at `typed: false` to `typed: true` even if it creates typechecking errors: +Force the change without typechecking: -``` -$ spoom srb bump --from false --to true -f +```sh +spoom srb bump --from false --to true --force ``` -Bump the strictness from a list of files (one file by line): +Bump only files listed in a file, one path per line: -``` -$ spoom srb bump --from false --to true -o list.txt +```sh +spoom srb bump --from false --to true --only list.txt ``` -Check if files can be bumped without applying any change and show the list of files that can be bumped without errors. -Will exit with a non-zero status if some files can be bumped without errors (useful to check for bumpable files on CI for example): +Check which files can be bumped without applying changes: -``` -$ spoom srb bump --from false --to true --dry +```sh +spoom srb bump --from false --to true --dry ``` -Bump files using a custom instance of Sorbet: +This command exits with a non-zero status when files can be bumped, which is useful in CI. -``` -$ spoom srb bump --from false --to true --sorbet /path/to/sorbet/bin +Use a custom Sorbet executable: + +```sh +spoom srb bump --from false --to true --sorbet /path/to/sorbet/bin ``` -Count the number of type-checking errors if all files were bumped to true: +Count typechecking errors if all files were bumped: +```sh +spoom srb bump --from false --to true --count-errors --dry ``` -$ spoom srb bump --count-errors --dry + +### Signatures and type assertions + +`spoom srb sigs` translates signatures between Sorbet RBI syntax and RBS comments. + +Translate signatures from RBI to RBS comments: + +```sh +spoom srb sigs translate ``` -#### Translate sigs between RBI and RBS +Translate signatures from RBS comments to RBI: + +```sh +spoom srb sigs translate --from rbs --to rbi path/to/file.rb +``` -Translate all file sigs from RBI to RBS: +Strip Sorbet signatures from files: +```sh +spoom srb sigs strip path/to/file.rb ``` -$ spoom srb sigs translate + +Export gem signatures to an RBI file: + +```sh +spoom srb sigs export ``` -Translate one file's sigs from RBS to RBI: +Check that the exported RBI file is up to date: +```sh +spoom srb sigs export --check-sync ``` -$ spoom srb sigs translate --from rbs --to rbi /path/to/file.rb + +`spoom srb assertions` translates Sorbet type assertions to RBS comments: + +```sh +spoom srb assertions translate path/to/file.rb ``` -#### Interact with Sorbet LSP mode +### Sorbet LSP + +`spoom srb lsp` sends requests to Sorbet LSP. -**Experimental** +This command group is experimental. -Find all definitions for `Foo`: +Find symbols matching `Foo`: +```sh +spoom srb lsp find Foo ``` -$ spoom srb lsp find Foo + +List symbols in a file: + +```sh +spoom srb lsp symbols file.rb ``` -List all symbols in a file: +List definitions for a code location: +```sh +spoom srb lsp defs file.rb 10 4 ``` -$ spoom srb lsp symbols + +List references for a code location: + +```sh +spoom srb lsp refs file.rb 10 4 ``` -List all definitions for a specific code location: +Show hover information for a code location: +```sh +spoom srb lsp hover file.rb 10 4 ``` -$ spoom srb lsp defs + +Show signature information for a code location: + +```sh +spoom srb lsp sigs file.rb 10 4 ``` -List all references for a specific code location: +Show type information for a code location: +```sh +spoom srb lsp types file.rb 10 4 ``` -$ spoom srb lsp refs + +### Sorbet code metrics + +`spoom srb metrics` collects metrics about Sorbet usage in Ruby files. + +Show metrics for the current project: + +```sh +spoom srb metrics ``` -Show hover information for a specific code location: +Show metrics for specific files or directories: +```sh +spoom srb metrics lib/ test/foo_test.rb ``` -$ spoom srb lsp hover + +Dump raw metric keys and values: + +```sh +spoom srb metrics --dump ``` -Show signature information for a specific code location: +### Dead code +`spoom deadcode` indexes a project and reports definitions that do not appear to be referenced. + +Analyze the current project: + +```sh +spoom deadcode ``` -$ spoom srb lsp sig + +Analyze specific paths: + +```sh +spoom deadcode lib/ app/models/ ``` -Show type information for a specific code location: +Show files, loaded plugins, definitions, or references used during analysis: +```sh +spoom deadcode --show-files +spoom deadcode --show-plugins +spoom deadcode --show-defs +spoom deadcode --show-refs ``` -$ spoom srb lsp sig + +Remove a reported dead code candidate: + +```sh +spoom deadcode remove path/to/file.rb:42:18-47:23 ``` -### API +## Ruby API -#### Parsing Sorbet config +### Parsing Sorbet config -Parses a Sorbet config file: +Parse a Sorbet config file: ```ruby config = Spoom::Sorbet::Config.parse_file("sorbet/config") -puts config.paths # "." +puts config.paths ``` -Parses a Sorbet config string: +Parse a Sorbet config string: ```ruby config = Spoom::Sorbet::Config.parse_string(<<~CONFIG) @@ -300,30 +406,31 @@ config = Spoom::Sorbet::Config.parse_string(<<~CONFIG) --file=b --ignore=c CONFIG -puts config.paths # "a", "b" -puts config.ignore # "c" + +puts config.paths +puts config.ignore ``` -List all files typchecked by Sorbet: +List all files typechecked by Sorbet: ```ruby config = Spoom::Sorbet::Config.parse_file("sorbet/config") -puts Spoom::Sorbet.srb_files(config) +puts Spoom::Context.new(".").srb_files(with_config: config) ``` -#### Parsing Sorbet metrics +### Parsing Sorbet metrics Display metrics collected during typechecking: ```ruby -puts Spoom::Sorbet.srb_metrics(capture_err: false) +puts Spoom::Context.new(".").srb_metrics(capture_err: false) ``` -#### Interacting with LSP +### Interacting with LSP Create an LSP client: -```rb +```ruby client = Spoom::LSP::Client.new( Spoom::Sorbet::BIN_PATH, "--lsp", @@ -333,71 +440,88 @@ client = Spoom::LSP::Client.new( client.open(".") ``` -Find all the symbols matching a string: +Find symbols matching a string: -```rb +```ruby puts client.symbols("Foo") ``` -Find all the symbols for a file: +Find symbols in a file: -```rb +```ruby puts client.document_symbols("file://path/to/my/file.rb") ``` -### Backtrace Filtering +## Backtrace filtering + +Spoom provides a Minitest backtrace filter that removes Sorbet frames from test failures. -Spoom provides a backtrace filter for Minitest to remove the Sorbet frames from test failures, giving a more readable output. To enable it: +Enable it in your test helper: ```ruby # test/test_helper.rb require "spoom/backtrace_filter/minitest" + Minitest.backtrace_filter = Spoom::BacktraceFilter::Minitest.new ``` -### Dead code removal +## Development -Run dead code detection in your project with: +After checking out the repo, install dependencies: +```sh +bin/setup ``` -$ spoom deadcode -``` - -This will list all the methods and constants that do not appear to be used in your project. -You can remove them with Spoom: +Run the tests: +```sh +bin/test ``` -$ spoom deadcode remove path/to/file.rb:42:18-47:23 + +Run an interactive console: + +```sh +bin/console ``` -## Development +Run the full local sanity check before pushing: -After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Don't forget to run `bin/sanity` before pushing your changes. +```sh +bin/sanity +``` + +Install this gem locally: -To install this gem onto your local machine, run `bundle exec rake install`. +```sh +bundle exec rake install +``` ## Releasing ### Bump the gem version -- [ ] Locally, update the version number in [`version.rb`](https://github.com/Shopify/spoom/blob/main/lib/spoom/version.rb) -- [ ] Run `bundle install` to update the version number in `Gemfile.lock` -- [ ] Commit this change with the message `Bump version to vx.y.z` -- [ ] Push this change directly to main or open a PR +* [ ] Update the version number in [`lib/spoom/version.rb`](https://github.com/Shopify/spoom/blob/main/lib/spoom/version.rb) +* [ ] Run `bundle install` to update the version number in `Gemfile.lock` +* [ ] Commit the change with `Bump version to vx.y.z` +* [ ] Push the change directly to `main` or open a pull request ### Create a new tag -- [ ] Locally, create a new tag with the new version number: `git tag vx.y.z` -- [ ] Push this tag up to the remote `git push origin vx.y.z` +* [ ] Create a tag with the new version number: `git tag vx.y.z` +* [ ] Push the tag: `git push origin vx.y.z` -### Release workflow will run automatically +### Publish the release -We have a [release workflow](https://github.com/Shopify/spoom/actions/workflows/release.yml) that will publish your new gem version to rubygems.org via [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/). This workflow must be approved by a member of the Ruby and Rails Infrastructure team at Shopify before it will run. Once it is approved, it will automatically publish a new gem version to rubygems.org and create a new GitHub release. +The [release workflow](https://github.com/Shopify/spoom/actions/workflows/release.yml) publishes new gem versions to RubyGems through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/). + +A member of the Ruby and Rails Infrastructure team at Shopify must approve the workflow before it runs. Once approved, it publishes the gem and creates a GitHub release. ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/spoom. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. +Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/spoom. + +This project is intended to be a safe, welcoming space for collaboration. Contributors are expected to follow the [Contributor Covenant](http://contributor-covenant.org) code of conduct. ## License @@ -405,4 +529,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct -Everyone interacting in the Spoom project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/spoom/blob/main/CODE_OF_CONDUCT.md). +Everyone interacting in Spoom's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/spoom/blob/main/CODE_OF_CONDUCT.md).