maintenance_tasks 2.1.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|