maintenance_tasks 2.1.0 → 2.2.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 +4 -4
- data/README.md +48 -0
- data/app/controllers/maintenance_tasks/application_controller.rb +2 -7
- data/app/controllers/maintenance_tasks/runs_controller.rb +1 -1
- data/app/helpers/maintenance_tasks/tasks_helper.rb +15 -1
- data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +3 -3
- data/app/models/maintenance_tasks/run.rb +0 -1
- data/app/models/maintenance_tasks/task.rb +7 -1
- data/app/views/layouts/maintenance_tasks/application.html.erb +9 -7
- data/app/views/maintenance_tasks/runs/_arguments.html.erb +2 -2
- data/app/views/maintenance_tasks/runs/_run.html.erb +1 -1
- data/app/views/maintenance_tasks/tasks/index.html.erb +22 -20
- data/app/views/maintenance_tasks/tasks/show.html.erb +12 -10
- data/db/migrate/20210219212931_change_cursor_to_string.rb +18 -0
- data/lib/maintenance_tasks.rb +10 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04c33ddef00ee39487f42a7275fd3b11367bc16f32007a607e2d6fd7a9b3b55d
|
4
|
+
data.tar.gz: 841a336d86ce65e52d2ddee9cef746f9a448664328213056ea8c7a9f0d861b76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cefc3a619d3d3aec10c6104cf227c33558e1d30146f0d9338706eb30c4e21f58bda704905c85a8faa87bc4735d5255b714c8690b6b733f9dcde42216231abc3d
|
7
|
+
data.tar.gz: 4f23532262622d91da4065926f19e96bb22788877acc10e1e7a0ce327e7136a2c24905e9a29aacbbdbe380c39dc58e593a0254e6bfec2b1c6d929f8e5fe13304
|
data/README.md
CHANGED
@@ -696,6 +696,26 @@ MaintenanceTasks.tasks_module = "TaskModule"
|
|
696
696
|
|
697
697
|
If no value is specified, it will default to `Maintenance`.
|
698
698
|
|
699
|
+
#### Organizing tasks using namespaces
|
700
|
+
|
701
|
+
Tasks may be nested arbitrarily deeply under `app/tasks/maintenance`, for example given a
|
702
|
+
task file `app/tasks/maintenance/team_name/service_name/update_posts_task.rb` we
|
703
|
+
can define the task as:
|
704
|
+
|
705
|
+
```ruby
|
706
|
+
module Maintenance
|
707
|
+
module TeamName
|
708
|
+
module ServiceName
|
709
|
+
class UpdatePostsTask < MaintenanceTasks::Task
|
710
|
+
def process(rows)
|
711
|
+
# ...
|
712
|
+
end
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
end
|
717
|
+
```
|
718
|
+
|
699
719
|
#### Customizing the underlying job class
|
700
720
|
|
701
721
|
`MaintenanceTasks.job` can be configured to define a Job class for your tasks to
|
@@ -784,6 +804,34 @@ MaintenanceTasks.backtrace_cleaner = cleaner
|
|
784
804
|
If none is specified, the default `Rails.backtrace_cleaner` will be used to
|
785
805
|
clean backtraces.
|
786
806
|
|
807
|
+
#### Customizing the parent controller for the web UI
|
808
|
+
|
809
|
+
`MaintenanceTasks.parent_controller` can be configured to specify a controller class for all of the web UI engine's
|
810
|
+
controllers to inherit from.
|
811
|
+
|
812
|
+
This allows applications with common logic in their `ApplicationController` (or
|
813
|
+
any other controller) to optionally configure the web UI to inherit that logic
|
814
|
+
with a simple assignment in the initializer.
|
815
|
+
|
816
|
+
```ruby
|
817
|
+
# config/initializers/maintenance_tasks.rb
|
818
|
+
|
819
|
+
MaintenanceTasks.parent_controller = "Services::CustomController"
|
820
|
+
|
821
|
+
# app/controllers/services/custom_controller.rb
|
822
|
+
|
823
|
+
class Services::CustomController < ActionController::Base
|
824
|
+
include CustomSecurityThings
|
825
|
+
include CustomLoggingThings
|
826
|
+
...
|
827
|
+
end
|
828
|
+
```
|
829
|
+
|
830
|
+
The parent controller value **must** be a string corresponding to an existing
|
831
|
+
controller class which **must inherit** from `ActionController::Base`.
|
832
|
+
|
833
|
+
If no value is specified, it will default to `"ActionController::Base"`.
|
834
|
+
|
787
835
|
## Upgrading
|
788
836
|
|
789
837
|
Use bundler to check for and upgrade to newer versions. After installing a new
|
@@ -4,7 +4,7 @@ module MaintenanceTasks
|
|
4
4
|
# Base class for all controllers used by this engine.
|
5
5
|
#
|
6
6
|
# Can be extended to add different authentication and authorization code.
|
7
|
-
class ApplicationController <
|
7
|
+
class ApplicationController < MaintenanceTasks.parent_controller.constantize
|
8
8
|
BULMA_CDN = "https://cdn.jsdelivr.net"
|
9
9
|
|
10
10
|
content_security_policy do |policy|
|
@@ -15,16 +15,11 @@ module MaintenanceTasks
|
|
15
15
|
)
|
16
16
|
policy.script_src(
|
17
17
|
# page refresh script
|
18
|
-
"'sha256-
|
18
|
+
"'sha256-NiHKryHWudRC2IteTqmY9v1VkaDUA/5jhgXkMTkgo2w='",
|
19
19
|
)
|
20
20
|
policy.frame_ancestors(:self)
|
21
21
|
end
|
22
22
|
|
23
|
-
before_action do
|
24
|
-
request.content_security_policy_nonce_generator ||= ->(_request) { SecureRandom.base64(16) }
|
25
|
-
request.content_security_policy_nonce_directives = ["style-src"]
|
26
|
-
end
|
27
|
-
|
28
23
|
protect_from_forgery with: :exception
|
29
24
|
end
|
30
25
|
end
|
@@ -20,7 +20,7 @@ module MaintenanceTasks
|
|
20
20
|
rescue ActiveRecord::RecordInvalid => error
|
21
21
|
redirect_to(task_path(error.record.task_name), alert: error.message)
|
22
22
|
rescue ActiveRecord::ValueTooLong => error
|
23
|
-
task_name = params.fetch(:
|
23
|
+
task_name = params.fetch(:task_id)
|
24
24
|
redirect_to(task_path(task_name), alert: error.message)
|
25
25
|
rescue Runner::EnqueuingError => error
|
26
26
|
redirect_to(task_path(error.run.task_name), alert: error.message)
|
@@ -109,7 +109,7 @@ module MaintenanceTasks
|
|
109
109
|
when ActiveModel::Type::Decimal, ActiveModel::Type::Float
|
110
110
|
form_builder.number_field(parameter_name, { step: "any" })
|
111
111
|
when ActiveModel::Type::DateTime
|
112
|
-
form_builder.datetime_field(parameter_name)
|
112
|
+
form_builder.datetime_field(parameter_name) + datetime_field_help_text
|
113
113
|
when ActiveModel::Type::Date
|
114
114
|
form_builder.date_field(parameter_name)
|
115
115
|
when ActiveModel::Type::Time
|
@@ -120,5 +120,19 @@ module MaintenanceTasks
|
|
120
120
|
form_builder.text_area(parameter_name, class: "textarea")
|
121
121
|
end
|
122
122
|
end
|
123
|
+
|
124
|
+
# Return helper text for the datetime-local form field.
|
125
|
+
def datetime_field_help_text
|
126
|
+
text =
|
127
|
+
if Time.zone_default.nil? || Time.zone_default.name == "UTC"
|
128
|
+
"Timezone: UTC."
|
129
|
+
else
|
130
|
+
"Timezone: #{Time.now.zone}."
|
131
|
+
end
|
132
|
+
tag.div(
|
133
|
+
tag.p(text),
|
134
|
+
class: "content is-small",
|
135
|
+
)
|
136
|
+
end
|
123
137
|
end
|
124
138
|
end
|
@@ -56,14 +56,14 @@ module MaintenanceTasks
|
|
56
56
|
batch_size: collection.batch_size,
|
57
57
|
)
|
58
58
|
when Array
|
59
|
-
enumerator_builder.build_array_enumerator(collection, cursor: cursor)
|
59
|
+
enumerator_builder.build_array_enumerator(collection, cursor: cursor&.to_i)
|
60
60
|
when BatchCsvCollectionBuilder::BatchCsv
|
61
61
|
JobIteration::CsvEnumerator.new(collection.csv).batches(
|
62
62
|
batch_size: collection.batch_size,
|
63
|
-
cursor: cursor,
|
63
|
+
cursor: cursor&.to_i,
|
64
64
|
)
|
65
65
|
when CSV
|
66
|
-
JobIteration::CsvEnumerator.new(collection).rows(cursor: cursor)
|
66
|
+
JobIteration::CsvEnumerator.new(collection).rows(cursor: cursor&.to_i)
|
67
67
|
else
|
68
68
|
raise ArgumentError, <<~MSG.squish
|
69
69
|
#{@task.class.name}#collection must be either an
|
@@ -175,7 +175,13 @@ module MaintenanceTasks
|
|
175
175
|
namespace = MaintenanceTasks.tasks_module.safe_constantize
|
176
176
|
return unless namespace
|
177
177
|
|
178
|
-
|
178
|
+
load_const = lambda do |root|
|
179
|
+
root.constants.each do |name|
|
180
|
+
object = root.const_get(name)
|
181
|
+
load_const.call(object) if object.instance_of?(Module)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
load_const.call(namespace)
|
179
185
|
end
|
180
186
|
end
|
181
187
|
|
@@ -33,17 +33,19 @@
|
|
33
33
|
|
34
34
|
<script>
|
35
35
|
function refresh() {
|
36
|
-
|
36
|
+
const target = document.querySelector("[data-refresh]")
|
37
|
+
if (!target || !target.dataset.refresh) return
|
37
38
|
window.setTimeout(() => {
|
38
39
|
document.body.style.cursor = "wait"
|
39
40
|
fetch(document.location, { headers: { "X-Requested-With": "XMLHttpRequest" } }).then(
|
40
41
|
async response => {
|
41
42
|
const text = await response.text()
|
42
43
|
const newDocument = new DOMParser().parseFromString(text, "text/html")
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
const newTarget = newDocument.querySelector("[data-refresh]")
|
45
|
+
if (newTarget) {
|
46
|
+
target.replaceWith(newTarget)
|
47
|
+
}
|
48
|
+
document.body.style.cursor = ""
|
47
49
|
refresh()
|
48
50
|
},
|
49
51
|
error => location.reload()
|
@@ -54,7 +56,7 @@
|
|
54
56
|
</script>
|
55
57
|
</head>
|
56
58
|
|
57
|
-
|
59
|
+
<body>
|
58
60
|
<%= render 'layouts/maintenance_tasks/navbar' %>
|
59
61
|
|
60
62
|
<section class="section">
|
@@ -68,5 +70,5 @@
|
|
68
70
|
<%= yield %>
|
69
71
|
</div>
|
70
72
|
</div>
|
71
|
-
|
73
|
+
</body>
|
72
74
|
</html>
|
@@ -1,22 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
<%
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
<%= tag.div(data: { refresh: (defined?(@refresh) && @refresh) || "" }) do %>
|
2
|
+
<% if @available_tasks.empty? %>
|
3
|
+
<div class="content is-large">
|
4
|
+
<h3 class="title is-3"> The MaintenanceTasks gem has been successfully installed! </h3>
|
5
|
+
<p>
|
6
|
+
Any new Tasks will show up here. To start writing your first Task,
|
7
|
+
run <code>bin/rails generate maintenance_tasks:task my_task</code>.
|
8
|
+
</p>
|
9
|
+
</div>
|
10
|
+
<% else %>
|
11
|
+
<% if active_tasks = @available_tasks[:active] %>
|
12
|
+
<h3 class="title is-3">Active Tasks</h3>
|
13
|
+
<%= render partial: 'task', collection: active_tasks %>
|
14
|
+
<% end %>
|
15
|
+
<% if new_tasks = @available_tasks[:new] %>
|
16
|
+
<h3 class="title is-3">New Tasks</h3>
|
17
|
+
<%= render partial: 'task', collection: new_tasks %>
|
18
|
+
<% end %>
|
19
|
+
<% if completed_tasks = @available_tasks[:completed] %>
|
20
|
+
<h3 class="title is-3">Completed Tasks</h3>
|
21
|
+
<%= render partial: 'task', collection: completed_tasks %>
|
22
|
+
<% end %>
|
21
23
|
<% end %>
|
22
24
|
<% end %>
|
@@ -38,20 +38,22 @@
|
|
38
38
|
<pre><code><%= highlight_code(code) %></code></pre>
|
39
39
|
<% end %>
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
<%= tag.div(data: { refresh: (defined?(@refresh) && @refresh) || "" }) do %>
|
42
|
+
<% if @task.active_runs.any? %>
|
43
|
+
<hr/>
|
43
44
|
|
44
|
-
|
45
|
+
<h4 class="title is-4">Active Runs</h4>
|
45
46
|
|
46
|
-
|
47
|
-
<% end %>
|
47
|
+
<%= render @task.active_runs %>
|
48
|
+
<% end %>
|
48
49
|
|
49
|
-
<% if @runs_page.records.present? %>
|
50
|
-
|
50
|
+
<% if @runs_page.records.present? %>
|
51
|
+
<hr/>
|
51
52
|
|
52
|
-
|
53
|
+
<h4 class="title is-4">Previous Runs</h4>
|
53
54
|
|
54
|
-
|
55
|
+
<%= render @runs_page.records %>
|
55
56
|
|
56
|
-
|
57
|
+
<%= link_to "Next page", task_path(@task, cursor: @runs_page.next_cursor) unless @runs_page.last? %>
|
58
|
+
<% end %>
|
57
59
|
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChangeCursorToString < ActiveRecord::Migration[6.0]
|
4
|
+
# This migration will clear all existing data in the cursor column with MySQL.
|
5
|
+
# Ensure no Tasks are paused when this migration is deployed, or they will be resumed from the start.
|
6
|
+
# Running tasks are able to gracefully handle this change, even if interrupted.
|
7
|
+
def up
|
8
|
+
change_table(:maintenance_tasks_runs) do |t|
|
9
|
+
t.change(:cursor, :string)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
change_table(:maintenance_tasks_runs) do |t|
|
15
|
+
t.change(:cursor, :bigint)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/maintenance_tasks.rb
CHANGED
@@ -72,4 +72,14 @@ module MaintenanceTasks
|
|
72
72
|
# @return [Proc] the callback to perform when an error occurs in the Task.
|
73
73
|
mattr_accessor :error_handler, default:
|
74
74
|
->(_error, _task_context, _errored_element) {}
|
75
|
+
|
76
|
+
# @!attribute parent_controller
|
77
|
+
# @scope class
|
78
|
+
#
|
79
|
+
# The parent controller all web UI controllers will inherit from.
|
80
|
+
# Must be a class that inherits from `ActionController::Base`.
|
81
|
+
# Defaults to `"ActionController::Base"`
|
82
|
+
#
|
83
|
+
# @return [String] the name of the parent controller for web UI.
|
84
|
+
mattr_accessor :parent_controller, default: "ActionController::Base"
|
75
85
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maintenance_tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02
|
11
|
+
date: 2023-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- app/views/maintenance_tasks/tasks/show.html.erb
|
132
132
|
- config/routes.rb
|
133
133
|
- db/migrate/20201211151756_create_maintenance_tasks_runs.rb
|
134
|
+
- db/migrate/20210219212931_change_cursor_to_string.rb
|
134
135
|
- db/migrate/20210225152418_remove_index_on_task_name.rb
|
135
136
|
- db/migrate/20210517131953_add_arguments_to_maintenance_tasks_runs.rb
|
136
137
|
- db/migrate/20211210152329_add_lock_version_to_maintenance_tasks_runs.rb
|
@@ -154,7 +155,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
|
|
154
155
|
licenses:
|
155
156
|
- MIT
|
156
157
|
metadata:
|
157
|
-
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.
|
158
|
+
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.2.0
|
158
159
|
allowed_push_host: https://rubygems.org
|
159
160
|
post_install_message:
|
160
161
|
rdoc_options: []
|
@@ -164,14 +165,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
165
|
requirements:
|
165
166
|
- - ">="
|
166
167
|
- !ruby/object:Gem::Version
|
167
|
-
version: '0'
|
168
|
+
version: '3.0'
|
168
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
170
|
requirements:
|
170
171
|
- - ">="
|
171
172
|
- !ruby/object:Gem::Version
|
172
173
|
version: '0'
|
173
174
|
requirements: []
|
174
|
-
rubygems_version: 3.
|
175
|
+
rubygems_version: 3.4.17
|
175
176
|
signing_key:
|
176
177
|
specification_version: 4
|
177
178
|
summary: A Rails engine for queuing and managing maintenance tasks
|