Skip to content

Commit a2fb742

Browse files
fulfsds
andauthored
[FEATURE] New pre-commit hook: Sorbet (Issue #825) (#826)
## 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>
1 parent 9c3d118 commit a2fb742

File tree

4 files changed

+124
-28
lines changed

4 files changed

+124
-28
lines changed

README.md

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,43 @@ are stored in source control. You can also easily
1717
[add your existing hook scripts](#adding-existing-git-hooks) without writing
1818
any Ruby code.
1919

20-
* [Requirements](#requirements)
21-
* [Dependencies](#dependencies)
22-
* [Installation](#installation)
23-
* [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
24-
* [Usage](#usage)
25-
* [Continuous Integration](#continuous-integration)
26-
* [Configuration](#configuration)
27-
* [Hook Options](#hook-options)
28-
* [Hook Categories](#hook-categories)
29-
* [Gemfile](#gemfile)
30-
* [Plugin Directory](#plugin-directory)
31-
* [Signature Verification](#signature-verification)
32-
* [Built-In Hooks](#built-in-hooks)
33-
* [CommitMsg](#commitmsg)
34-
* [PostCheckout](#postcheckout)
35-
* [PostCommit](#postcommit)
36-
* [PostMerge](#postmerge)
37-
* [PostRewrite](#postrewrite)
38-
* [PreCommit](#precommit)
39-
* [PrePush](#prepush)
40-
* [PreRebase](#prerebase)
41-
* [Repo-Specific Hooks](#repo-specific-hooks)
42-
* [Adding Existing Git Hooks](#adding-existing-git-hooks)
43-
* [Security](#security)
44-
* [Contributing](#contributing)
45-
* [Community](#community)
46-
* [Changelog](#changelog)
47-
* [License](#license)
20+
- [Requirements](#requirements)
21+
- [Windows](#windows)
22+
- [Dependencies](#dependencies)
23+
- [Installation](#installation)
24+
- [Automatically Install Overcommit Hooks](#automatically-install-overcommit-hooks)
25+
- [Usage](#usage)
26+
- [Skipping Hooks](#skipping-hooks)
27+
- [Disabling Overcommit](#disabling-overcommit)
28+
- [Disabling Colorized Output](#disabling-colorized-output)
29+
- [Continuous Integration](#continuous-integration)
30+
- [Configuration](#configuration)
31+
- [Hook Options](#hook-options)
32+
- [Hook Categories](#hook-categories)
33+
- [The `ALL` Hook](#the-all-hook)
34+
- [Gemfile](#gemfile)
35+
- [Plugin Directory](#plugin-directory)
36+
- [Quiet Hook Runs](#quiet-hook-runs)
37+
- [Concurrency](#concurrency)
38+
- [Signature Verification](#signature-verification)
39+
- [Built-In Hooks](#built-in-hooks)
40+
- [CommitMsg](#commitmsg)
41+
- [PostCheckout](#postcheckout)
42+
- [PostCommit](#postcommit)
43+
- [PostMerge](#postmerge)
44+
- [PostRewrite](#postrewrite)
45+
- [PreCommit](#precommit)
46+
- [WARNING: pre-commit hooks cannot have side effects](#warning-pre-commit-hooks-cannot-have-side-effects)
47+
- [PrePush](#prepush)
48+
- [PreRebase](#prerebase)
49+
- [Repo-Specific hooks](#repo-specific-hooks)
50+
- [Adding Existing Git Hooks](#adding-existing-git-hooks)
51+
- [Security](#security)
52+
- [Disabling Signature Checking](#disabling-signature-checking)
53+
- [Contributing](#contributing)
54+
- [Community](#community)
55+
- [Changelog](#changelog)
56+
- [License](#license)
4857

4958
## Requirements
5059

@@ -561,6 +570,7 @@ issue](https://github.com/sds/overcommit/issues/238) for more details.
561570
* [SemiStandard](lib/overcommit/hook/pre_commit/semi_standard.rb)
562571
* [ShellCheck](lib/overcommit/hook/pre_commit/shell_check.rb)
563572
* [SlimLint](lib/overcommit/hook/pre_commit/slim_lint.rb)
573+
* [Sorbet](lib/overcommit/hook/pre_commit/sorbet.rb)
564574
* [Sqlint](lib/overcommit/hook/pre_commit/sqlint.rb)
565575
* [Standard](lib/overcommit/hook/pre_commit/standard.rb)
566576
* [Stylelint](lib/overcommit/hook/pre_commit/stylelint.rb)

config/default.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,14 @@ PreCommit:
790790
install_command: 'gem install slim_lint'
791791
include: '**/*.slim'
792792

793+
Sorbet:
794+
enabled: false
795+
description: 'Analyze with Sorbet'
796+
required_executable: 'srb'
797+
install_command: 'gem install sorbet'
798+
command: ['srb', 'tc']
799+
include: '**/*.rb'
800+
793801
Sqlint:
794802
enabled: false
795803
description: 'Analyze with sqlint'
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Overcommit::Hook::PreCommit
4+
# Runs 'srb tc' against any modified files.
5+
#
6+
# @see https://github.com/sorbet/sorbet
7+
class Sorbet < Base
8+
# example of output:
9+
# sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
10+
MESSAGE_REGEX = /^(?<file>[^:]+):(?<line>\d+): (?<message>.*)$/.freeze
11+
12+
def run
13+
result = execute(command, args: applicable_files)
14+
return :pass if result.success?
15+
16+
output = result.stderr.split("\n").grep(MESSAGE_REGEX)
17+
18+
extract_messages(
19+
output,
20+
MESSAGE_REGEX
21+
)
22+
end
23+
end
24+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Overcommit::Hook::PreCommit::Sorbet do
6+
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
7+
let(:context) { double('context') }
8+
subject { described_class.new(config, context) }
9+
10+
before do
11+
subject.stub(:applicable_files).and_return(%w[file1.rb file2.rb])
12+
end
13+
14+
context 'when Sorbet exits successfully' do
15+
let(:result) { double('result') }
16+
17+
before do
18+
result.stub(:success?).and_return(true)
19+
subject.stub(:execute).and_return(result)
20+
end
21+
22+
it { should pass }
23+
24+
context 'and it printed a message to stderr' do
25+
before do
26+
result.stub(:stderr).and_return("No errors! Great job.\n")
27+
end
28+
29+
it { should pass }
30+
end
31+
end
32+
33+
context 'when Sorbet exits unsucessfully' do
34+
let(:result) { double('result') }
35+
36+
before do
37+
result.stub(:success?).and_return(false)
38+
subject.stub(:execute).and_return(result)
39+
end
40+
41+
context 'and it reports an error' do
42+
before do
43+
result.stub(:stderr).and_return(normalize_indent(<<-MSG))
44+
sorbet.rb:1: Method `foo` does not exist on `T.class_of(Bar)` https://srb.help/7003
45+
5 | foo 'bar'
46+
^^^
47+
Errors: 1
48+
MSG
49+
end
50+
51+
it { should fail_hook }
52+
end
53+
end
54+
end

0 commit comments

Comments
 (0)