rails_error_dashboard 0.5.9 → 0.5.10
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 +4 -4
- data/README.md +26 -6
- data/app/controllers/rails_error_dashboard/errors_controller.rb +10 -0
- data/app/controllers/rails_error_dashboard/webhooks_controller.rb +2 -1
- data/app/views/layouts/rails_error_dashboard.html.erb +5 -0
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +14 -1
- data/app/views/rails_error_dashboard/errors/releases.html.erb +284 -0
- data/app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb +15 -0
- data/app/views/rails_error_dashboard/errors/settings.html.erb +32 -0
- data/config/routes.rb +1 -0
- data/lib/generators/rails_error_dashboard/install/templates/initializer.rb +32 -2
- data/lib/rails_error_dashboard/configuration.rb +10 -13
- data/lib/rails_error_dashboard/queries/release_timeline.rb +181 -0
- data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +4 -6
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3004ee5b49b622c064ceb8103c6abb2212f1a0cc269471a7281b49338213240
|
|
4
|
+
data.tar.gz: a8e27db79b78052909acc3ce04ab4875b31148b6e50b631b46bfe67eb0562fd3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2dfe54a84c8a7049e6b51956d3512c6c18f8d37cabdd5f655bbdb046b6ed7ce2b287886f80a36a7bba85839090309eb81e823e29b5b766036f9b7ba726f7fef
|
|
7
|
+
data.tar.gz: fdcf6aea6b7f568da4a525330e63de6121aa087926ffd8982c631ec90efa5282081d428c7fd9069b4ed923f31a1c1445483c443f4b845d4677648b10c4d56fc4
|
data/README.md
CHANGED
|
@@ -150,23 +150,43 @@ config.enable_activestorage_tracking = true # requires enable_breadcrumbs = tru
|
|
|
150
150
|
<details>
|
|
151
151
|
<summary><strong>Issue Tracking — GitHub, GitLab, Codeberg</strong></summary>
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
One switch connects errors to your issue tracker. Platform becomes the source of truth — status, assignees, labels, and comments are mirrored live in the dashboard.
|
|
154
154
|
|
|
155
|
-
- **
|
|
156
|
-
- **Auto-create:**
|
|
157
|
-
- **Lifecycle sync:** Resolve → close
|
|
158
|
-
- **
|
|
155
|
+
- **Create & link:** "Create Issue" button or paste an existing URL
|
|
156
|
+
- **Auto-create:** New errors auto-create issues. Critical/high severity always creates
|
|
157
|
+
- **Lifecycle sync:** Resolve → close, recur → reopen + comment, all via background jobs
|
|
158
|
+
- **Platform mirror:** Issue state, assignees (with avatars), labels (with colors), and comments displayed in the dashboard. Workflow controls (Resolve, Assign, Priority) replaced by platform state
|
|
159
|
+
- **Two-way webhooks:** Issue closed/reopened on platform syncs back to dashboard
|
|
159
160
|
- **RED branding:** Issues show "Created by RED (Rails Error Dashboard)"
|
|
160
161
|
|
|
161
162
|
```ruby
|
|
162
163
|
config.enable_issue_tracking = true
|
|
163
164
|
config.issue_tracker_token = ENV["RED_BOT_TOKEN"]
|
|
164
|
-
#
|
|
165
|
+
# That's it — provider and repo auto-detected from git_repository_url
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
[Complete documentation →](docs/guides/CONFIGURATION.md)
|
|
168
169
|
</details>
|
|
169
170
|
|
|
171
|
+
<details>
|
|
172
|
+
<summary><strong>Release Tracking</strong></summary>
|
|
173
|
+
|
|
174
|
+
Dedicated Releases page at `/errors/releases` shows a timeline of all deploys/versions with health stats. Answers: "Did this deploy introduce new errors?" and "Is this release stable?"
|
|
175
|
+
|
|
176
|
+
- **Release timeline:** Every version seen, sorted newest-first, with error counts, unique types, and time range
|
|
177
|
+
- **"New in this release":** Errors whose fingerprint first appeared in each version — flagged with a red badge
|
|
178
|
+
- **Stability indicators:** Green (at or below average), yellow (1-2x), red (>2x average error rate)
|
|
179
|
+
- **Release comparison:** Delta and percentage change vs the previous release
|
|
180
|
+
- **Current release:** Highlighted card with live health stats
|
|
181
|
+
- **Zero config:** Works automatically when `app_version` or `git_sha` is set (via config, `APP_VERSION`, `GIT_SHA`, `HEROKU_SLUG_COMMIT`, or `RENDER_GIT_COMMIT` env vars)
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
config.app_version = "1.2.0" # or set APP_VERSION env var
|
|
185
|
+
config.git_sha = ENV["GIT_SHA"] # auto-detected on Heroku/Render
|
|
186
|
+
config.git_repository_url = "https://github.com/user/repo" # enables SHA links
|
|
187
|
+
```
|
|
188
|
+
</details>
|
|
189
|
+
|
|
170
190
|
<details>
|
|
171
191
|
<summary><strong>Source Code Integration + Git Blame</strong></summary>
|
|
172
192
|
|
|
@@ -276,6 +276,16 @@ module RailsErrorDashboard
|
|
|
276
276
|
@platform_specific_errors = correlation.platform_specific_errors
|
|
277
277
|
end
|
|
278
278
|
|
|
279
|
+
def releases
|
|
280
|
+
days = (params[:days] || 30).to_i
|
|
281
|
+
@days = days
|
|
282
|
+
result = Queries::ReleaseTimeline.call(days, application_id: @current_application_id)
|
|
283
|
+
all_releases = result[:releases]
|
|
284
|
+
@summary = result[:summary]
|
|
285
|
+
|
|
286
|
+
@pagy, @releases = pagy(:offset, all_releases, limit: params[:per_page] || 25)
|
|
287
|
+
end
|
|
288
|
+
|
|
279
289
|
def deprecations
|
|
280
290
|
unless RailsErrorDashboard.configuration.enable_breadcrumbs
|
|
281
291
|
flash[:alert] = "Breadcrumbs are not enabled. Enable them in config/initializers/rails_error_dashboard.rb"
|
|
@@ -43,7 +43,8 @@ module RailsErrorDashboard
|
|
|
43
43
|
private
|
|
44
44
|
|
|
45
45
|
def verify_webhook_enabled
|
|
46
|
-
|
|
46
|
+
config = RailsErrorDashboard.configuration
|
|
47
|
+
unless config.enable_issue_tracking && config.issue_webhook_secret.present?
|
|
47
48
|
head :not_found
|
|
48
49
|
end
|
|
49
50
|
end
|
|
@@ -1651,6 +1651,11 @@ body.dark-mode .sidebar-section-body { background: var(--ctp-mantle); }
|
|
|
1651
1651
|
<i class="bi bi-diagram-3"></i> Correlation
|
|
1652
1652
|
<% end %>
|
|
1653
1653
|
</li>
|
|
1654
|
+
<li class="nav-item">
|
|
1655
|
+
<%= link_to releases_errors_path(nav_params), class: "nav-link #{request.path == releases_errors_path ? 'active' : ''}" do %>
|
|
1656
|
+
<i class="bi bi-rocket-takeoff"></i> Releases
|
|
1657
|
+
<% end %>
|
|
1658
|
+
</li>
|
|
1654
1659
|
<li class="nav-item">
|
|
1655
1660
|
<%= link_to settings_path(nav_params), class: "nav-link #{request.path == settings_path ? 'active' : ''}" do %>
|
|
1656
1661
|
<i class="bi bi-gear"></i> Settings
|
|
@@ -71,8 +71,21 @@
|
|
|
71
71
|
<% end %>
|
|
72
72
|
</div>
|
|
73
73
|
|
|
74
|
+
<% if RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
75
|
+
<div class="mb-3">
|
|
76
|
+
<small class="text-muted" style="font-size: 0.78em;">
|
|
77
|
+
<i class="bi bi-info-circle me-1"></i>
|
|
78
|
+
Status, assignment, and priority are managed via your
|
|
79
|
+
<% if error.respond_to?(:external_issue_url) && error.external_issue_url.present? %>
|
|
80
|
+
<a href="<%= error.external_issue_url %>" target="_blank" rel="noopener">linked issue</a>.
|
|
81
|
+
<% else %>
|
|
82
|
+
issue tracker. Link an issue to see details.
|
|
83
|
+
<% end %>
|
|
84
|
+
</small>
|
|
85
|
+
</div>
|
|
86
|
+
<% end %>
|
|
74
87
|
<% unless RailsErrorDashboard.configuration.enable_issue_tracking %>
|
|
75
|
-
<!-- Workflow Status
|
|
88
|
+
<!-- Workflow Status -->
|
|
76
89
|
<div class="mb-3">
|
|
77
90
|
<small class="metadata-label d-block mb-1">Workflow Status</small>
|
|
78
91
|
<% if error.respond_to?(:status) %>
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
<% content_for :page_title, "Releases" %>
|
|
2
|
+
|
|
3
|
+
<div class="container-fluid py-4">
|
|
4
|
+
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
5
|
+
<h1 class="h3 mb-0">
|
|
6
|
+
<i class="bi bi-rocket-takeoff me-2"></i>
|
|
7
|
+
Releases
|
|
8
|
+
</h1>
|
|
9
|
+
|
|
10
|
+
<div class="btn-group" role="group">
|
|
11
|
+
<%= link_to releases_errors_path(days: 7), class: "btn btn-sm #{@days == 7 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
12
|
+
7 Days
|
|
13
|
+
<% end %>
|
|
14
|
+
<%= link_to releases_errors_path(days: 30), class: "btn btn-sm #{@days == 30 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
15
|
+
30 Days
|
|
16
|
+
<% end %>
|
|
17
|
+
<%= link_to releases_errors_path(days: 90), class: "btn btn-sm #{@days == 90 ? 'btn-primary' : 'btn-outline-primary'}" do %>
|
|
18
|
+
90 Days
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<% if @releases.empty? %>
|
|
24
|
+
<div class="text-center py-5">
|
|
25
|
+
<i class="bi bi-rocket-takeoff display-1 text-muted mb-3"></i>
|
|
26
|
+
<h4 class="text-muted">No Release Data Found</h4>
|
|
27
|
+
<p class="text-muted">
|
|
28
|
+
No version data was detected in errors over the last <%= @days %> days.
|
|
29
|
+
</p>
|
|
30
|
+
<div class="card mx-auto" style="max-width: 500px;">
|
|
31
|
+
<div class="card-body text-start">
|
|
32
|
+
<h6>How to enable release tracking:</h6>
|
|
33
|
+
<ul class="mb-0">
|
|
34
|
+
<li>Set <code>config.app_version</code> in your initializer</li>
|
|
35
|
+
<li>Or set the <code>APP_VERSION</code> environment variable</li>
|
|
36
|
+
<li>Git SHA is auto-detected from <code>GIT_SHA</code>, <code>HEROKU_SLUG_COMMIT</code>, or <code>RENDER_GIT_COMMIT</code></li>
|
|
37
|
+
<li>Version and SHA are captured automatically when errors occur</li>
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<% else %>
|
|
43
|
+
<%
|
|
44
|
+
current_release = @releases.find { |r| r[:current] }
|
|
45
|
+
%>
|
|
46
|
+
|
|
47
|
+
<!-- Summary Cards -->
|
|
48
|
+
<div class="row mb-4">
|
|
49
|
+
<div class="col-md-3">
|
|
50
|
+
<div class="card text-center">
|
|
51
|
+
<div class="card-body">
|
|
52
|
+
<div class="display-6 text-primary"><%= @summary[:total_releases] %></div>
|
|
53
|
+
<small class="text-muted">Releases</small>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="col-md-3">
|
|
58
|
+
<div class="card text-center">
|
|
59
|
+
<div class="card-body">
|
|
60
|
+
<div class="display-6"><code><%= @summary[:current_version] || "N/A" %></code></div>
|
|
61
|
+
<small class="text-muted">Current Release</small>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="col-md-3">
|
|
66
|
+
<div class="card text-center">
|
|
67
|
+
<div class="card-body">
|
|
68
|
+
<div class="display-6 text-warning"><%= @summary[:avg_errors_per_release] %></div>
|
|
69
|
+
<small class="text-muted">Avg Errors / Release</small>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="col-md-3">
|
|
74
|
+
<div class="card text-center">
|
|
75
|
+
<div class="card-body">
|
|
76
|
+
<%
|
|
77
|
+
problematic_count = @releases.count { |r| r[:problematic] }
|
|
78
|
+
%>
|
|
79
|
+
<div class="display-6 <%= problematic_count > 0 ? 'text-danger' : 'text-success' %>"><%= problematic_count %></div>
|
|
80
|
+
<small class="text-muted">Problematic Releases</small>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<% if current_release %>
|
|
87
|
+
<!-- Current Release Card -->
|
|
88
|
+
<div class="card mb-4 border-primary">
|
|
89
|
+
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
|
90
|
+
<h5 class="mb-0">
|
|
91
|
+
<i class="bi bi-broadcast"></i> Current Release: <strong><%= current_release[:version] %></strong>
|
|
92
|
+
</h5>
|
|
93
|
+
<span class="badge bg-light text-primary">Live</span>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="card-body">
|
|
96
|
+
<div class="row text-center">
|
|
97
|
+
<div class="col-md-2">
|
|
98
|
+
<div class="h4 mb-0"><%= current_release[:total_errors] %></div>
|
|
99
|
+
<small class="text-muted">Errors</small>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="col-md-2">
|
|
102
|
+
<div class="h4 mb-0"><%= current_release[:unique_error_types] %></div>
|
|
103
|
+
<small class="text-muted">Error Types</small>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="col-md-2">
|
|
106
|
+
<div class="h4 mb-0">
|
|
107
|
+
<% if current_release[:new_error_count] > 0 %>
|
|
108
|
+
<span class="text-danger"><%= current_release[:new_error_count] %></span>
|
|
109
|
+
<% else %>
|
|
110
|
+
<span class="text-success">0</span>
|
|
111
|
+
<% end %>
|
|
112
|
+
</div>
|
|
113
|
+
<small class="text-muted">New Errors</small>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="col-md-2">
|
|
116
|
+
<%
|
|
117
|
+
stability_color = case current_release[:stability]
|
|
118
|
+
when :green then "success"
|
|
119
|
+
when :yellow then "warning"
|
|
120
|
+
when :red then "danger"
|
|
121
|
+
else "secondary"
|
|
122
|
+
end
|
|
123
|
+
%>
|
|
124
|
+
<div class="h4 mb-0">
|
|
125
|
+
<span class="badge bg-<%= stability_color %>">
|
|
126
|
+
<%= current_release[:stability].to_s.capitalize %>
|
|
127
|
+
</span>
|
|
128
|
+
</div>
|
|
129
|
+
<small class="text-muted">Stability</small>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="col-md-2">
|
|
132
|
+
<% if current_release[:git_shas].any? %>
|
|
133
|
+
<div class="h4 mb-0"><%= git_commit_link(current_release[:git_shas].first) %></div>
|
|
134
|
+
<% else %>
|
|
135
|
+
<div class="h4 mb-0 text-muted">N/A</div>
|
|
136
|
+
<% end %>
|
|
137
|
+
<small class="text-muted">Git SHA</small>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="col-md-2">
|
|
140
|
+
<% if current_release[:delta_from_previous] %>
|
|
141
|
+
<%
|
|
142
|
+
delta = current_release[:delta_from_previous]
|
|
143
|
+
delta_class = delta > 0 ? "text-danger" : (delta < 0 ? "text-success" : "text-muted")
|
|
144
|
+
delta_icon = delta > 0 ? "bi-arrow-up" : (delta < 0 ? "bi-arrow-down" : "bi-dash")
|
|
145
|
+
%>
|
|
146
|
+
<div class="h4 mb-0 <%= delta_class %>">
|
|
147
|
+
<i class="bi <%= delta_icon %>"></i> <%= delta.abs %>
|
|
148
|
+
</div>
|
|
149
|
+
<% else %>
|
|
150
|
+
<div class="h4 mb-0 text-muted">—</div>
|
|
151
|
+
<% end %>
|
|
152
|
+
<small class="text-muted">vs Previous</small>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<% end %>
|
|
158
|
+
|
|
159
|
+
<!-- Release Timeline Table -->
|
|
160
|
+
<div class="card mb-4">
|
|
161
|
+
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
162
|
+
<h5 class="mb-0">
|
|
163
|
+
<i class="bi bi-clock-history text-primary me-2"></i>
|
|
164
|
+
Release Timeline
|
|
165
|
+
<span class="badge bg-primary"><%= @summary[:total_releases] %></span>
|
|
166
|
+
</h5>
|
|
167
|
+
<small class="text-muted"><%== @pagy.info_tag %></small>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="card-body p-0">
|
|
170
|
+
<div class="table-responsive">
|
|
171
|
+
<table class="table table-hover mb-0">
|
|
172
|
+
<thead class="table-light">
|
|
173
|
+
<tr>
|
|
174
|
+
<th>Version</th>
|
|
175
|
+
<th>Git SHA</th>
|
|
176
|
+
<th>Time Range</th>
|
|
177
|
+
<th width="100">Errors</th>
|
|
178
|
+
<th width="100">Types</th>
|
|
179
|
+
<th width="120">New Errors</th>
|
|
180
|
+
<th width="100">Stability</th>
|
|
181
|
+
<th width="120">vs Previous</th>
|
|
182
|
+
</tr>
|
|
183
|
+
</thead>
|
|
184
|
+
<tbody>
|
|
185
|
+
<% @releases.each do |release| %>
|
|
186
|
+
<tr class="<%= 'table-active' if release[:current] %>">
|
|
187
|
+
<td>
|
|
188
|
+
<strong><%= release[:version] %></strong>
|
|
189
|
+
<% if release[:current] %>
|
|
190
|
+
<span class="badge bg-primary ms-1">Current</span>
|
|
191
|
+
<% end %>
|
|
192
|
+
<% if release[:problematic] %>
|
|
193
|
+
<span class="badge bg-danger ms-1">Problematic</span>
|
|
194
|
+
<% end %>
|
|
195
|
+
</td>
|
|
196
|
+
<td>
|
|
197
|
+
<% if release[:git_shas].any? %>
|
|
198
|
+
<%= git_commit_link(release[:git_shas].first) %>
|
|
199
|
+
<% if release[:git_shas].size > 1 %>
|
|
200
|
+
<small class="text-muted">+<%= release[:git_shas].size - 1 %></small>
|
|
201
|
+
<% end %>
|
|
202
|
+
<% else %>
|
|
203
|
+
<span class="text-muted">N/A</span>
|
|
204
|
+
<% end %>
|
|
205
|
+
</td>
|
|
206
|
+
<td>
|
|
207
|
+
<small>
|
|
208
|
+
<%= local_time_ago(release[:first_seen]) %>
|
|
209
|
+
<% unless release[:first_seen] == release[:last_seen] %>
|
|
210
|
+
→ <%= local_time_ago(release[:last_seen]) %>
|
|
211
|
+
<% end %>
|
|
212
|
+
</small>
|
|
213
|
+
</td>
|
|
214
|
+
<td>
|
|
215
|
+
<span class="badge bg-secondary"><%= release[:total_errors] %></span>
|
|
216
|
+
</td>
|
|
217
|
+
<td>
|
|
218
|
+
<span class="badge bg-info"><%= release[:unique_error_types] %></span>
|
|
219
|
+
</td>
|
|
220
|
+
<td>
|
|
221
|
+
<% if release[:new_error_count] > 0 %>
|
|
222
|
+
<span class="badge bg-danger">
|
|
223
|
+
<i class="bi bi-exclamation-circle"></i> <%= release[:new_error_count] %> new
|
|
224
|
+
</span>
|
|
225
|
+
<% else %>
|
|
226
|
+
<span class="badge bg-success">
|
|
227
|
+
<i class="bi bi-check-circle"></i> 0 new
|
|
228
|
+
</span>
|
|
229
|
+
<% end %>
|
|
230
|
+
</td>
|
|
231
|
+
<td>
|
|
232
|
+
<%
|
|
233
|
+
stability_color = case release[:stability]
|
|
234
|
+
when :green then "success"
|
|
235
|
+
when :yellow then "warning"
|
|
236
|
+
when :red then "danger"
|
|
237
|
+
else "secondary"
|
|
238
|
+
end
|
|
239
|
+
%>
|
|
240
|
+
<span class="badge bg-<%= stability_color %>">
|
|
241
|
+
<i class="bi bi-circle-fill" style="font-size: 0.5em; vertical-align: middle;"></i>
|
|
242
|
+
<%= release[:stability].to_s.capitalize %>
|
|
243
|
+
</span>
|
|
244
|
+
</td>
|
|
245
|
+
<td>
|
|
246
|
+
<% if release[:delta_from_previous] %>
|
|
247
|
+
<%
|
|
248
|
+
delta = release[:delta_from_previous]
|
|
249
|
+
pct = release[:delta_percentage]
|
|
250
|
+
delta_class = delta > 0 ? "text-danger" : (delta < 0 ? "text-success" : "text-muted")
|
|
251
|
+
delta_icon = delta > 0 ? "bi-arrow-up" : (delta < 0 ? "bi-arrow-down" : "bi-dash")
|
|
252
|
+
%>
|
|
253
|
+
<span class="<%= delta_class %>">
|
|
254
|
+
<i class="bi <%= delta_icon %>"></i>
|
|
255
|
+
<%= delta > 0 ? "+#{delta}" : delta %>
|
|
256
|
+
<% if pct %>
|
|
257
|
+
<small>(<%= pct > 0 ? "+#{pct}" : pct %>%)</small>
|
|
258
|
+
<% end %>
|
|
259
|
+
</span>
|
|
260
|
+
<% else %>
|
|
261
|
+
<span class="text-muted">—</span>
|
|
262
|
+
<% end %>
|
|
263
|
+
</td>
|
|
264
|
+
</tr>
|
|
265
|
+
<% end %>
|
|
266
|
+
</tbody>
|
|
267
|
+
</table>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="card-footer bg-white border-top d-flex justify-content-between align-items-center">
|
|
271
|
+
<small class="text-muted">
|
|
272
|
+
<i class="bi bi-info-circle"></i>
|
|
273
|
+
Stability: <span class="badge bg-success">Green</span> ≤ avg errors,
|
|
274
|
+
<span class="badge bg-warning">Yellow</span> 1-2x avg,
|
|
275
|
+
<span class="badge bg-danger">Red</span> > 2x avg.
|
|
276
|
+
"New" errors are error types first seen in that release.
|
|
277
|
+
</small>
|
|
278
|
+
<div>
|
|
279
|
+
<%== @pagy.series_nav(:bootstrap) if @pagy.pages > 1 %>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
<% end %>
|
|
284
|
+
</div>
|
|
@@ -245,6 +245,21 @@
|
|
|
245
245
|
<%
|
|
246
246
|
end
|
|
247
247
|
|
|
248
|
+
when :secret
|
|
249
|
+
if value.present?
|
|
250
|
+
%>
|
|
251
|
+
<span class="badge bg-success fs-6">
|
|
252
|
+
<i class="bi bi-check-circle"></i> Set
|
|
253
|
+
</span>
|
|
254
|
+
<%
|
|
255
|
+
else
|
|
256
|
+
%>
|
|
257
|
+
<span class="badge bg-warning text-dark fs-6">
|
|
258
|
+
<i class="bi bi-exclamation-triangle"></i> Not set
|
|
259
|
+
</span>
|
|
260
|
+
<%
|
|
261
|
+
end
|
|
262
|
+
|
|
248
263
|
when :retention_days
|
|
249
264
|
# Show retention configuration with manual cleanup instructions
|
|
250
265
|
if value.present?
|
|
@@ -113,6 +113,38 @@
|
|
|
113
113
|
dashboard_base_url: { label: "Dashboard Base URL", description: "Base URL for dashboard links", type: :url }
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
|
+
"Issue Tracking" => {
|
|
117
|
+
icon: "bi-link-45deg",
|
|
118
|
+
color: "primary",
|
|
119
|
+
settings: {
|
|
120
|
+
enable_issue_tracking: { label: "Issue Tracking", description: "GitHub/GitLab/Codeberg integration — one switch enables all: issue creation, auto-create, lifecycle sync, platform state mirroring, comment display. Replaces workflow controls with platform state.", type: :boolean },
|
|
121
|
+
issue_tracker_token: { label: "API Token", description: "RED bot token (set RED_BOT_TOKEN env var)", type: :secret, show_if: :enable_issue_tracking },
|
|
122
|
+
issue_tracker_provider: { label: "Provider", description: "Auto-detected from git_repository_url", type: :auto_detect, show_if: :enable_issue_tracking, method: :effective_issue_tracker_provider },
|
|
123
|
+
issue_tracker_repo: { label: "Repository", description: "Auto-detected from git_repository_url", type: :auto_detect, show_if: :enable_issue_tracking, method: :effective_issue_tracker_repo },
|
|
124
|
+
issue_tracker_labels: { label: "Labels", description: "Labels added to new issues", type: :array, show_if: :enable_issue_tracking },
|
|
125
|
+
issue_webhook_secret: { label: "Webhook Secret", description: "Set to enable two-way sync (HMAC verification)", type: :secret, show_if: :enable_issue_tracking }
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"Deep Debugging" => {
|
|
129
|
+
icon: "bi-search",
|
|
130
|
+
color: "danger",
|
|
131
|
+
settings: {
|
|
132
|
+
enable_local_variables: { label: "Local Variable Capture", description: "TracePoint(:raise) — capture locals at exception time", type: :boolean },
|
|
133
|
+
enable_instance_variables: { label: "Instance Variable Capture", description: "Capture self's ivars at exception time", type: :boolean },
|
|
134
|
+
detect_swallowed_exceptions: { label: "Swallowed Exception Detection", description: "TracePoint(:raise) + (:rescue) — detect silently rescued exceptions (Ruby 3.3+)", type: :boolean },
|
|
135
|
+
enable_diagnostic_dump: { label: "Diagnostic Dump", description: "On-demand system state snapshots", type: :boolean },
|
|
136
|
+
enable_crash_capture: { label: "Process Crash Capture", description: "at_exit hook for unhandled crashes", type: :boolean }
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"Event Tracking" => {
|
|
140
|
+
icon: "bi-broadcast",
|
|
141
|
+
color: "info",
|
|
142
|
+
settings: {
|
|
143
|
+
enable_rack_attack_tracking: { label: "Rack Attack Tracking", description: "Track throttle/blocklist events as breadcrumbs", type: :boolean },
|
|
144
|
+
enable_actioncable_tracking: { label: "ActionCable Tracking", description: "Track WebSocket channel events as breadcrumbs", type: :boolean },
|
|
145
|
+
enable_activestorage_tracking: { label: "ActiveStorage Tracking", description: "Track uploads/downloads/deletes as breadcrumbs", type: :boolean }
|
|
146
|
+
}
|
|
147
|
+
},
|
|
116
148
|
"Advanced Configuration" => {
|
|
117
149
|
icon: "bi-sliders",
|
|
118
150
|
color: "secondary",
|
data/config/routes.rb
CHANGED
|
@@ -444,10 +444,40 @@ RailsErrorDashboard.configure do |config|
|
|
|
444
444
|
config.git_sha = ENV["GIT_SHA"]
|
|
445
445
|
# config.total_users_for_impact = 10000 # For user impact % calculation
|
|
446
446
|
|
|
447
|
-
# Git repository URL for clickable commit links
|
|
447
|
+
# Git repository URL for clickable commit links and issue tracking
|
|
448
448
|
# Examples:
|
|
449
449
|
# GitHub: "https://github.com/username/repo"
|
|
450
450
|
# GitLab: "https://gitlab.com/username/repo"
|
|
451
|
-
#
|
|
451
|
+
# Codeberg: "https://codeberg.org/username/repo"
|
|
452
452
|
# config.git_repository_url = ENV["GIT_REPOSITORY_URL"]
|
|
453
|
+
|
|
454
|
+
# ============================================================================
|
|
455
|
+
# ISSUE TRACKING (GitHub / GitLab / Codeberg)
|
|
456
|
+
# ============================================================================
|
|
457
|
+
#
|
|
458
|
+
# One switch enables everything: issue creation, auto-create on first
|
|
459
|
+
# occurrence, lifecycle sync (resolve → close, reopen → reopen), platform
|
|
460
|
+
# state mirroring (status, assignees, labels), and comment display.
|
|
461
|
+
#
|
|
462
|
+
# IMPORTANT: When enabled, the dashboard shows platform state instead of
|
|
463
|
+
# internal workflow controls:
|
|
464
|
+
# - "Mark as Resolved" → replaced by issue open/closed from platform
|
|
465
|
+
# - Workflow Status → issue state from platform
|
|
466
|
+
# - Assigned To → assignees from platform (with avatars)
|
|
467
|
+
# - Priority → labels from platform (with colors)
|
|
468
|
+
# - Snooze and Mute remain (no platform equivalent)
|
|
469
|
+
#
|
|
470
|
+
# Setup:
|
|
471
|
+
# 1. Create a RED bot account on GitHub/GitLab/Codeberg
|
|
472
|
+
# 2. Generate a token and set RED_BOT_TOKEN env var
|
|
473
|
+
# 3. Set git_repository_url above (already used for source code linking)
|
|
474
|
+
# 4. Enable:
|
|
475
|
+
#
|
|
476
|
+
# config.enable_issue_tracking = true
|
|
477
|
+
# config.issue_tracker_token = ENV["RED_BOT_TOKEN"]
|
|
478
|
+
#
|
|
479
|
+
# Optional overrides:
|
|
480
|
+
# config.issue_tracker_labels = ["bug"] # Labels added to new issues
|
|
481
|
+
# config.issue_tracker_auto_create_severities = [:critical, :high] # Auto-create threshold
|
|
482
|
+
# config.issue_webhook_secret = ENV["ISSUE_WEBHOOK_SECRET"] # Enables two-way webhook sync
|
|
453
483
|
end
|
|
@@ -82,17 +82,17 @@ module RailsErrorDashboard
|
|
|
82
82
|
attr_accessor :git_repository_url
|
|
83
83
|
|
|
84
84
|
# Issue tracker integration (GitHub, GitLab, Codeberg/Gitea/Forgejo)
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
# One switch enables everything: issue creation, auto-create, lifecycle sync,
|
|
86
|
+
# platform state mirroring, and comment display. Webhooks activate when
|
|
87
|
+
# issue_webhook_secret is set.
|
|
88
|
+
attr_accessor :enable_issue_tracking # Master switch (default: false) — enables all platform integration
|
|
87
89
|
attr_accessor :issue_tracker_token # String or lambda/proc for Rails credentials
|
|
90
|
+
attr_accessor :issue_tracker_provider # :github, :gitlab, :codeberg (auto-detected from git_repository_url)
|
|
88
91
|
attr_accessor :issue_tracker_repo # "owner/repo" (auto-extracted from git_repository_url)
|
|
89
92
|
attr_accessor :issue_tracker_labels # Array of label strings (default: ["bug"])
|
|
90
93
|
attr_accessor :issue_tracker_api_url # Custom API base URL for self-hosted instances
|
|
91
|
-
attr_accessor :
|
|
92
|
-
attr_accessor :
|
|
93
|
-
attr_accessor :auto_create_issues_for_severities # Array of symbols (default: [:critical, :high])
|
|
94
|
-
attr_accessor :enable_issue_webhooks # Boolean (default: false) — receive webhooks for two-way sync
|
|
95
|
-
attr_accessor :issue_webhook_secret # String — HMAC secret for webhook signature verification
|
|
94
|
+
attr_accessor :issue_tracker_auto_create_severities # Auto-create for these severities (default: [:critical, :high])
|
|
95
|
+
attr_accessor :issue_webhook_secret # HMAC secret — webhooks activate when this is set
|
|
96
96
|
|
|
97
97
|
# Advanced error analysis features
|
|
98
98
|
attr_accessor :enable_similar_errors # Fuzzy error matching
|
|
@@ -244,17 +244,14 @@ module RailsErrorDashboard
|
|
|
244
244
|
@total_users_for_impact = nil # Auto-detect if not set
|
|
245
245
|
@git_repository_url = ENV["GIT_REPOSITORY_URL"]
|
|
246
246
|
|
|
247
|
-
# Issue tracker integration defaults — OFF by default
|
|
247
|
+
# Issue tracker integration defaults — OFF by default, one switch enables all
|
|
248
248
|
@enable_issue_tracking = false
|
|
249
|
+
@issue_tracker_token = ENV["RED_BOT_TOKEN"] || ENV["ISSUE_TRACKER_TOKEN"]
|
|
249
250
|
@issue_tracker_provider = nil # Auto-detect from git_repository_url
|
|
250
|
-
@issue_tracker_token = ENV["ISSUE_TRACKER_TOKEN"]
|
|
251
251
|
@issue_tracker_repo = nil # Auto-extract from git_repository_url
|
|
252
252
|
@issue_tracker_labels = [ "bug" ]
|
|
253
253
|
@issue_tracker_api_url = nil # For self-hosted instances
|
|
254
|
-
@
|
|
255
|
-
@auto_create_issues_on_first_occurrence = true
|
|
256
|
-
@auto_create_issues_for_severities = [ :critical, :high ]
|
|
257
|
-
@enable_issue_webhooks = false
|
|
254
|
+
@issue_tracker_auto_create_severities = [ :critical, :high ]
|
|
258
255
|
@issue_webhook_secret = ENV["ISSUE_WEBHOOK_SECRET"]
|
|
259
256
|
|
|
260
257
|
# Advanced error analysis features (all OFF by default - opt-in)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsErrorDashboard
|
|
4
|
+
module Queries
|
|
5
|
+
# Query: Build a release timeline from error data, showing per-version health stats,
|
|
6
|
+
# "new in this release" error detection, stability indicators, and release-over-release deltas.
|
|
7
|
+
# Uses existing app_version and git_sha columns on error_logs — no new migration needed.
|
|
8
|
+
class ReleaseTimeline
|
|
9
|
+
def self.call(days = 30, application_id: nil)
|
|
10
|
+
new(days, application_id: application_id).call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(days = 30, application_id: nil)
|
|
14
|
+
@days = days
|
|
15
|
+
@application_id = application_id
|
|
16
|
+
@start_date = days.days.ago
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
return empty_result unless has_version_column?
|
|
21
|
+
|
|
22
|
+
releases = build_releases
|
|
23
|
+
{
|
|
24
|
+
releases: releases,
|
|
25
|
+
summary: build_summary(releases)
|
|
26
|
+
}
|
|
27
|
+
rescue => e
|
|
28
|
+
Rails.logger.error("[RailsErrorDashboard] ReleaseTimeline failed: #{e.class}: #{e.message}")
|
|
29
|
+
empty_result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def base_scope
|
|
35
|
+
scope = ErrorLog.where("occurred_at >= ?", @start_date)
|
|
36
|
+
.where.not(app_version: [ nil, "" ])
|
|
37
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
38
|
+
scope
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def build_releases
|
|
42
|
+
version_stats = aggregate_version_stats
|
|
43
|
+
return [] if version_stats.empty?
|
|
44
|
+
|
|
45
|
+
new_errors = new_errors_per_version
|
|
46
|
+
avg_errors = version_stats.sum { |_v, s| s[:total_errors] }.to_f / version_stats.size
|
|
47
|
+
|
|
48
|
+
# Sort chronologically by first_seen (oldest first) for delta calculation
|
|
49
|
+
sorted_chrono = version_stats.sort_by { |_v, s| s[:first_seen] }
|
|
50
|
+
|
|
51
|
+
releases = []
|
|
52
|
+
sorted_chrono.each_with_index do |(version, stats), idx|
|
|
53
|
+
previous_count = idx > 0 ? sorted_chrono[idx - 1][1][:total_errors] : nil
|
|
54
|
+
|
|
55
|
+
releases << {
|
|
56
|
+
version: version,
|
|
57
|
+
git_shas: stats[:git_shas],
|
|
58
|
+
first_seen: stats[:first_seen],
|
|
59
|
+
last_seen: stats[:last_seen],
|
|
60
|
+
current: false, # set below
|
|
61
|
+
total_errors: stats[:total_errors],
|
|
62
|
+
unique_error_types: stats[:unique_error_types],
|
|
63
|
+
new_error_count: new_errors[version] || 0,
|
|
64
|
+
stability: stability_indicator(stats[:total_errors], avg_errors),
|
|
65
|
+
problematic: stats[:total_errors] > (avg_errors * 2),
|
|
66
|
+
delta_from_previous: previous_count ? (stats[:total_errors] - previous_count) : nil,
|
|
67
|
+
delta_percentage: previous_count && previous_count > 0 ? ((stats[:total_errors] - previous_count).to_f / previous_count * 100).round(1) : nil
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Mark the most recent release as current
|
|
72
|
+
releases.last[:current] = true if releases.any?
|
|
73
|
+
|
|
74
|
+
# Return in reverse chronological order (newest first)
|
|
75
|
+
releases.reverse
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Single GROUP BY query for per-version aggregates
|
|
79
|
+
def aggregate_version_stats
|
|
80
|
+
rows = base_scope
|
|
81
|
+
.group(:app_version)
|
|
82
|
+
.select(
|
|
83
|
+
:app_version,
|
|
84
|
+
"COUNT(*) AS total_count",
|
|
85
|
+
"COUNT(DISTINCT error_type) AS unique_types",
|
|
86
|
+
"MIN(occurred_at) AS first_seen_at",
|
|
87
|
+
"MAX(occurred_at) AS last_seen_at"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Collect git_shas per version in a second lightweight query
|
|
91
|
+
sha_map = {}
|
|
92
|
+
if has_git_sha_column?
|
|
93
|
+
base_scope.where.not(git_sha: [ nil, "" ])
|
|
94
|
+
.group(:app_version, :git_sha)
|
|
95
|
+
.pluck(:app_version, :git_sha)
|
|
96
|
+
.each do |version, sha|
|
|
97
|
+
(sha_map[version] ||= []) << sha
|
|
98
|
+
end
|
|
99
|
+
sha_map.each_value(&:uniq!)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
rows.each_with_object({}) do |row, result|
|
|
103
|
+
version = row.app_version
|
|
104
|
+
first_seen = row.first_seen_at
|
|
105
|
+
last_seen = row.last_seen_at
|
|
106
|
+
first_seen = Time.zone.parse(first_seen) if first_seen.is_a?(String)
|
|
107
|
+
last_seen = Time.zone.parse(last_seen) if last_seen.is_a?(String)
|
|
108
|
+
|
|
109
|
+
result[version] = {
|
|
110
|
+
total_errors: row.total_count.to_i,
|
|
111
|
+
unique_error_types: row.unique_types.to_i,
|
|
112
|
+
first_seen: first_seen,
|
|
113
|
+
last_seen: last_seen,
|
|
114
|
+
git_shas: sha_map[version] || []
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
rescue => e
|
|
118
|
+
Rails.logger.error("[RailsErrorDashboard] ReleaseTimeline aggregate failed: #{e.class}: #{e.message}")
|
|
119
|
+
{}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# For each error_hash in the window, find which app_version it first appeared in.
|
|
123
|
+
# Count per version to get "new errors introduced in this release".
|
|
124
|
+
def new_errors_per_version
|
|
125
|
+
return {} unless has_error_hash_column?
|
|
126
|
+
|
|
127
|
+
# Get the earliest occurrence per error_hash, with its app_version
|
|
128
|
+
# We need (error_hash, app_version) at MIN(occurred_at)
|
|
129
|
+
earliest = {}
|
|
130
|
+
base_scope.select(:error_hash, :app_version, :occurred_at)
|
|
131
|
+
.where.not(error_hash: [ nil, "" ])
|
|
132
|
+
.order(:occurred_at)
|
|
133
|
+
.each do |row|
|
|
134
|
+
earliest[row.error_hash] ||= row.app_version
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Count how many error_hashes have each version as their earliest
|
|
138
|
+
earliest.values.tally
|
|
139
|
+
rescue => e
|
|
140
|
+
Rails.logger.error("[RailsErrorDashboard] ReleaseTimeline new_errors failed: #{e.class}: #{e.message}")
|
|
141
|
+
{}
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def stability_indicator(count, avg)
|
|
145
|
+
return :green if avg <= 0
|
|
146
|
+
ratio = count.to_f / avg
|
|
147
|
+
if ratio <= 1.0
|
|
148
|
+
:green
|
|
149
|
+
elsif ratio <= 2.0
|
|
150
|
+
:yellow
|
|
151
|
+
else
|
|
152
|
+
:red
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def build_summary(releases)
|
|
157
|
+
{
|
|
158
|
+
total_releases: releases.size,
|
|
159
|
+
current_version: releases.first&.dig(:version),
|
|
160
|
+
avg_errors_per_release: releases.any? ? (releases.sum { |r| r[:total_errors] }.to_f / releases.size).round(1) : 0
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def empty_result
|
|
165
|
+
{ releases: [], summary: { total_releases: 0, current_version: nil, avg_errors_per_release: 0 } }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def has_version_column?
|
|
169
|
+
ErrorLog.column_names.include?("app_version")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def has_git_sha_column?
|
|
173
|
+
ErrorLog.column_names.include?("git_sha")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def has_error_hash_column?
|
|
177
|
+
ErrorLog.column_names.include?("error_hash")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -49,17 +49,15 @@ module RailsErrorDashboard
|
|
|
49
49
|
|
|
50
50
|
def should_auto_create?(error_log)
|
|
51
51
|
config = RailsErrorDashboard.configuration
|
|
52
|
-
return false unless config.enable_issue_tracking
|
|
52
|
+
return false unless config.enable_issue_tracking
|
|
53
53
|
return false if error_log.external_issue_url.present?
|
|
54
54
|
|
|
55
|
-
# First occurrence
|
|
56
|
-
|
|
57
|
-
return true
|
|
58
|
-
end
|
|
55
|
+
# First occurrence — always auto-create
|
|
56
|
+
return true if error_log.occurrence_count == 1
|
|
59
57
|
|
|
60
58
|
# Severity threshold check
|
|
61
59
|
severity = error_log.severity&.to_sym
|
|
62
|
-
if config.
|
|
60
|
+
if config.issue_tracker_auto_create_severities&.include?(severity)
|
|
63
61
|
return true
|
|
64
62
|
end
|
|
65
63
|
|
|
@@ -119,6 +119,7 @@ require "rails_error_dashboard/queries/job_health_summary"
|
|
|
119
119
|
require "rails_error_dashboard/queries/database_health_summary"
|
|
120
120
|
require "rails_error_dashboard/queries/swallowed_exception_summary"
|
|
121
121
|
require "rails_error_dashboard/queries/rack_attack_summary"
|
|
122
|
+
require "rails_error_dashboard/queries/release_timeline"
|
|
122
123
|
require "rails_error_dashboard/error_reporter"
|
|
123
124
|
require "rails_error_dashboard/middleware/error_catcher"
|
|
124
125
|
require "rails_error_dashboard/middleware/rate_limiter"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_error_dashboard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anjan Jagirdar
|
|
@@ -310,6 +310,7 @@ files:
|
|
|
310
310
|
- app/views/rails_error_dashboard/errors/overview.html.erb
|
|
311
311
|
- app/views/rails_error_dashboard/errors/platform_comparison.html.erb
|
|
312
312
|
- app/views/rails_error_dashboard/errors/rack_attack_summary.html.erb
|
|
313
|
+
- app/views/rails_error_dashboard/errors/releases.html.erb
|
|
313
314
|
- app/views/rails_error_dashboard/errors/settings.html.erb
|
|
314
315
|
- app/views/rails_error_dashboard/errors/settings/_value_badge.html.erb
|
|
315
316
|
- app/views/rails_error_dashboard/errors/show.html.erb
|
|
@@ -414,6 +415,7 @@ files:
|
|
|
414
415
|
- lib/rails_error_dashboard/queries/platform_comparison.rb
|
|
415
416
|
- lib/rails_error_dashboard/queries/rack_attack_summary.rb
|
|
416
417
|
- lib/rails_error_dashboard/queries/recurring_issues.rb
|
|
418
|
+
- lib/rails_error_dashboard/queries/release_timeline.rb
|
|
417
419
|
- lib/rails_error_dashboard/queries/similar_errors.rb
|
|
418
420
|
- lib/rails_error_dashboard/queries/swallowed_exception_summary.rb
|
|
419
421
|
- lib/rails_error_dashboard/services/analytics_cache_manager.rb
|
|
@@ -485,7 +487,7 @@ metadata:
|
|
|
485
487
|
bug_tracker_uri: https://github.com/AnjanJ/rails_error_dashboard/issues
|
|
486
488
|
funding_uri: https://github.com/sponsors/AnjanJ
|
|
487
489
|
post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
|
|
488
|
-
\ Rails Error Dashboard v0.5.
|
|
490
|
+
\ Rails Error Dashboard v0.5.10\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
|
|
489
491
|
First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
|
|
490
492
|
db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
|
|
491
493
|
=> '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n
|