good_job 3.28.0 → 3.28.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/app/views/good_job/batches/_jobs.erb +23 -21
- data/app/views/good_job/batches/_table.erb +15 -15
- data/app/views/good_job/jobs/_executions.erb +7 -7
- data/app/views/good_job/jobs/_table.erb +156 -154
- data/app/views/good_job/shared/_filter.erb +9 -9
- data/app/views/good_job/shared/_navbar.erb +1 -1
- data/app/views/good_job/shared/_secondary_navbar.erb +1 -1
- data/app/views/layouts/good_job/application.html.erb +2 -2
- data/lib/good_job/active_job_extensions/batches.rb +1 -1
- data/lib/good_job/active_job_extensions/concurrency.rb +1 -1
- data/lib/good_job/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bbf6aa5c2cdbc7b2ff5b5f7dd31a219140934b799737c192aa8de55e5030bca
|
4
|
+
data.tar.gz: 1f7c614722781287cf6482af08fe97d4af5305fdfbd7583ce343277b7b8fe5a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f97f043eed52700808203c42ab9a714a7fdefdd33f3a201045a9a1649e675de6305386439f4707788a89842993902187c21b89939e4458e6a1750d25d6972d1
|
7
|
+
data.tar.gz: a3591c147f153964a0d929d8b49c43386b9f03bf53414471eed55d5312d9bb72e0fbd0ee49b966519570b4a13f33d4fa5ddda91c47d6fa31342af928cfa8185a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v3.28.1](https://github.com/bensheldon/good_job/tree/v3.28.1) (2024-04-24)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.28.0...v3.28.1)
|
6
|
+
|
7
|
+
**Fixed bugs:**
|
8
|
+
|
9
|
+
- Ensure job context does not leak into extensions when`perform_now` is called within another job [\#1336](https://github.com/bensheldon/good_job/pull/1336) ([bensheldon](https://github.com/bensheldon))
|
10
|
+
|
11
|
+
**Closed issues:**
|
12
|
+
|
13
|
+
- Executing perform\_now on a good\_job with GoodJobs::ActiveJobExtensions::Concurrency can run twice [\#1335](https://github.com/bensheldon/good_job/issues/1335)
|
14
|
+
- `erb_lint` doesn't lint all files [\#1329](https://github.com/bensheldon/good_job/issues/1329)
|
15
|
+
- Server hangs after \#1297 [\#1301](https://github.com/bensheldon/good_job/issues/1301)
|
16
|
+
- Figure out how to use bind parameters for time queries [\#1285](https://github.com/bensheldon/good_job/issues/1285)
|
17
|
+
|
18
|
+
**Merged pull requests:**
|
19
|
+
|
20
|
+
- Upgrade development / demo Ruby to v3.3.1 [\#1333](https://github.com/bensheldon/good_job/pull/1333) ([bensheldon](https://github.com/bensheldon))
|
21
|
+
- Exploratory testing for presence of scheduled\_at when enqueuing [\#1332](https://github.com/bensheldon/good_job/pull/1332) ([bensheldon](https://github.com/bensheldon))
|
22
|
+
- Run `erb_lint` against all the views and partials [\#1331](https://github.com/bensheldon/good_job/pull/1331) ([bensheldon](https://github.com/bensheldon))
|
23
|
+
|
3
24
|
## [v3.28.0](https://github.com/bensheldon/good_job/tree/v3.28.0) (2024-04-19)
|
4
25
|
|
5
26
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.27.4...v3.28.0)
|
@@ -2,14 +2,14 @@
|
|
2
2
|
<div class="list-group list-group-flush text-nowrap table-jobs" role="table">
|
3
3
|
<header class="list-group-item bg-body-tertiary">
|
4
4
|
<div class="row small text-muted text-uppercase align-items-center">
|
5
|
-
<div class="col-4"><%=t
|
6
|
-
<div class="d-none d-lg-block col-lg-1 text-lg-center"><%=t "good_job.models.job.queue" %></div>
|
7
|
-
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%=t "good_job.models.job.priority" %></div>
|
8
|
-
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%=t "good_job.models.job.attempts" %></div>
|
5
|
+
<div class="col-4"><%= t("good_job.models.batch.jobs") %></div>
|
6
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-center"><%= t "good_job.models.job.queue" %></div>
|
7
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.priority" %></div>
|
8
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.attempts" %></div>
|
9
9
|
<div class="col text-end">
|
10
10
|
<%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
|
11
11
|
data: { bs_toggle: "collapse", bs_target: ".job-params" },
|
12
|
-
aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job,
|
12
|
+
aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job, 'params')}" }.join(" ") } do %>
|
13
13
|
<%= render_icon "info" %>
|
14
14
|
<span class="visually-hidden">Inspect</span>
|
15
15
|
<% end %>
|
@@ -26,22 +26,24 @@
|
|
26
26
|
<%= tag.h5 tag.code(link_to(job.display_name, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
|
27
27
|
</div>
|
28
28
|
<div class="col-4 col-lg-1 text-lg-center">
|
29
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.queue" %></div>
|
29
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.queue" %></div>
|
30
30
|
<span class="badge bg-primary text-dark font-monospace"><%= job.queue_name %></span>
|
31
31
|
</div>
|
32
32
|
<div class="col-4 col-lg-1 text-lg-end">
|
33
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.priority" %>Priority</div>
|
33
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %>Priority</div>
|
34
34
|
<span class="font-monospace fw-bold"><%= job.priority %></span>
|
35
35
|
</div>
|
36
36
|
<div class="col-4 col-lg-1 text-lg-end">
|
37
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.attempts" %></div>
|
37
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
|
38
38
|
<% if job.executions_count > 0 && job.status != :finished %>
|
39
|
-
<%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
<%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
|
40
|
+
data: {
|
41
|
+
bs_toggle: "popover",
|
42
|
+
bs_trigger: "hover focus click",
|
43
|
+
bs_placement: "bottom",
|
44
|
+
bs_content: job.recent_error,
|
45
|
+
}
|
46
|
+
%>
|
45
47
|
<% else %>
|
46
48
|
<span class="badge bg-secondary rounded-pill"><%= job.executions_count %></span>
|
47
49
|
<% end %>
|
@@ -53,33 +55,33 @@
|
|
53
55
|
<div class="dropdown float-end">
|
54
56
|
<button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
|
55
57
|
<%= render_icon :dots %>
|
56
|
-
<span class="visually-hidden"><%=t ".actions.title" %></span>
|
58
|
+
<span class="visually-hidden"><%= t ".actions.title" %></span>
|
57
59
|
</button>
|
58
60
|
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
59
61
|
<li>
|
60
62
|
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
61
63
|
<%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t(".actions.reschedule"), data: { confirm: t(".actions.confirm_reschedule"), disable: true } do %>
|
62
64
|
<%= render_icon "skip_forward" %>
|
63
|
-
<%=t "good_job.actions.reschedule" %>
|
65
|
+
<%= t "good_job.actions.reschedule" %>
|
64
66
|
<% end %>
|
65
67
|
</li>
|
66
68
|
<li>
|
67
69
|
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
68
70
|
<%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t(".actions.discard"), data: { confirm: t(".actions.confirm_discard"), disable: true } do %>
|
69
71
|
<%= render_icon "stop" %>
|
70
|
-
<%=t "good_job.actions.discard" %>
|
72
|
+
<%= t "good_job.actions.discard" %>
|
71
73
|
<% end %>
|
72
74
|
</li>
|
73
75
|
<li>
|
74
76
|
<%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t(".actions.retry"), data: { confirm: t(".actions.confirm_retry"), disable: true } do %>
|
75
77
|
<%= render_icon "arrow_clockwise" %>
|
76
|
-
<%=t "good_job.actions.retry" %>
|
78
|
+
<%= t "good_job.actions.retry" %>
|
77
79
|
<% end %>
|
78
80
|
</li>
|
79
81
|
<li>
|
80
82
|
<%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :finished]}", title: t(".actions.destroy"), data: { confirm: t(".actions.confirm_destroy"), disable: true } do %>
|
81
83
|
<%= render_icon "trash" %>
|
82
|
-
<%=t "good_job.actions.destroy" %>
|
84
|
+
<%= t "good_job.actions.destroy" %>
|
83
85
|
<% end %>
|
84
86
|
</li>
|
85
87
|
|
@@ -89,7 +91,7 @@
|
|
89
91
|
data: { bs_toggle: "collapse" },
|
90
92
|
aria: { expanded: false, controls: dom_id(job, "params") } do %>
|
91
93
|
<%= render_icon "info" %>
|
92
|
-
<%=t "good_job.actions.inspect" %>
|
94
|
+
<%= t "good_job.actions.inspect" %>
|
93
95
|
<% end %>
|
94
96
|
</li>
|
95
97
|
</ul>
|
@@ -103,7 +105,7 @@
|
|
103
105
|
<% end %>
|
104
106
|
<% else %>
|
105
107
|
<div class="list-group-item py-4 text-center text-muted">
|
106
|
-
<%=t ".no_jobs_found" %>
|
108
|
+
<%= t ".no_jobs_found" %>
|
107
109
|
</div>
|
108
110
|
<% end %>
|
109
111
|
</div>
|
@@ -2,18 +2,18 @@
|
|
2
2
|
<div class="list-group list-group-flush text-nowrap table-batches" role="table">
|
3
3
|
<header class="list-group-item bg-body-tertiary">
|
4
4
|
<div class="row small text-muted text-uppercase align-items-center">
|
5
|
-
<div class="col-4"><%=t "good_job.models.batch.name" %></div>
|
6
|
-
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.created" %></div>
|
7
|
-
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.enqueued" %></div>
|
8
|
-
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.discarded" %></div>
|
9
|
-
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.finished" %></div>
|
10
|
-
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.jobs" %></div>
|
5
|
+
<div class="col-4"><%= t "good_job.models.batch.name" %></div>
|
6
|
+
<div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.batch.created" %></div>
|
7
|
+
<div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.batch.enqueued" %></div>
|
8
|
+
<div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.batch.discarded" %></div>
|
9
|
+
<div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.batch.finished" %></div>
|
10
|
+
<div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.batch.jobs" %></div>
|
11
11
|
<div class="col text-end">
|
12
12
|
<%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
|
13
13
|
data: { bs_toggle: "collapse", bs_target: ".batch-properties" },
|
14
|
-
aria: { expanded: false, controls: batches.map { |batch| "##{dom_id(batch,
|
14
|
+
aria: { expanded: false, controls: batches.map { |batch| "##{dom_id(batch, 'params')}" }.join(" ") } do %>
|
15
15
|
<%= render_icon "info" %>
|
16
|
-
<span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
|
16
|
+
<span class="visually-hidden"><%= t "good_job.actions.inspect" %></span>
|
17
17
|
<% end %>
|
18
18
|
</div>
|
19
19
|
</div>
|
@@ -33,29 +33,29 @@
|
|
33
33
|
<% end %>
|
34
34
|
</div>
|
35
35
|
<div class="col-6 col-lg-1 text-wrap">
|
36
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.created_at" %></div>
|
36
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.created_at" %></div>
|
37
37
|
<%= relative_time(batch.created_at) %>
|
38
38
|
</div>
|
39
39
|
<div class="col-6 col-lg-1 text-wrap">
|
40
40
|
<% if batch.enqueued_at %>
|
41
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.enqueued_at" %></div>
|
41
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.enqueued_at" %></div>
|
42
42
|
<%= relative_time(batch.enqueued_at) %>
|
43
43
|
<% end %>
|
44
44
|
</div>
|
45
45
|
<div class="col-6 col-lg-1 text-wrap">
|
46
46
|
<% if batch.discarded_at %>
|
47
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.discarded_at" %></div>
|
47
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.discarded_at" %></div>
|
48
48
|
<%= relative_time(batch.discarded_at) %>
|
49
49
|
<% end %>
|
50
50
|
</div>
|
51
51
|
<div class="col-6 col-lg-1 text-wrap">
|
52
52
|
<% if batch.finished_at %>
|
53
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.finished_at" %></div>
|
53
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.finished_at" %></div>
|
54
54
|
<%= relative_time(batch.finished_at) %>
|
55
55
|
<% end %>
|
56
56
|
</div>
|
57
57
|
<div class="col-6 col-lg-1">
|
58
|
-
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.jobs" %></div>
|
58
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.jobs" %></div>
|
59
59
|
<%= batch.jobs.count %>
|
60
60
|
</div>
|
61
61
|
<div class="col text-end">
|
@@ -64,7 +64,7 @@
|
|
64
64
|
data: { bs_toggle: "collapse", bs_target: "##{dom_id(batch, 'properties')}" },
|
65
65
|
aria: { expanded: false, controls: dom_id(batch, "state") } do %>
|
66
66
|
<%= render_icon "info" %>
|
67
|
-
<span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
|
67
|
+
<span class="visually-hidden"><%= t "good_job.actions.inspect" %></span>
|
68
68
|
<% end %>
|
69
69
|
</div>
|
70
70
|
</div>
|
@@ -75,7 +75,7 @@
|
|
75
75
|
<% end %>
|
76
76
|
<% else %>
|
77
77
|
<div class="list-group-item py-4 text-center text-muted">
|
78
|
-
<%=t ".no_batches_found" %>
|
78
|
+
<%= t ".no_batches_found" %>
|
79
79
|
</div>
|
80
80
|
<% end %>
|
81
81
|
</div>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<h5><%=t ".title" %></h5>
|
1
|
+
<h5><%= t ".title" %></h5>
|
2
2
|
<div class="card mb-4" data-live-poll-region="executions-table">
|
3
3
|
<div class="list-group list-group-flush">
|
4
4
|
<% executions.each do |execution| %>
|
@@ -10,12 +10,12 @@
|
|
10
10
|
</div>
|
11
11
|
<div class="col-md-2 small">
|
12
12
|
<% if execution.queue_latency %>
|
13
|
-
<%= format_duration execution.queue_latency %> <span class="text-muted"><%=t ".in_queue" %></span>
|
13
|
+
<%= format_duration execution.queue_latency %> <span class="text-muted"><%= t ".in_queue" %></span>
|
14
14
|
<% end %>
|
15
15
|
</div>
|
16
16
|
<div class="col-md-2 small">
|
17
17
|
<% if execution.runtime_latency %>
|
18
|
-
<%= format_duration execution.runtime_latency %> <span class="text-muted"><%=t ".runtime" %></span>
|
18
|
+
<%= format_duration execution.runtime_latency %> <span class="text-muted"><%= t ".runtime" %></span>
|
19
19
|
<% end %>
|
20
20
|
</div>
|
21
21
|
<div class="col">
|
@@ -28,20 +28,20 @@
|
|
28
28
|
data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
|
29
29
|
aria: { expanded: false, controls: dom_id(execution, "params") } do %>
|
30
30
|
<%= render_icon "info" %>
|
31
|
-
<span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
|
31
|
+
<span class="visually-hidden"><%= t "good_job.actions.inspect" %></span>
|
32
32
|
<% end %>
|
33
33
|
</div>
|
34
34
|
</div>
|
35
35
|
</div>
|
36
36
|
<% if execution.error %>
|
37
37
|
<div class="mt-3 small">
|
38
|
-
<strong class="small text-danger"><%=t "good_job.shared.error" %>:</strong>
|
38
|
+
<strong class="small text-danger"><%= t "good_job.shared.error" %>:</strong>
|
39
39
|
<code class="text-wrap text-break m-0 text-secondary-emphasis"><%= execution.error %></code>
|
40
40
|
</div>
|
41
41
|
<% if GoodJob::DiscreteExecution.backtrace_migrated? && execution.error_backtrace&.any? %>
|
42
|
-
<%= tag.ul class: "nav nav-tabs small w-fit-content", id: dom_id(execution, :tab), role:"tablist" do %>
|
42
|
+
<%= tag.ul class: "nav nav-tabs small w-fit-content", id: dom_id(execution, :tab), role: "tablist" do %>
|
43
43
|
<li class="nav-item" role="presentation">
|
44
|
-
<%= tag.button t(".application_trace"), class: "nav-link active p-1", id: dom_id(execution, :application), data: { bs_toggle: "tab", bs_target: dom_id(execution, :"#application_pane") }, type: "button", role: "tab", aria: { controls: dom_id(execution, :application_pane), selected: true
|
44
|
+
<%= tag.button t(".application_trace"), class: "nav-link active p-1", id: dom_id(execution, :application), data: { bs_toggle: "tab", bs_target: dom_id(execution, :"#application_pane") }, type: "button", role: "tab", aria: { controls: dom_id(execution, :application_pane), selected: true } %>
|
45
45
|
</li>
|
46
46
|
<li class="nav-item" role="presentation">
|
47
47
|
<%= tag.button t(".full_trace"), class: "nav-link p-1", id: dom_id(execution, :full), data: { bs_toggle: "tab", bs_target: dom_id(execution, :"#full_pane") }, type: "button", role: "tab", aria: { controls: dom_id(execution, :full_pane), selected: false } %>
|
@@ -1,176 +1,178 @@
|
|
1
1
|
<%= form_with(url: mass_update_jobs_path(filter.to_params), method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
|
2
|
-
<div class="my-3 card" data-gj-poll-replace id="jobs-table">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.retry_all"), data: { confirm: t(".actions.confirm_retry_all"), disable: true } do %>
|
16
|
-
<span class="me-1"><%= render_icon "arrow_clockwise" %></span> <%=t "good_job.actions.retry" %>
|
17
|
-
<% end %>
|
2
|
+
<div class="my-3 card" data-gj-poll-replace id="jobs-table">
|
3
|
+
<div class="list-group list-group-flush text-nowrap table-jobs" role="table">
|
4
|
+
<header class="list-group-item bg-body-tertiary">
|
5
|
+
<div class="row small text-muted text-uppercase align-items-center">
|
6
|
+
<div class="col-lg-4 d-flex gap-2 flex-wrap">
|
7
|
+
<div class="form-check d-flex flex-row px-0 mb-0">
|
8
|
+
<%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
|
9
|
+
<%= label_tag('toggle_job_ids', t(".toggle_all_jobs"), class: "visually-hidden") %>
|
10
|
+
</div>
|
11
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'ms-1 btn btn-sm btn-outline-secondary', title: t(".actions.reschedule_all"), data: { confirm: t(".actions.confirm_reschedule_all"), disable: true } do %>
|
12
|
+
<span class="me-1"><%= render_icon "skip_forward" %></span> <%= t "good_job.actions.reschedule" %>
|
13
|
+
<% end %>
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
<span class="me-1"><%= render_icon "stop" %></span> <%=t "good_job.actions.discard" %>
|
15
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.retry_all"), data: { confirm: t(".actions.confirm_retry_all"), disable: true } do %>
|
16
|
+
<span class="me-1"><%= render_icon "arrow_clockwise" %></span> <%= t "good_job.actions.retry" %>
|
22
17
|
<% end %>
|
23
|
-
<button id="destroy-dropdown-toggle" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
24
|
-
<span class="visually-hidden"><%=t ".toggle_actions" %></span>
|
25
|
-
</button>
|
26
|
-
<ul class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
|
27
|
-
<li>
|
28
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn dropdown-item', title: t(".actions.destroy_all"), data: { confirm: t(".actions.confirm_destroy_all"), disable: true } do %>
|
29
|
-
<span class="me-1"><%= render_icon "trash" %></span> <%=t "good_job.actions.destroy" %>
|
30
|
-
<% end %>
|
31
|
-
</li>
|
32
|
-
</ul>
|
33
|
-
</div>
|
34
18
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
19
|
+
<div class="btn-group" role="group">
|
20
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.discard_all"), data: { confirm: t(".actions.confirm_discard_all"), disable: true } do %>
|
21
|
+
<span class="me-1"><%= render_icon "stop" %></span> <%= t "good_job.actions.discard" %>
|
22
|
+
<% end %>
|
23
|
+
<button id="destroy-dropdown-toggle" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
24
|
+
<span class="visually-hidden"><%= t ".toggle_actions" %></span>
|
25
|
+
</button>
|
26
|
+
<ul class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
|
27
|
+
<li>
|
28
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn dropdown-item', title: t(".actions.destroy_all"), data: { confirm: t(".actions.confirm_destroy_all"), disable: true } do %>
|
29
|
+
<span class="me-1"><%= render_icon "trash" %></span> <%= t "good_job.actions.destroy" %>
|
30
|
+
<% end %>
|
31
|
+
</li>
|
32
|
+
</ul>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
</div>
|
36
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-center"><%= t "good_job.models.job.queue" %></div>
|
37
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.priority" %></div>
|
38
|
+
<div class="d-none d-lg-block col-lg-1 text-lg-end"><%= t "good_job.models.job.attempts" %></div>
|
39
|
+
<div class="col text-end">
|
40
|
+
<%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
|
41
|
+
data: { bs_toggle: "collapse", bs_target: ".job-params" },
|
42
|
+
aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job, 'params')}" }.join(" ") } do %>
|
43
43
|
<%= render_icon "info" %>
|
44
|
-
<span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
|
45
|
-
|
46
|
-
|
47
|
-
</div>
|
48
|
-
</header>
|
49
|
-
<label role="row" class="list-group-item list-group-item-warning list-group-item-action py-2 d-none" data-checkbox-toggle-show="job_ids">
|
50
|
-
<div class="row">
|
51
|
-
<div class="col-auto">
|
52
|
-
<%= check_box_tag "all_job_ids", 1, false, disabled: true, data: { "checkbox-toggle-show": "job_ids"} %>
|
44
|
+
<span class="visually-hidden"><%= t "good_job.actions.inspect" %></span>
|
45
|
+
<% end %>
|
46
|
+
</div>
|
53
47
|
</div>
|
54
|
-
|
55
|
-
|
48
|
+
</header>
|
49
|
+
<label role="row" class="list-group-item list-group-item-warning list-group-item-action py-2 d-none" data-checkbox-toggle-show="job_ids">
|
50
|
+
<div class="row">
|
51
|
+
<div class="col-auto">
|
52
|
+
<%= check_box_tag "all_job_ids", 1, false, disabled: true, data: { "checkbox-toggle-show": "job_ids" } %>
|
53
|
+
</div>
|
54
|
+
<div class="col-auto">
|
55
|
+
<%= t ".actions.apply_to_all", count: filter.filtered_count %>
|
56
|
+
</div>
|
56
57
|
</div>
|
57
|
-
</
|
58
|
-
</label>
|
58
|
+
</label>
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
60
|
+
<% if jobs.present? %>
|
61
|
+
<% jobs.each do |job| %>
|
62
|
+
<%= label_tag dom_id(job, :checkbox), id: dom_id(job), role: "row", class: "list-group-item list-group-item-action py-3" do %>
|
63
|
+
<div class="row align-items-center">
|
64
|
+
<div class="col-lg-4 d-flex">
|
65
|
+
<%= check_box_tag 'job_ids[]', job.id, false, id: dom_id(job, :checkbox), data: { "checkbox-toggle-each": "job_ids" } %>
|
66
|
+
<div class="ms-2">
|
67
|
+
<%= tag.code link_to(job.id, job_path(job), class: "small text-muted text-decoration-none") %>
|
68
|
+
<%= tag.h5 tag.code(link_to(job.display_name, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
|
69
|
+
<% if job.error %>
|
70
|
+
<div class="mt-1 small">
|
71
|
+
<strong class="small text-danger"><%= t "good_job.shared.error" %>:</strong>
|
72
|
+
<code class="text-wrap text-break m-0 text-secondary-emphasis"><%= job.error %></code>
|
73
|
+
</div>
|
74
|
+
<% end %>
|
75
|
+
</div>
|
75
76
|
</div>
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
77
|
+
<div class="col-4 col-lg-1 text-lg-center">
|
78
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.queue" %></div>
|
79
|
+
<span class="badge bg-primary font-monospace"><%= job.queue_name %></span>
|
80
|
+
</div>
|
81
|
+
<div class="col-4 col-lg-1 text-lg-end">
|
82
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %></div>
|
83
|
+
<%= job.priority %>
|
84
|
+
</div>
|
85
|
+
<div class="col-4 col-lg-1 text-lg-end">
|
86
|
+
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
|
87
|
+
<% if job.executions_count > 0 && job.status != :succeeded %>
|
88
|
+
<%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
|
89
|
+
data: {
|
89
90
|
bs_toggle: "popover",
|
90
91
|
bs_trigger: "hover focus click",
|
91
92
|
bs_placement: "bottom",
|
92
93
|
bs_content: job.display_error,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
<div class="
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
<
|
106
|
-
|
107
|
-
|
108
|
-
|
94
|
+
}
|
95
|
+
%>
|
96
|
+
<% else %>
|
97
|
+
<span class="badge bg-secondary rounded-pill"><%= job.executions_count %></span>
|
98
|
+
<% end %>
|
99
|
+
</div>
|
100
|
+
<div class="mt-3 mt-lg-0 col">
|
101
|
+
<div class="d-flex gap-3 align-items-start justify-content-end">
|
102
|
+
<%= tag.span relative_time(job.last_status_at), class: "small mt-1" %>
|
103
|
+
<div>
|
104
|
+
<%= status_badge job.status %>
|
105
|
+
<% if job.status == :discarded && job.class.error_event_migrated? && job.error_event %>
|
106
|
+
<div class="text-black text-center">
|
107
|
+
<small><%= t(job.error_event, scope: 'good_job.error_event') %></small>
|
108
|
+
</div>
|
109
|
+
<% end %>
|
110
|
+
</div>
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
112
|
+
<div class="dropdown float-end">
|
113
|
+
<button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
|
114
|
+
<%= render_icon "dots" %>
|
115
|
+
<span class="visually-hidden"><%= t ".actions.title" %></span>
|
116
|
+
</button>
|
117
|
+
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
118
|
+
<li>
|
119
|
+
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
120
|
+
<%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t("good_job.jobs.actions.reschedule"), data: { confirm: t("good_job.jobs.actions.confirm_reschedule"), disable: true } do %>
|
121
|
+
<%= render_icon "skip_forward" %>
|
122
|
+
<%= t "good_job.actions.reschedule" %>
|
123
|
+
<% end %>
|
124
|
+
</li>
|
125
|
+
<li>
|
126
|
+
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
127
|
+
<%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t("good_job.jobs.actions.discard"), data: { confirm: t("good_job.jobs.actions.confirm_discard"), disable: true } do %>
|
128
|
+
<%= render_icon "stop" %>
|
129
|
+
<%= t "good_job.actions.discard" %>
|
130
|
+
<% end %>
|
131
|
+
</li>
|
132
|
+
<li>
|
133
|
+
<% job_force_discardable = job.status.in? [:running] %>
|
134
|
+
<%= link_to force_discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_force_discardable}", title: t("good_job.jobs.actions.force_discard"), data: { confirm: t("good_job.jobs.actions.confirm_force_discard"), disable: true } do %>
|
135
|
+
<%= render_icon "eject" %>
|
136
|
+
<%= t "good_job.actions.force_discard" %>
|
137
|
+
<% end %>
|
138
|
+
</li>
|
139
|
+
<li>
|
140
|
+
<%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t("good_job.jobs.actions.retry"), data: { confirm: t("good_job.jobs.actions.confirm_retry"), disable: true } do %>
|
141
|
+
<%= render_icon "arrow_clockwise" %>
|
142
|
+
<%= t "good_job.actions.retry" %>
|
143
|
+
<% end %>
|
144
|
+
</li>
|
145
|
+
<li>
|
146
|
+
<%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :succeeded]}", title: t("good_job.jobs.actions.destroy"), data: { confirm: t("good_job.jobs.actions.confirm_destroy"), disable: true } do %>
|
147
|
+
<%= render_icon "trash" %>
|
148
|
+
<%= t "good_job.actions.destroy" %>
|
149
|
+
<% end %>
|
150
|
+
</li>
|
149
151
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
152
|
+
<li>
|
153
|
+
<%= link_to "##{dom_id(job, 'params')}",
|
154
|
+
class: "dropdown-item",
|
155
|
+
data: { bs_toggle: "collapse" },
|
156
|
+
aria: { expanded: false, controls: dom_id(job, "params") } do %>
|
157
|
+
<%= render_icon "info" %>
|
158
|
+
<%= t "good_job.actions.inspect" %>
|
159
|
+
<% end %>
|
160
|
+
</li>
|
161
|
+
</ul>
|
162
|
+
</div>
|
160
163
|
</div>
|
161
164
|
</div>
|
162
165
|
</div>
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
166
|
+
<% end %>
|
167
|
+
<%= tag.div id: dom_id(job, "params"), class: "job-params list-group-item collapse small bg-dark text-light" do %>
|
168
|
+
<%= tag.pre JSON.pretty_generate(job.display_serialized_params) %>
|
169
|
+
<% end %>
|
167
170
|
<% end %>
|
171
|
+
<% else %>
|
172
|
+
<div class="list-group-item py-4 text-center text-muted">
|
173
|
+
<%= t ".no_jobs_found" %>
|
174
|
+
</div>
|
168
175
|
<% end %>
|
169
|
-
|
170
|
-
<div class="list-group-item py-4 text-center text-muted">
|
171
|
-
<%=t ".no_jobs_found" %>
|
172
|
-
</div>
|
173
|
-
<% end %>
|
176
|
+
</div>
|
174
177
|
</div>
|
175
|
-
</div>
|
176
178
|
<% end %>
|
@@ -12,7 +12,7 @@
|
|
12
12
|
<div class="me-md-2 mb-2 mb-md-0">
|
13
13
|
<%= label_tag "job_queue_filter", t(".queue_name"), class: "visually-hidden" %>
|
14
14
|
<select name="queue_name" id="job_queue_filter" class="form-select form-select-sm">
|
15
|
-
<option value="" <%= "selected='selected'" if params[:queue_name].blank? %>><%=t ".all_queues" %></option>
|
15
|
+
<option value="" <%= "selected='selected'" if params[:queue_name].blank? %>><%= t ".all_queues" %></option>
|
16
16
|
|
17
17
|
<% filter.queues.each do |name, count| %>
|
18
18
|
<option value="<%= name.to_param %>" <%= "selected='selected'" if params[:queue_name] == name %>><%= name %> (<%= number_with_delimiter(count, translate_hash('good_job.number.format')) %>)</option>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<div class="me-md-2 mb-2 mb-md-0">
|
24
24
|
<%= label_tag "job_class_filter", t(".job_name"), class: "visually-hidden" %>
|
25
25
|
<select name="job_class" id="job_class_filter" class="form-select form-select-sm">
|
26
|
-
<option value="" <%= "selected='selected'" if params[:job_class].blank? %>><%=t ".all_jobs" %></option>
|
26
|
+
<option value="" <%= "selected='selected'" if params[:job_class].blank? %>><%= t ".all_jobs" %></option>
|
27
27
|
|
28
28
|
<% filter.job_classes.each do |name, count| %>
|
29
29
|
<option value="<%= name.to_param %>" <%= "selected='selected'" if params[:job_class] == name %>><%= name %> (<%= number_with_delimiter(count, translate_hash('good_job.number.format')) %>)</option>
|
@@ -33,14 +33,14 @@
|
|
33
33
|
|
34
34
|
<div class="me-md-2 mb-2 mb-md-0 flex-fill">
|
35
35
|
<%= label_tag "query", t(".search"), class: "visually-hidden" %>
|
36
|
-
<%= search_field_tag "query", params[:query], class: "form-control form-control-sm", placeholder: t(".placeholder") %>
|
36
|
+
<%= search_field_tag "query", params[:query], class: "form-control form-control-sm", placeholder: t(".placeholder"), autocomplete: "off" %>
|
37
37
|
</div>
|
38
38
|
|
39
39
|
<div class="d-flex flex-col align-items-end">
|
40
40
|
<div>
|
41
41
|
<%= form.submit t(".search"), name: nil, class: "btn btn-primary btn-sm" %>
|
42
42
|
<%= link_to filter.to_params(job_class: nil, state: nil, queue_name: nil, query: nil), class: "btn btn-secondary btn-sm" do %>
|
43
|
-
<%=t ".clear" %>
|
43
|
+
<%= t ".clear" %>
|
44
44
|
<% end %>
|
45
45
|
</div>
|
46
46
|
</div>
|
@@ -49,21 +49,21 @@
|
|
49
49
|
|
50
50
|
<ul data-controller="async-values" data-async-values-url-value="<%= metrics_job_status_path %>" data-live-poll-region="filter-tabs" class="nav nav-tabs my-3">
|
51
51
|
<li class="nav-item">
|
52
|
-
<%= link_to t(".all"), filter.to_params(state: nil), class: "nav-link #{
|
52
|
+
<%= link_to t(".all"), filter.to_params(state: nil), class: "nav-link #{'active' if params[:state].blank?}" %>
|
53
53
|
</li>
|
54
54
|
|
55
55
|
<% filter.state_names.each do |name| %>
|
56
56
|
<li class="nav-item">
|
57
|
-
<%= link_to filter.to_params(state: name), class: "nav-link #{
|
57
|
+
<%= link_to filter.to_params(state: name), class: "nav-link #{'active' if params[:state] == name}" do %>
|
58
58
|
<%= t(name, scope: 'good_job.status') %>
|
59
|
-
<span data-async-values-target="value"
|
59
|
+
<span data-async-values-target="value" data-async-values-key="<%= name %>" data-async-values-zero-class="bg-secondary" class="badge bg-primary rounded-pill d-none"></span>
|
60
60
|
<% end %>
|
61
61
|
</li>
|
62
62
|
<% end %>
|
63
63
|
</ul>
|
64
64
|
</div>
|
65
65
|
|
66
|
-
|
66
|
+
<script nonce="<%= content_security_policy_nonce %>">
|
67
67
|
document.addEventListener("DOMContentLoaded", () => {
|
68
68
|
document.querySelectorAll("#job_class_filter, #job_queue_filter").forEach((filter) => {
|
69
69
|
filter.addEventListener("change", () => {
|
@@ -71,5 +71,5 @@
|
|
71
71
|
});
|
72
72
|
})
|
73
73
|
})
|
74
|
-
|
74
|
+
</script>
|
75
75
|
</div>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
</li>
|
24
24
|
<li class="nav-item">
|
25
25
|
<%= link_to batches_path, class: ["nav-link", ("active" if controller_name == 'batches')] do %>
|
26
|
-
<%=t ".batches" %>
|
26
|
+
<%= t ".batches" %>
|
27
27
|
<span data-async-values-target="value" data-async-values-key="batches_count" class="badge bg-secondary rounded-pill d-none"></span>
|
28
28
|
<% end %>
|
29
29
|
</li>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
</div>
|
6
6
|
<div class="flex-fill text-end">
|
7
7
|
<% last_updated_at = Time.current.utc.iso8601 %>
|
8
|
-
<%= t(".last_updated") %> <time id="page-updated-at" datetime="<%= last_updated_at %>"
|
8
|
+
<%= t(".last_updated") %> <time id="page-updated-at" datetime="<%= last_updated_at %>"><%= last_updated_at %></time>
|
9
9
|
</div>
|
10
10
|
</div>
|
11
11
|
</nav>
|
@@ -12,13 +12,13 @@
|
|
12
12
|
to reduce potential screen flickering during reloading of your site."
|
13
13
|
https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript
|
14
14
|
%>
|
15
|
-
|
15
|
+
<script nonce="<%= content_security_policy_nonce %>">
|
16
16
|
let theme = localStorage.getItem('good_job-theme');
|
17
17
|
if (!["light", "dark"].includes(theme)) {
|
18
18
|
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
19
19
|
}
|
20
20
|
document.documentElement.setAttribute('data-bs-theme', theme);
|
21
|
-
|
21
|
+
</script>
|
22
22
|
|
23
23
|
<%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
|
24
24
|
<%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
@@ -6,7 +6,7 @@ module GoodJob
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
def batch
|
9
|
-
@_batch ||= CurrentThread.execution&.batch&.to_batch
|
9
|
+
@_batch ||= CurrentThread.execution&.batch&.to_batch if CurrentThread.execution.present? && CurrentThread.execution.active_job_id == job_id
|
10
10
|
end
|
11
11
|
alias batch? batch
|
12
12
|
end
|
@@ -75,7 +75,7 @@ module GoodJob
|
|
75
75
|
key = job.good_job_concurrency_key
|
76
76
|
next if key.blank?
|
77
77
|
|
78
|
-
if CurrentThread.execution.blank?
|
78
|
+
if CurrentThread.execution.blank? || CurrentThread.execution.active_job_id != job_id
|
79
79
|
logger.debug("Ignoring concurrency limits because the job is executed with `perform_now`.")
|
80
80
|
next
|
81
81
|
end
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.28.
|
4
|
+
version: 3.28.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -431,7 +431,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
431
431
|
- !ruby/object:Gem::Version
|
432
432
|
version: '0'
|
433
433
|
requirements: []
|
434
|
-
rubygems_version: 3.5.
|
434
|
+
rubygems_version: 3.5.9
|
435
435
|
signing_key:
|
436
436
|
specification_version: 4
|
437
437
|
summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
|