sidekiq-failures 1.0.4 → 1.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71e370c4485d8ecc481914afd5d8a489c9f2743c1749975dceb536977607230a
4
- data.tar.gz: 1a8e989912c378459e478eb9f29c0dd1d53128c126cc135c2bf597375e26ed84
3
+ metadata.gz: ba0a7d8ecb1ed1f847be0918723281d6c12d7f40a4780c089340cc8e687183f3
4
+ data.tar.gz: 10aca014769f8883c9de557bded7ac01287276924ad314ec1006d6d0e1c332bd
5
5
  SHA512:
6
- metadata.gz: 49f99dbee68555a41e53d615e27afa48f5852d0f887fd96891864c89fd86f5d72fe552d9eefa5b22f860502d6bed321c38f5a4fd28ea5c4f1dfeea703c9d17eb
7
- data.tar.gz: f59b94960937b9f7d3482c79f79b905015fac87df83bcde5db946023383658138d7226003431a4ece758780f0ad06e7a1b2785258917d19fc5a9278fe6583267
6
+ metadata.gz: 1bc85386b596a67b27e00b6c2efed3377f8377bb89dd4eb128336b0f1bf4150acd361c3be1f1ed8dce233710031f3bd9b17d09d4c85b7ee7673da67ba7649d89
7
+ data.tar.gz: 236381eb70f14564b3dd43fc715ad8ac3f5578973b4c20fb95f8f6cfcedadb82db922c1cb8eafed13e6c5189d5218ec0c959dbe559592413b727da2ef18fed25
@@ -12,8 +12,8 @@ jobs:
12
12
  strategy:
13
13
  fail-fast: false
14
14
  matrix:
15
- ruby: [2.6, 2.7, '3.0', 3.1]
16
- sidekiq: [4.2, 5.2, 6.2]
15
+ ruby: [2.6, 2.7, '3.0', 3.1, 3.4.3]
16
+ sidekiq: [4.2, 5.2, 6.2, 8.0.3]
17
17
  services:
18
18
  redis:
19
19
  image: redis
@@ -27,7 +27,7 @@ jobs:
27
27
  env:
28
28
  SIDEKIQ_VERSION: ~> ${{ matrix.sidekiq }}
29
29
  steps:
30
- - uses: actions/checkout@v3
30
+ - uses: actions/checkout@v4
31
31
  - name: Set up Ruby
32
32
  uses: ruby/setup-ruby@v1
33
33
  with:
data/.gitignore CHANGED
@@ -17,4 +17,4 @@ tmp
17
17
  Gemfile.lock
18
18
  .ruby-version
19
19
  .ruby-gemset
