snitch-rails 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/lib/generators/snitch/install/install_generator.rb +21 -0
- data/lib/generators/snitch/install/templates/create_snitch_errors.rb.erb +22 -0
- data/lib/generators/snitch/install/templates/snitch.rb +16 -0
- data/lib/snitch/configuration.rb +18 -0
- data/lib/snitch/engine.rb +9 -0
- data/lib/snitch/exception_handler.rb +73 -0
- data/lib/snitch/fingerprint.rb +25 -0
- data/lib/snitch/github_client.rb +82 -0
- data/lib/snitch/jobs/report_exception_job.rb +26 -0
- data/lib/snitch/middleware.rb +18 -0
- data/lib/snitch/models/event.rb +13 -0
- data/lib/snitch/version.rb +5 -0
- data/lib/snitch.rb +30 -0
- metadata +151 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3c15ca6b5626013845df2ca3750b6b3233426c74a3bd0fe3b24e167b5da4cbd3
|
|
4
|
+
data.tar.gz: a641b8bc7ad9713d20c33cd7bd388a7d84371368e33c3f4dfaf5b383b94da267
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d2ae2b490cc8ebecff6e82b4f73143e6937f482dfa478582bc94292e6d9e513f1c0a11ca40de50ec2b793dbad6097b088e604dd2ef2e220c1bdf655ac2e25cae
|
|
7
|
+
data.tar.gz: a2f09e55285231f04746b6917961bbab1832786b0dd8811a688449e8ec706716cb568aa6b5a01c383ff187a94eb02678ecae2b8a9f967fa51cf57e5ac9850896
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RiseKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Snitch
|
|
2
|
+
|
|
3
|
+
Snitch automatically catches unhandled exceptions in your Rails application, persists them to the database, and reports them as GitHub issues that @mention Claude for automated investigation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add Snitch to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "snitch"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run bundle install:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run the install generator to create the migration and initializer:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
rails generate snitch:install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run the migration:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
rails db:migrate
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
The generator creates an initializer at `config/initializers/snitch.rb`. Update it with your settings:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
Snitch.configure do |config|
|
|
37
|
+
# Required: GitHub personal access token with repo scope
|
|
38
|
+
config.github_token = ENV["SNITCH_GITHUB_TOKEN"]
|
|
39
|
+
|
|
40
|
+
# Required: GitHub repository in "owner/repo" format
|
|
41
|
+
config.github_repo = "your-org/your-repo"
|
|
42
|
+
|
|
43
|
+
# Who to @mention in GitHub issues (default: "@claude")
|
|
44
|
+
config.mention = "@claude"
|
|
45
|
+
|
|
46
|
+
# Enable/disable Snitch (default: true)
|
|
47
|
+
config.enabled = Rails.env.production?
|
|
48
|
+
|
|
49
|
+
# Exceptions to ignore (default: ActiveRecord::RecordNotFound, ActionController::RoutingError)
|
|
50
|
+
config.ignored_exceptions += [YourCustomError]
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### GitHub Token
|
|
55
|
+
|
|
56
|
+
Create a [personal access token](https://github.com/settings/tokens) with the `repo` scope and set it as an environment variable:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export SNITCH_GITHUB_TOKEN=ghp_your_token_here
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## How It Works
|
|
63
|
+
|
|
64
|
+
1. Rack middleware catches any unhandled exception (and re-raises it so normal error handling still applies)
|
|
65
|
+
2. The exception is fingerprinted using a SHA256 hash of the exception class and the first application backtrace line
|
|
66
|
+
3. A `snitch_errors` record is created (or updated if the same fingerprint already exists, incrementing the occurrence count)
|
|
67
|
+
4. An ActiveJob is enqueued to create a GitHub issue (or comment on the existing one for duplicate exceptions)
|
|
68
|
+
5. The GitHub issue includes the full backtrace, request context, and an @mention for investigation
|
|
69
|
+
|
|
70
|
+
## Requirements
|
|
71
|
+
|
|
72
|
+
- Ruby >= 3.1
|
|
73
|
+
- Rails >= 7.0
|
|
74
|
+
- An ActiveJob backend (Sidekiq, GoodJob, etc.) for async GitHub reporting
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
require "rails/generators/active_record"
|
|
3
|
+
|
|
4
|
+
module Snitch
|
|
5
|
+
module Generators
|
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
|
7
|
+
include ActiveRecord::Generators::Migration
|
|
8
|
+
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
def create_migration
|
|
12
|
+
migration_template "create_snitch_errors.rb.erb",
|
|
13
|
+
"db/migrate/create_snitch_errors.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_initializer
|
|
17
|
+
template "snitch.rb", "config/initializers/snitch.rb"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CreateSnitchErrors < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :snitch_errors do |t|
|
|
4
|
+
t.string :exception_class, null: false
|
|
5
|
+
t.text :message
|
|
6
|
+
t.text :backtrace
|
|
7
|
+
t.string :fingerprint, null: false
|
|
8
|
+
t.string :request_url
|
|
9
|
+
t.string :request_method
|
|
10
|
+
t.text :request_params
|
|
11
|
+
t.integer :occurrence_count, default: 1
|
|
12
|
+
t.integer :github_issue_number
|
|
13
|
+
t.string :github_issue_url
|
|
14
|
+
t.datetime :first_occurred_at
|
|
15
|
+
t.datetime :last_occurred_at
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
add_index :snitch_errors, :fingerprint, unique: true
|
|
20
|
+
add_index :snitch_errors, :exception_class
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Snitch.configure do |config|
|
|
2
|
+
# Required: GitHub personal access token
|
|
3
|
+
config.github_token = ENV["SNITCH_GITHUB_TOKEN"]
|
|
4
|
+
|
|
5
|
+
# Required: GitHub repository (owner/repo format)
|
|
6
|
+
config.github_repo = "owner/repo"
|
|
7
|
+
|
|
8
|
+
# Who to @mention in GitHub issues (default: "@claude")
|
|
9
|
+
# config.mention = "@claude"
|
|
10
|
+
|
|
11
|
+
# Enable/disable Snitch (default: true)
|
|
12
|
+
# config.enabled = Rails.env.production?
|
|
13
|
+
|
|
14
|
+
# Exceptions to ignore (default: RecordNotFound, RoutingError)
|
|
15
|
+
# config.ignored_exceptions += [CustomError]
|
|
16
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Snitch
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :github_token, :github_repo, :mention, :enabled, :ignored_exceptions
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@github_token = nil
|
|
9
|
+
@github_repo = nil
|
|
10
|
+
@mention = "@claude"
|
|
11
|
+
@enabled = true
|
|
12
|
+
@ignored_exceptions = [
|
|
13
|
+
"ActiveRecord::RecordNotFound",
|
|
14
|
+
"ActionController::RoutingError"
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Snitch
|
|
2
|
+
class ExceptionHandler
|
|
3
|
+
class << self
|
|
4
|
+
def handle(exception, env = {})
|
|
5
|
+
return unless Snitch.configuration.enabled
|
|
6
|
+
return if ignored?(exception)
|
|
7
|
+
|
|
8
|
+
fingerprint = Fingerprint.generate(exception)
|
|
9
|
+
request_data = extract_request_data(env)
|
|
10
|
+
|
|
11
|
+
record = find_or_create_record(exception, fingerprint, request_data)
|
|
12
|
+
enqueue_report(record)
|
|
13
|
+
record
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def ignored?(exception)
|
|
19
|
+
Snitch.configuration.ignored_exceptions.any? do |ignored|
|
|
20
|
+
ignored_class = ignored.is_a?(String) ? ignored.safe_constantize : ignored
|
|
21
|
+
ignored_class && exception.is_a?(ignored_class)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def extract_request_data(env)
|
|
26
|
+
return {} if env.empty?
|
|
27
|
+
request = ActionDispatch::Request.new(env) rescue nil
|
|
28
|
+
return {} unless request
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
request_url: request.url,
|
|
32
|
+
request_method: request.method,
|
|
33
|
+
request_params: filtered_params(request)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def filtered_params(request)
|
|
38
|
+
request.filtered_parameters.to_json rescue request.params.to_json rescue "{}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def find_or_create_record(exception, fingerprint, request_data)
|
|
42
|
+
existing = Event.find_by(fingerprint: fingerprint)
|
|
43
|
+
|
|
44
|
+
if existing
|
|
45
|
+
existing.update!(
|
|
46
|
+
occurrence_count: existing.occurrence_count + 1,
|
|
47
|
+
last_occurred_at: Time.current,
|
|
48
|
+
message: exception.message,
|
|
49
|
+
backtrace: exception.backtrace
|
|
50
|
+
)
|
|
51
|
+
existing
|
|
52
|
+
else
|
|
53
|
+
Event.create!(
|
|
54
|
+
exception_class: exception.class.name,
|
|
55
|
+
message: exception.message,
|
|
56
|
+
backtrace: exception.backtrace,
|
|
57
|
+
fingerprint: fingerprint,
|
|
58
|
+
occurrence_count: 1,
|
|
59
|
+
first_occurred_at: Time.current,
|
|
60
|
+
last_occurred_at: Time.current,
|
|
61
|
+
**request_data
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def enqueue_report(record)
|
|
67
|
+
ReportExceptionJob.perform_later(record.id)
|
|
68
|
+
rescue => e
|
|
69
|
+
Rails.logger.error("[Snitch] Failed to enqueue report job: #{e.message}") if defined?(Rails)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Snitch
|
|
6
|
+
module Fingerprint
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def generate(exception)
|
|
10
|
+
app_line = first_app_backtrace_line(exception)
|
|
11
|
+
source = "#{exception.class.name}:#{app_line}"
|
|
12
|
+
Digest::SHA256.hexdigest(source)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first_app_backtrace_line(exception)
|
|
16
|
+
return "" unless exception.backtrace
|
|
17
|
+
|
|
18
|
+
exception.backtrace.find { |line| app_line?(line) } || ""
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def app_line?(line)
|
|
22
|
+
!line.include?("/gems/") && !line.include?("ruby/")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "octokit"
|
|
4
|
+
|
|
5
|
+
module Snitch
|
|
6
|
+
class GitHubClient
|
|
7
|
+
def initialize
|
|
8
|
+
@client = Octokit::Client.new(access_token: Snitch.configuration.github_token)
|
|
9
|
+
@repo = Snitch.configuration.github_repo
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_issue(event)
|
|
13
|
+
title = build_title(event)
|
|
14
|
+
body = build_issue_body(event)
|
|
15
|
+
|
|
16
|
+
issue = @client.create_issue(@repo, title, body, labels: ["snitch", "bug"])
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
number: issue.number,
|
|
20
|
+
url: issue.html_url
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def comment_on_issue(event)
|
|
25
|
+
body = build_comment_body(event)
|
|
26
|
+
@client.add_comment(@repo, event.github_issue_number, body)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def build_title(record)
|
|
32
|
+
msg = record.message.to_s.truncate(100)
|
|
33
|
+
"[Snitch] #{record.exception_class}: #{msg}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_issue_body(record)
|
|
37
|
+
mention = Snitch.configuration.mention
|
|
38
|
+
<<~MARKDOWN
|
|
39
|
+
## Exception Details
|
|
40
|
+
**Class:** `#{record.exception_class}`
|
|
41
|
+
**Message:** #{record.message}
|
|
42
|
+
**Occurrences:** #{record.occurrence_count}
|
|
43
|
+
**First seen:** #{record.first_occurred_at&.utc}
|
|
44
|
+
|
|
45
|
+
## Backtrace
|
|
46
|
+
```ruby
|
|
47
|
+
#{format_backtrace(record.backtrace)}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Request Context
|
|
51
|
+
- **URL:** #{record.request_method} #{record.request_url}
|
|
52
|
+
- **Params:** `#{record.request_params}`
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
#{mention} Please investigate this exception. Analyze the backtrace, identify the root cause, and suggest a fix.
|
|
56
|
+
MARKDOWN
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def build_comment_body(record)
|
|
60
|
+
mention = Snitch.configuration.mention
|
|
61
|
+
<<~MARKDOWN
|
|
62
|
+
## New Occurrence
|
|
63
|
+
**Total occurrences:** #{record.occurrence_count}
|
|
64
|
+
**Latest occurrence:** #{record.last_occurred_at&.utc}
|
|
65
|
+
|
|
66
|
+
## Latest Request Context
|
|
67
|
+
- **URL:** #{record.request_method} #{record.request_url}
|
|
68
|
+
- **Params:** `#{record.request_params}`
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
#{mention} This exception has occurred again. Please review if the previous analysis still applies.
|
|
72
|
+
MARKDOWN
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def format_backtrace(backtrace)
|
|
76
|
+
return "No backtrace available" if backtrace.nil? || backtrace.empty?
|
|
77
|
+
|
|
78
|
+
lines = backtrace.is_a?(Array) ? backtrace : JSON.parse(backtrace) rescue [backtrace.to_s]
|
|
79
|
+
lines.first(20).join("\n")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Snitch
|
|
4
|
+
class ReportExceptionJob < ActiveJob::Base
|
|
5
|
+
queue_as :default
|
|
6
|
+
|
|
7
|
+
retry_on StandardError, wait: :polynomially_longer, attempts: 3
|
|
8
|
+
|
|
9
|
+
def perform(event_id)
|
|
10
|
+
record = Event.find_by(id: event_id)
|
|
11
|
+
return unless record
|
|
12
|
+
|
|
13
|
+
client = GitHubClient.new
|
|
14
|
+
|
|
15
|
+
if record.github_issue_number.present?
|
|
16
|
+
client.comment_on_issue(record)
|
|
17
|
+
else
|
|
18
|
+
result = client.create_issue(record)
|
|
19
|
+
record.update!(
|
|
20
|
+
github_issue_number: result[:number],
|
|
21
|
+
github_issue_url: result[:url]
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Snitch
|
|
2
|
+
class Middleware
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
@app.call(env)
|
|
9
|
+
rescue Exception => e
|
|
10
|
+
begin
|
|
11
|
+
ExceptionHandler.handle(e, env)
|
|
12
|
+
rescue => handler_error
|
|
13
|
+
Rails.logger.error("[Snitch] Handler error: #{handler_error.message}") if defined?(Rails)
|
|
14
|
+
end
|
|
15
|
+
raise e
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Snitch
|
|
2
|
+
class Event < ActiveRecord::Base
|
|
3
|
+
self.table_name = "snitch_errors"
|
|
4
|
+
|
|
5
|
+
serialize :backtrace, coder: JSON
|
|
6
|
+
serialize :request_params, coder: JSON
|
|
7
|
+
|
|
8
|
+
validates :exception_class, presence: true
|
|
9
|
+
validates :fingerprint, presence: true
|
|
10
|
+
|
|
11
|
+
scope :by_fingerprint, ->(fp) { where(fingerprint: fp) }
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/snitch.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "snitch/version"
|
|
4
|
+
require "snitch/configuration"
|
|
5
|
+
require "snitch/fingerprint"
|
|
6
|
+
require "snitch/middleware"
|
|
7
|
+
require "snitch/exception_handler"
|
|
8
|
+
require "snitch/github_client"
|
|
9
|
+
require "snitch/jobs/report_exception_job" if defined?(ActiveJob)
|
|
10
|
+
require "snitch/engine" if defined?(Rails)
|
|
11
|
+
|
|
12
|
+
module Snitch
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
attr_writer :configuration
|
|
17
|
+
|
|
18
|
+
def configuration
|
|
19
|
+
@configuration ||= Configuration.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configure
|
|
23
|
+
yield(configuration)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def reset!
|
|
27
|
+
@configuration = Configuration.new
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: snitch-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- RiseKit
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: octokit
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '9.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '9.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec-rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: webmock
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: factory_bot_rails
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: sqlite3
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: faraday-retry
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
description: Snitch catches unhandled exceptions in your Rails app and opens GitHub
|
|
111
|
+
issues with full context.
|
|
112
|
+
executables: []
|
|
113
|
+
extensions: []
|
|
114
|
+
extra_rdoc_files: []
|
|
115
|
+
files:
|
|
116
|
+
- LICENSE.txt
|
|
117
|
+
- README.md
|
|
118
|
+
- lib/generators/snitch/install/install_generator.rb
|
|
119
|
+
- lib/generators/snitch/install/templates/create_snitch_errors.rb.erb
|
|
120
|
+
- lib/generators/snitch/install/templates/snitch.rb
|
|
121
|
+
- lib/snitch.rb
|
|
122
|
+
- lib/snitch/configuration.rb
|
|
123
|
+
- lib/snitch/engine.rb
|
|
124
|
+
- lib/snitch/exception_handler.rb
|
|
125
|
+
- lib/snitch/fingerprint.rb
|
|
126
|
+
- lib/snitch/github_client.rb
|
|
127
|
+
- lib/snitch/jobs/report_exception_job.rb
|
|
128
|
+
- lib/snitch/middleware.rb
|
|
129
|
+
- lib/snitch/models/event.rb
|
|
130
|
+
- lib/snitch/version.rb
|
|
131
|
+
licenses:
|
|
132
|
+
- MIT
|
|
133
|
+
metadata: {}
|
|
134
|
+
rdoc_options: []
|
|
135
|
+
require_paths:
|
|
136
|
+
- lib
|
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
|
+
requirements:
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: 3.1.0
|
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
requirements: []
|
|
148
|
+
rubygems_version: 4.0.3
|
|
149
|
+
specification_version: 4
|
|
150
|
+
summary: Automatic GitHub issue creation for unhandled Rails exceptions.
|
|
151
|
+
test_files: []
|