Skip to content

Commit

Permalink
[FEATURE] New pre-commit hook: Sorbet (Issue #825) (#826)
Browse files Browse the repository at this point in the history
## Introduction
This pre-commit hook runs the Sorbet type-checker.
[More information about Sorbet here](https://sorbet.org/docs/overview)

Fixes #825 

## Requirements
[Sorbet](https://github.com/sorbet/sorbet)

Notes: not sure if this should be a pre-commit hook or pre-push hook to
be honest.

Co-authored-by: Shane da Silva <shane@dasilva.io>
  • Loading branch information
fulf and sds authored Feb 25, 2024
1 parent 9c3d118 commit a2fb742
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 28 deletions.
66 changes: 38 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,43 @@ are stored in source control. You can also easily
[add your existing hook scripts](#adding-existing-git-hooks) without writing
any Ruby code.

* [Requirements](#requirements)
* [Dependencies](#dependencies)
* [Installation](#installation)
* [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
* [Usage](#usage)
* [Continuous Integration](#continuous-integration)
* [Configuration](#configuration)
* [Hook Options](#hook-options)
* [Hook Categories](#hook-categories)
* [Gemfile](#gemfile)
* [Plugin Directory](#plugin-directory)
* [Signature Verification](#signature-verification)
* [Built-In Hooks](#built-in-hooks)
* [CommitMsg](#commitmsg)
* [PostCheckout](#postcheckout)
* [PostCommit](#postcommit)
* [PostMerge](#postmerge)
* [PostRewrite](#postrewrite)
* [PreCommit](#precommit)
* [PrePush](#prepush)
* [PreRebase](#prerebase)
* [Repo-Specific Hooks](#repo-specific-hooks)
* [Adding Existing Git Hooks](#adding-existing-git-hooks)
* [Security](#security)
* [Contributing](#contributing)
* [Community](#community)
* [Changelog](#changelog)
* [License](#license)
- [Requirements](#requirements)
- [Windows](#windows)
- [Dependencies](#dependencies)
- [Installation](#installation)
- [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
- [Usage](#usage)
- [Skipping Hooks](#skipping-hooks)
- [Disabling Overcommit](#disabling-overcommit)
- [Disabling Colorized Output](#disabling-colorized-output)
- [Continuous Integration](#continuous-integration)
- [Configuration](#configuration)
- [Hook Options](#hook-options)
- [Hook Categories](#hook-categories)
- [The `ALL` Hook](#the-all-hook)
- [Gemfile](#gemfile)
- [Plugin Directory](#plugin-directory)
- [Quiet Hook Runs](#quiet-hook-runs)
- [Concurrency](#concurrency)
- [Signature Verification](#signature-verification)
- [Built-In Hooks](#built-in-hooks)
- [CommitMsg](#commitmsg)
- [PostCheckout](#postcheckout)
- [PostCommit](#postcommit)
- [PostMerge](#postmerge)
- [PostRewrite](#postrewrite)
- [PreCommit](#precommit)
- [WARNING: pre-commit hooks cannot have side effects](#warning-pre-commit-hooks-cannot-have-side-effects)
- [PrePush](#prepush)
- [PreRebase](#prerebase)
- [Repo-Specific hooks](#repo-specific-hooks)
- [Adding Existing Git Hooks](#adding-existing-git-hooks)
- [Security](#security)
- [Disabling Signature Checking](#disabling-signature-checking)
- [Contributing](#contributing)
- [Community](#community)
- [Changelog](#changelog)
- [License](#license)

## Requirements

Expand Down Expand Up @@ -561,6 +570,7 @@ issue](https://github.com/sds/overcommit/issues/238) for more details.
* [SemiStandard](lib/overcommit/hook/pre_commit/semi_standard.rb)
* [ShellCheck](lib/overcommit/hook/pre_commit/shell_check.rb)
* [SlimLint](lib/overcommit/hook/pre_commit/slim_lint.rb)
* [Sorbet](lib/overcommit/hook/pre_commit/sorbet.rb)
* [Sqlint](lib/overcommit/hook/pre_commit/sqlint.rb)
* [Standard](lib/overcommit/hook/pre_commit/standard.rb)
* [Stylelint](lib/overcommit/hook/pre_commit/stylelint.rb)
Expand Down
8 changes: 8 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,14 @@ PreCommit:
install_command: 'gem install slim_lint'
include: '**/*.slim'

Sorbet:
enabled: false
description: 'Analyze with Sorbet'
required_executable: 'srb'
install_command: 'gem install sorbet'
command: ['srb', 'tc']
include: '**/*.rb'

Sqlint:
enabled: false
description: 'Analyze with sqlint'
Expand Down
24 changes: 24 additions & 0 deletions lib/overcommit/hook/pre_commit/sorbet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Overcommit::Hook::PreCommit
# Runs 'srb tc' against any modified files.
#
# @see https://github.com/sorbet/sorbet
class Sorbet < Base
# example of output:
# sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
MESSAGE_REGEX = /^(?<file>[^:]+):(?<line>\d+): (?<message>.*)$/.freeze

def run
result = execute(command, args: applicable_files)
return :pass if result.success?

output = result.stderr.split("\n").grep(MESSAGE_REGEX)

extract_messages(
output,
MESSAGE_REGEX
)
end
end
end
54 changes: 54 additions & 0 deletions spec/overcommit/hook/pre_commit/sorbet_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

require 'spec_helper'

describe Overcommit::Hook::PreCommit::Sorbet do
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
let(:context) { double('context') }
subject { described_class.new(config, context) }

before do
subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])
end

context 'when Sorbet exits successfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(true)
subject.stub(:execute).and_return(result)
end

it { should pass }

context 'and it printed a message to stderr' do
before do
result.stub(:stderr).and_return("No errors! Great job.\n")
end

it { should pass }
end
end

context 'when Sorbet exits unsucessfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(false)
subject.stub(:execute).and_return(result)
end

context 'and it reports an error' do
before do
result.stub(:stderr).and_return(normalize_indent(<<-MSG))
sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
5 | foo 'bar'
^^^
Errors: 1
MSG
end

it { should fail_hook }
end
end
end

0 comments on commit a2fb742

Please sign in to comment.