20
- .tool-versions
20
+
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 1.1.0
4
+
5
+ * Add support for Sidekiq 8.x (#145 @navidemad)
6
+ * Fix nil exception in safe_relative_time (#152 @leedrum)
7
+
3
8
  ## 1.0.4
4
9
 
5
10
  * Sanitize failure text on the failure details page (#144 @mcasper)
@@ -61,8 +66,8 @@
61
66
  ## 0.3.0
62
67
 
63
68
  * Bump sidekiq dependency to sidekiq >= 2.14.0
64
- * Remove slim templates and dependecy
65
- * Escape exception info when outputing to html
69
+ * Remove slim templates and dependency
70
+ * Escape exception info when outputting to html
66
71
  * Add `Sidekiq::Failures.reset_failures` helper method
67
72
  * Add `Sidekiq::Failures.count` helper method (@zanker)
68
73
  * Adhere to sidekiq approach of showing UTC times
@@ -85,8 +90,8 @@
85
90
  * Added processor identity to failure data (@krasnoukhov)
86
91
  * Handle Sidekiq::Shutdown exceptions (@krasnoukhov)
87
92
  * Add failures maximum count option (@mathieulaporte)
88
- * User Expception#message instead of Exception#to_s (@supaspoida)
89
- * Removed web depencies (@LongMan)
93
+ * User Exception#message instead of Exception#to_s (@supaspoida)
94
+ * Removed web dependencies (@LongMan)
90
95
  * Stop overloading find_template (@zquestz)
91
96
 
92
97
  ## 0.1.0
data/README.md CHANGED
@@ -127,13 +127,18 @@ influenced by `failures_max_count`.
127
127
  Sidekiq::Failures.count
128
128
  ```
129
129
 
130
- ### Reset Failures
130
+ ### Reset and clear Failures
131
131
 
132
- Gives a convenient way of reseting Sidekiq Failure stored failed jobs programmatically.
133
- Takes an options hash and if the `counter` key is present also resets Sidekiq own failed stats.
132
+ Gives a convenient way of resetting Sidekiq Failure stored failed jobs programmatically.
134
133
 
135
134
  ```ruby
136
- Sidekiq::Failures.reset_failures
135
+ Sidekiq::Failures.clear_failures
136
+ ```
137
+
138
+ To reset Sidekiq own failed *stats*.
139
+
140
+ ```ruby
141
+ Sidekiq::Failures.reset_failure_count
137
142
  ```
138
143
 
139
144
  ## Dependencies
@@ -11,7 +11,14 @@ module Sidekiq
11
11
 
12
12
  def retry_failure
13
13
  Sidekiq.redis do |conn|
14
- results = conn.zrangebyscore(Sidekiq::Failures::LIST_KEY, score, score)
14
+ # from Redis v6.2.0, zrangebyscore is deprecated and zrange with BYSCORE is used
15
+ # option byscore is available from redis-rb v4.6.0
16
+ results = if Gem::Version.new(conn.info["redis_version"].to_s) >= Gem::Version.new('6.2.0') &&
17
+ (Gem.loaded_specs['redis'].nil? || Gem.loaded_specs['redis'].version >= Gem::Version.new('4.6.0'))
18
+ conn.zrange(Sidekiq::Failures::LIST_KEY, score.to_i, score.to_i, byscore: true)
19
+ else
20
+ conn.zrangebyscore(Sidekiq::Failures::LIST_KEY, score, score)
21
+ end
15
22
  conn.zremrangebyscore(Sidekiq::Failures::LIST_KEY, score, score)
16
23
  results.map do |message|
17
24
  msg = Sidekiq.load_json(message)
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Failures
3
- VERSION = "1.0.4"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -1,31 +1,39 @@
1
1
  <%= erb :_job_info, :locals => {:job => @failure, :type => :failure} %>
2
2
 
3
- <h3><%= t('Error') %></h3>
4
- <table class="error table table-bordered table-striped">
5
- <tbody>
6
- <tr>
7
- <th><%= t('ErrorClass') %></th>
8
- <td>
9
- <code><%= h @failure['error_class'] %></code>
10
- </td>
11
- </tr>
12
- <tr>
13
- <th><%= t('ErrorMessage') %></th>
14
- <td><%= h @failure['error_message'] %></td>
15
- </tr>
16
- <% if !@failure['error_backtrace'].nil? %>
17
- <tr>
18
- <th><%= t('ErrorBacktrace') %></th>
19
- <td>
20
- <code><%= @failure['error_backtrace'].join("<br/>") %></code>
21
- </td>
22
- </tr>
23
- <% end %>
24
- </tbody>
25
- </table>
26
- <form class="form-horizontal" action="<%= root_path %>failures/<%= job_params(@failure, @failure.score) %>" method="post">
27
- <%= csrf_tag if respond_to?(:csrf_tag) %>
28
- <a class="btn" href="<%= root_path %>failures"><%= t('GoBack') %></a>
29
- <input class="btn btn-primary" type="submit" name="retry" value="<%= t('RetryNow') %>" />
30
- <input class="btn btn-danger" type="submit" name="delete" value="<%= t('Delete') %>" />
31
- </form>
3
+ <section>
4
+ <header>
5
+ <h2><%= t('Error') %></h2>
6
+ </header>
7
+
8
+ <div class="table_container">
9
+ <table>
10
+ <tbody>
11
+ <tr>
12
+ <th><%= t('ErrorClass') %></th>
13
+ <td>
14
+ <code><%= h @failure['error_class'] %></code>
15
+ </td>
16
+ </tr>
17
+ <tr>
18
+ <th><%= t('ErrorMessage') %></th>
19
+ <td><%= h @failure['error_message'] %></td>
20
+ </tr>
21
+ <% if !@failure['error_backtrace'].nil? %>
22
+ <tr>
23
+ <th><%= t('ErrorBacktrace') %></th>
24
+ <td>
25
+ <code><%= @failure['error_backtrace'].join("<br/>") %></code>
26
+ </td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ </div>
32
+
33
+ <form action="<%= root_path %>failures/<%= job_params(@failure, @failure.score) %>" method="post">
34
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
35
+ <a class="btn btn-default" href="<%= root_path %>failures"><%= t('GoBack') %></a>
36
+ <input class="btn btn-primary" type="submit" name="retry" value="<%= t('RetryNow') %>" />
37
+ <input class="btn btn-danger" type="submit" name="delete" value="<%= t('Delete') %>" />
38
+ </form>
39
+ </section>
@@ -0,0 +1,31 @@
1
+ <%= erb :_job_info, :locals => {:job => @failure, :type => :failure} %>
2
+
3
+ <h3><%= t('Error') %></h3>
4
+ <table class="error table table-bordered table-striped">
5
+ <tbody>
6
+ <tr>
7
+ <th><%= t('ErrorClass') %></th>
8
+ <td>
9
+ <code><%= h @failure['error_class'] %></code>
10
+ </td>
11
+ </tr>
12
+ <tr>
13
+ <th><%= t('ErrorMessage') %></th>
14
+ <td><%= h @failure['error_message'] %></td>
15
+ </tr>
16
+ <% if !@failure['error_backtrace'].nil? %>
17
+ <tr>
18
+ <th><%= t('ErrorBacktrace') %></th>
19
+ <td>
20
+ <code><%= @failure['error_backtrace'].join("<br/>") %></code>
21
+ </td>
22
+ </tr>
23
+ <% end %>
24
+ </tbody>
25
+ </table>
26
+ <form class="form-horizontal" action="<%= root_path %>failures/<%= job_params(@failure, @failure.score) %>" method="post">
27
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
28
+ <a class="btn" href="<%= root_path %>failures"><%= t('GoBack') %></a>
29
+ <input class="btn btn-primary" type="submit" name="retry" value="<%= t('RetryNow') %>" />
30
+ <input class="btn btn-danger" type="submit" name="delete" value="<%= t('Delete') %>" />
31
+ </form>
@@ -1,105 +1,113 @@
1
- <header class="row">
2
- <div class="col-sm-5">
3
- <h3><%= t('FailedJobs') %></h3>
4
- </div>
5
- <% if @failures.count > 0 && @total_size > @count %>
6
- <div class="col-sm-4">
1
+ <section>
2
+ <header>
3
+ <h1>
4
+ <span class="title"><%= t('FailedJobs') %></span>
5
+ <span class="badge badge-secondary"><%= number_with_delimiter(@failures.count) %></span>
6
+ </h1>
7
+ <% if @failures.count > 0 && @total_size > @count %>
7
8
  <%= erb :_paging, :locals => { :url => "#{root_path}failures" } %>
8
- </div>
9
- <% end %>
10
- <%= filtering('failures') if respond_to?(:filtering) %>
11
- </header>
9
+ <% end %>
10
+ <%= filtering('failures') if respond_to?(:filtering) %>
11
+ </header>
12
12
 
13
- <% if @failures.count > 0 %>
14
- <form action="<%= root_path %>failures" method="post">
15
- <%= csrf_tag if respond_to?(:csrf_tag) %>
16
- <table class="table table-striped table-bordered table-white">
17
- <thead>
18
- <tr>
19
- <th width="20px" class="table-checkbox">
20
- <label>
21
- <input type="checkbox" class="check_all" />
22
- </label>
23
- </th>
24
- <th><%= t('FailedAt') %></th>
25
- <th><%= t('Queue') %></th>
26
- <th><%= t('Worker') %></th>
27
- <th><%= t('Arguments') %></th>
28
- <th><%= t('Error') %></th>
29
- </tr>
30
- </thead>
31
- <% @failures.each do |entry| %>
32
- <tr>
33
- <td class="table-checkbox">
34
- <label>
35
- <input type='checkbox' name='key[]' value='<%= job_params(entry.item, entry.score) %>' />
36
- </label>
37
- </td>
38
- <td>
39
- <a href="<%= root_path %>failures/<%= job_params(entry.item, entry.score) %>"><%= safe_relative_time(entry['failed_at']) %></a>
40
- </td>
41
- <td>
42
- <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
43
- </td>
44
- <td><%= entry.respond_to?(:display_class) ? entry.display_class : entry.klass %></td>
45
- <td>
46
- <div class="args"><%= display_args(entry.respond_to?(:display_args) ? entry.display_args : entry.args) %></div>
47
- </td>
48
- <td style="overflow: auto; padding: 10px;">
49
- <a class="backtrace" href="javascript:void(0)", onclick="toggle_error_backtrace(this)">
50
- <%= h entry['error_class'] %>: <%= h entry['error_message'].to_s.size > 500 ? entry['error_message'][0..500] + '...' : entry['error_message'] %>
51
- </a>
52
- <pre style="display: none; background: none; border: 0; width: 100%; max-height: 30em; font-size: 0.8em; white-space: nowrap; overflow: auto;">
53
- <%= entry['error_backtrace'].join("<br />") if entry['error_backtrace'] %>
54
- </pre>
55
- <p>
56
- <span>Processor: <%= entry['processor'] %></span>
57
- </p>
58
- </td>
59
- </tr>
60
- <% end %>
61
- </table>
62
- <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="<%= t('RetryNow') %>" />
63
- <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="<%= t('Delete') %>" />
64
- </form>
13
+ <% if @failures.count > 0 %>
14
+ <form action="<%= root_path %>failures" method="post">
15
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
16
+ <div class="table_container">
17
+ <table>
18
+ <thead>
19
+ <tr>
20
+ <th width="20px" class="table-checkbox">
21
+ <label>
22
+ <input type="checkbox" class="check_all" />
23
+ </label>
24
+ </th>
25
+ <th><%= t('FailedAt') %></th>
26
+ <th><%= t('Queue') %></th>
27
+ <th><%= t('Worker') %></th>
28
+ <th><%= t('Arguments') %></th>
29
+ <th><%= t('Error') %></th>
30
+ </tr>
31
+ </thead>
32
+ <% @failures.each do |entry| %>
33
+ <tr>
34
+ <td class="table-checkbox">
35
+ <label>
36
+ <input type='checkbox' name='key[]' value='<%= job_params(entry.item, entry.score) %>' />
37
+ </label>
38
+ </td>
39
+ <td>
40
+ <a href="<%= root_path %>failures/<%= job_params(entry.item, entry.score) %>"><%= safe_relative_time(entry['failed_at']) %></a>
41
+ </td>
42
+ <td>
43
+ <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
44
+ </td>
45
+ <td><%= entry.respond_to?(:display_class) ? entry.display_class : entry.klass %></td>
46
+ <td>
47
+ <code>
48
+ <div class="args"><%= display_args(entry.respond_to?(:display_args) ? entry.display_args : entry.args) %></div>
49
+ </code>
50
+ </td>
51
+ <td>
52
+ <% error_class = entry['error_class'].to_s %>
53
+ <% error_message = entry['error_message'].to_s %>
54
+ <% error_text = "#{h(error_class)}: #{h(error_message)}" %>
55
+ <% truncated_error = truncate(error_text, 200) %>
65
56
 
66
- <form action="<%= root_path %>failures/all/delete" method="post">
67
- <%= csrf_tag if respond_to?(:csrf_tag) %>
68
- <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
69
- </form>
70
- <form action="<%= root_path %>failures/all/retry" method="post">
71
- <%= csrf_tag if respond_to?(:csrf_tag) %>
72
- <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="<%= t('RetryAll') %>" data-confirm="<%= t('AreYouSure') %>" />
73
- </form>
57
+ <% if entry['error_backtrace'] %>
58
+ <details>
59
+ <summary>
60
+ <%= truncated_error %>
61
+ </summary>
62
+ <% if entry['error_backtrace'] %>
63
+ <code>
64
+ <%= entry['error_backtrace'].join("<br/>") %>
65
+ </code>
66
+ <% end %>
67
+ </details>
68
+ <% else %>
69
+ <div>
70
+ <%= truncated_error %>
71
+ </div>
72
+ <% end %>
73
+ <div>
74
+ <span>Processor: <%= entry['processor'] %></span>
75
+ </div>
76
+ </td>
77
+ </tr>
78
+ <% end %>
79
+ </table>
80
+ </div>
81
+ <div class="buttons-row">
82
+ <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="<%= t('RetryNow') %>" />
83
+ <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="<%= t('Delete') %>" />
84
+ </div>
85
+ </form>
74
86
 
75
- <% if @failures.count > 0 && @total_size > @count %>
76
- <div class="col-sm-4">
77
- <%= erb :_paging, :locals => { :url => "#{root_path}failures" } %>
87
+ <div class="buttons-row">
88
+ <form action="<%= root_path %>failures/all/delete" method="post">
89
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
90
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
91
+ </form>
92
+ <form action="<%= root_path %>failures/all/retry" method="post">
93
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
94
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="<%= t('RetryAll') %>" data-confirm="<%= t('AreYouSure') %>" />
95
+ </form>
78
96
  </div>
79
- <% end %>
80
97
 
81
- <script>
82
- function toggle_error_backtrace(e) {
83
- let x = e.nextElementSibling;
84
- if (x.style.display === "none") {
85
- x.style.display = "block";
86
- } else {
87
- x.style.display = "none";
88
- }
89
- }
90
- </script>
98
+ <% if @failures.count > 0 && @total_size > @count %>
99
+ <div>
100
+ <%= erb :_paging, :locals => { :url => "#{root_path}failures" } %>
101
+ </div>
102
+ <% end %>
103
+ <% else %>
104
+ <div class="alert alert-success"><%= t('NoFailedJobsFound') %></div>
105
+ <% end %>
91
106
 
92
- <style type="text/css">
93
- @media only screen and (prefers-color-scheme: dark) {
94
- pre {
95
- color: white
96
- }
97
- }
98
- </style>
99
- <% else %>
100
- <div class="alert alert-success"><%= t('NoFailedJobsFound') %></div>
101
- <% end %>
102
- <form action="<%= root_path %>failures/all/reset" method="post">
103
- <%= csrf_tag if respond_to?(:csrf_tag) %>
104
- <input class="btn btn-danger btn-xs pull-right" type="submit" name="reset" value="<%= t('ResetCounter') %>" data-confirm="<%= t('AreYouSure') %>" />
105
- </form>
107
+ <div class="buttons-row">
108
+ <form action="<%= root_path %>failures/all/reset" method="post">
109
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
110
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="reset" value="<%= t('ResetCounter') %>" data-confirm="<%= t('AreYouSure') %>" />
111
+ </form>
112
+ </div>
113
+ </section>
@@ -0,0 +1,105 @@
1
+ <header class="row">
2
+ <div class="col-sm-5">
3
+ <h3><%= t('FailedJobs') %></h3>
4
+ </div>
5
+ <% if @failures.count > 0 && @total_size > @count %>
6
+ <div class="col-sm-4">
7
+ <%= erb :_paging, :locals => { :url => "#{root_path}failures" } %>
8
+ </div>
9
+ <% end %>
10
+ <%= filtering('failures') if respond_to?(:filtering) %>
11
+ </header>
12
+
13
+ <% if @failures.count > 0 %>
14
+ <form action="<%= root_path %>failures" method="post">
15
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
16
+ <table class="table table-striped table-bordered table-white">
17
+ <thead>
18
+ <tr>
19
+ <th width="20px" class="table-checkbox">
20
+ <label>
21
+ <input type="checkbox" class="check_all" />
22
+ </label>
23
+ </th>
24
+ <th><%= t('FailedAt') %></th>
25
+ <th><%= t('Queue') %></th>
26
+ <th><%= t('Worker') %></th>
27
+ <th><%= t('Arguments') %></th>
28
+ <th><%= t('Error') %></th>
29
+ </tr>
30
+ </thead>
31
+ <% @failures.each do |entry| %>
32
+ <tr>
33
+ <td class="table-checkbox">
34
+ <label>
35
+ <input type='checkbox' name='key[]' value='<%= job_params(entry.item, entry.score) %>' />
36
+ </label>
37
+ </td>
38
+ <td>
39
+ <a href="<%= root_path %>failures/<%= job_params(entry.item, entry.score) %>"><%= safe_relative_time(entry['failed_at']) %></a>
40
+ </td>
41
+ <td>
42
+ <a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
43
+ </td>
44
+ <td><%= entry.respond_to?(:display_class) ? entry.display_class : entry.klass %></td>
45
+ <td>
46
+ <div class="args"><%= display_args(entry.respond_to?(:display_args) ? entry.display_args : entry.args) %></div>
47
+ </td>
48
+ <td style="overflow: auto; padding: 10px;">
49
+ <a class="backtrace" href="javascript:void(0)", onclick="toggle_error_backtrace(this)">
50
+ <%= h entry['error_class'] %>: <%= h entry['error_message'].to_s.size > 500 ? entry['error_message'][0..500] + '...' : entry['error_message'] %>
51
+ </a>
52
+ <pre style="display: none; background: none; border: 0; width: 100%; max-height: 30em; font-size: 0.8em; white-space: nowrap; overflow: auto;">
53
+ <%= entry['error_backtrace'].join("<br />") if entry['error_backtrace'] %>
54
+ </pre>
55
+ <p>
56
+ <span>Processor: <%= entry['processor'] %></span>
57
+ </p>
58
+ </td>
59
+ </tr>
60
+ <% end %>
61
+ </table>
62
+ <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="<%= t('RetryNow') %>" />
63
+ <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="<%= t('Delete') %>" />
64
+ </form>
65
+
66
+ <form action="<%= root_path %>failures/all/delete" method="post">
67
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
68
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSure') %>" />
69
+ </form>
70
+ <form action="<%= root_path %>failures/all/retry" method="post">
71
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
72
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="<%= t('RetryAll') %>" data-confirm="<%= t('AreYouSure') %>" />
73
+ </form>
74
+
75
+ <% if @failures.count > 0 && @total_size > @count %>
76
+ <div class="col-sm-4">
77
+ <%= erb :_paging, :locals => { :url => "#{root_path}failures" } %>
78
+ </div>
79
+ <% end %>
80
+
81
+ <script>
82
+ function toggle_error_backtrace(e) {
83
+ let x = e.nextElementSibling;
84
+ if (x.style.display === "none") {
85
+ x.style.display = "block";
86
+ } else {
87
+ x.style.display = "none";
88
+ }
89
+ }
90
+ </script>
91
+
92
+ <style type="text/css">
93
+ @media only screen and (prefers-color-scheme: dark) {
94
+ pre {
95
+ color: white
96
+ }
97
+ }
98
+ </style>
99
+ <% else %>
100
+ <div class="alert alert-success"><%= t('NoFailedJobsFound') %></div>
101
+ <% end %>
102
+ <form action="<%= root_path %>failures/all/reset" method="post">
103
+ <%= csrf_tag if respond_to?(:csrf_tag) %>
104
+ <input class="btn btn-danger btn-xs pull-right" type="submit" name="reset" value="<%= t('ResetCounter') %>" data-confirm="<%= t('AreYouSure') %>" />
105
+ </form>