maintenance_tasks 2.1.1 → 2.3.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 +41 -0
- data/app/controllers/maintenance_tasks/application_controller.rb +1 -6
- data/app/controllers/maintenance_tasks/runs_controller.rb +2 -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 +12 -4
- data/app/models/maintenance_tasks/runner.rb +2 -2
- data/app/views/maintenance_tasks/runs/_arguments.html.erb +2 -2
- data/app/views/maintenance_tasks/runs/_run.html.erb +1 -1
- data/db/migrate/20210219212931_change_cursor_to_string.rb +18 -0
- data/db/migrate/20230622035229_add_metadata_to_runs.rb +7 -0
- data/lib/maintenance_tasks.rb +17 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a14c442d683123d8aabb30db8c73c15a31ddd24852356aa64b0255b1cb2312e0
|
4
|
+
data.tar.gz: d5df5cbfa0cf9feab143ae2ea15cb5a0013978b552a068d9a588fed4e04178bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 528669b01c611fe0e502683665182bddb2a98f2a74911c5d957f3868fca2ff57323a07eb2093acdfedc39e44057f8346702f70fc67cf027f59a32b985a6f7cea
|
7
|
+
data.tar.gz: bb00d7ef688b49ad9ae295650eea0b57718ea95aea6b341780011d882ae2b0f9ac830241cd9663d3bd7c9b9e08b3ee9bf6daa91a4ef2e4c53b69a15350c2da06
|
data/README.md
CHANGED
@@ -804,6 +804,47 @@ MaintenanceTasks.backtrace_cleaner = cleaner
|
|
804
804
|
If none is specified, the default `Rails.backtrace_cleaner` will be used to
|
805
805
|
clean backtraces.
|
806
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
|
+
|
835
|
+
### Metadata
|
836
|
+
|
837
|
+
`MaintenanceTasks.metadata` can be configured to specify a proc from which to get extra information about the run.
|
838
|
+
Since this proc will be ran in the context of the `MaintenanceTasks.parent_controller`, it can be used to keep the id
|
839
|
+
or email of the user who performed the maintenance task.
|
840
|
+
|
841
|
+
```ruby
|
842
|
+
# config/initializers/maintenance_tasks.rb
|
843
|
+
MaintenanceTasks.metadata = -> do
|
844
|
+
{ user_email: current_user.email }
|
845
|
+
end
|
846
|
+
```
|
847
|
+
|
807
848
|
## Upgrading
|
808
849
|
|
809
850
|
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|
|
@@ -20,11 +20,6 @@ module MaintenanceTasks
|
|
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
|
@@ -14,13 +14,14 @@ module MaintenanceTasks
|
|
14
14
|
name: params.fetch(:task_id),
|
15
15
|
csv_file: params[:csv_file],
|
16
16
|
arguments: params.fetch(:task_arguments, {}).permit!.to_h,
|
17
|
+
metadata: MaintenanceTasks.metadata&.call,
|
17
18
|
&block
|
18
19
|
)
|
19
20
|
redirect_to(task_path(task))
|
20
21
|
rescue ActiveRecord::RecordInvalid => error
|
21
22
|
redirect_to(task_path(error.record.task_name), alert: error.message)
|
22
23
|
rescue ActiveRecord::ValueTooLong => error
|
23
|
-
task_name = params.fetch(:
|
24
|
+
task_name = params.fetch(:task_id)
|
24
25
|
redirect_to(task_path(task_name), alert: error.message)
|
25
26
|
rescue Runner::EnqueuingError => error
|
26
27
|
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
|
@@ -32,14 +32,11 @@ module MaintenanceTasks
|
|
32
32
|
:cancelled,
|
33
33
|
]
|
34
34
|
COMPLETED_STATUSES = [:succeeded, :errored, :cancelled]
|
35
|
-
COMPLETED_RUNS_LIMIT = 10
|
36
35
|
STUCK_TASK_TIMEOUT = 5.minutes
|
37
36
|
|
38
37
|
enum status: STATUSES.to_h { |status| [status, status.to_s] }
|
39
38
|
|
40
|
-
|
41
|
-
in: ->(_) { Task.available_tasks.map(&:to_s) },
|
42
|
-
}
|
39
|
+
validate :task_name_belongs_to_a_valid_task, on: :create
|
43
40
|
validate :csv_attachment_presence, on: :create
|
44
41
|
validate :csv_content_type, on: :create
|
45
42
|
validate :validate_task_arguments, on: :create
|
@@ -49,9 +46,11 @@ module MaintenanceTasks
|
|
49
46
|
if Rails.gem_version >= Gem::Version.new("7.1.alpha")
|
50
47
|
serialize :backtrace, coder: YAML
|
51
48
|
serialize :arguments, coder: JSON
|
49
|
+
serialize :metadata, coder: JSON
|
52
50
|
else
|
53
51
|
serialize :backtrace
|
54
52
|
serialize :arguments, JSON
|
53
|
+
serialize :metadata, JSON
|
55
54
|
end
|
56
55
|
|
57
56
|
scope :active, -> { where(status: ACTIVE_STATUSES) }
|
@@ -338,6 +337,15 @@ module MaintenanceTasks
|
|
338
337
|
cancelling? && updated_at <= STUCK_TASK_TIMEOUT.ago
|
339
338
|
end
|
340
339
|
|
340
|
+
# Performs validation on the task_name attribute.
|
341
|
+
# A Run must be associated with a valid Task to be valid.
|
342
|
+
# In order to confirm that, the Task is looked up by name.
|
343
|
+
def task_name_belongs_to_a_valid_task
|
344
|
+
Task.named(task_name)
|
345
|
+
rescue Task::NotFoundError
|
346
|
+
errors.add(:task_name, "must be the name of an existing Task.")
|
347
|
+
end
|
348
|
+
|
341
349
|
# Performs validation on the presence of a :csv_file attachment.
|
342
350
|
# A Run for a Task that uses CsvCollection must have an attached :csv_file
|
343
351
|
# to be valid. Conversely, a Run for a Task that doesn't use CsvCollection
|
@@ -39,8 +39,8 @@ module MaintenanceTasks
|
|
39
39
|
# creating the Run.
|
40
40
|
# @raise [ActiveRecord::ValueTooLong] if the creation of the Run fails due
|
41
41
|
# to a value being too long for the column type.
|
42
|
-
def run(name:, csv_file: nil, arguments: {}, run_model: Run)
|
43
|
-
run = run_model.new(task_name: name, arguments: arguments)
|
42
|
+
def run(name:, csv_file: nil, arguments: {}, run_model: Run, metadata: nil)
|
43
|
+
run = run_model.new(task_name: name, arguments: arguments, metadata: metadata)
|
44
44
|
if csv_file
|
45
45
|
run.csv_file.attach(csv_file)
|
46
46
|
run.csv_file.filename = filename(name)
|
@@ -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,21 @@ 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"
|
85
|
+
|
86
|
+
# @!attribute metadata
|
87
|
+
# @scope class
|
88
|
+
# The Proc to call from the controller to generate metadata that will be persisted on the Run.
|
89
|
+
#
|
90
|
+
# @return [Proc] generates a hash containing the metadata to be stored on the Run
|
91
|
+
mattr_accessor :metadata, default: nil
|
75
92
|
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.3.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-
|
11
|
+
date: 2023-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -131,11 +131,13 @@ 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
|
137
138
|
- db/migrate/20220706101937_change_runs_tick_columns_to_bigints.rb
|
138
139
|
- db/migrate/20220713131925_add_index_on_task_name_and_status_to_runs.rb
|
140
|
+
- db/migrate/20230622035229_add_metadata_to_runs.rb
|
139
141
|
- exe/maintenance_tasks
|
140
142
|
- lib/generators/maintenance_tasks/install_generator.rb
|
141
143
|
- lib/generators/maintenance_tasks/task_generator.rb
|
@@ -154,7 +156,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
|
|
154
156
|
licenses:
|
155
157
|
- MIT
|
156
158
|
metadata:
|
157
|
-
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.
|
159
|
+
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.3.0
|
158
160
|
allowed_push_host: https://rubygems.org
|
159
161
|
post_install_message:
|
160
162
|
rdoc_options: []
|
@@ -164,14 +166,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
166
|
requirements:
|
165
167
|
- - ">="
|
166
168
|
- !ruby/object:Gem::Version
|
167
|
-
version: '0'
|
169
|
+
version: '3.0'
|
168
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
171
|
requirements:
|
170
172
|
- - ">="
|
171
173
|
- !ruby/object:Gem::Version
|
172
174
|
version: '0'
|
173
175
|
requirements: []
|
174
|
-
rubygems_version: 3.4.
|
176
|
+
rubygems_version: 3.4.18
|
175
177
|
signing_key:
|
176
178
|
specification_version: 4
|
177
179
|
summary: A Rails engine for queuing and managing maintenance tasks
|