maintenance_tasks 2.11.0 → 2.12.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 +78 -44
- data/app/controllers/maintenance_tasks/application_controller.rb +9 -5
- data/app/helpers/maintenance_tasks/tasks_helper.rb +28 -11
- data/app/models/maintenance_tasks/run.rb +17 -0
- data/app/models/maintenance_tasks/task.rb +19 -3
- data/app/models/maintenance_tasks/task_data_index.rb +2 -1
- data/app/views/layouts/maintenance_tasks/_navbar.html.erb +1 -1
- data/app/views/layouts/maintenance_tasks/application.html.erb +33 -3
- data/app/views/maintenance_tasks/runs/_arguments.html.erb +1 -1
- data/app/views/maintenance_tasks/runs/_run.html.erb +14 -8
- data/app/views/maintenance_tasks/runs/_serializable.html.erb +15 -18
- data/app/views/maintenance_tasks/tasks/_task.html.erb +4 -4
- data/app/views/maintenance_tasks/tasks/index.html.erb +6 -4
- data/app/views/maintenance_tasks/tasks/show.html.erb +16 -12
- data/db/migrate/20201211151756_create_maintenance_tasks_runs.rb +9 -1
- data/lib/maintenance_tasks.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d307d92f96c1d1975cc3fbefcddf6ec2e11086097437b63594090c5bc524eea8
|
4
|
+
data.tar.gz: 9176d7a34c4a54f450601ea2748a88b1a9f134c9e416e97978e613bf3fbf1538
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87d37b437e518c0398023335fe88cb0c7829387142873eeb01aa713df04f397e7711491f0ace1a32baae20f4f1a613313c59e95696deef73d6ecfad24b6fb267
|
7
|
+
data.tar.gz: b0c5fce124651391b77b8f2e5d91f8eb84c0415744fda8429b11a64b730a2e1ef02335c05685a98946fffdfd5042aa3b6f2a23a645a454613acd364af612b09d
|
data/README.md
CHANGED
@@ -74,9 +74,11 @@ The generator creates and runs a migration to add the necessary table to your
|
|
74
74
|
database. It also mounts Maintenance Tasks in your `config/routes.rb`. By
|
75
75
|
default the web UI can be accessed in the new `/maintenance_tasks` path.
|
76
76
|
|
77
|
-
This gem uses the [Rails Error Reporter]
|
78
|
-
tracking service you may want to subscribe to the
|
79
|
-
for more information.
|
77
|
+
This gem uses the [Rails Error Reporter][rails-error-reporting] to report errors.
|
78
|
+
If you are using a bug tracking service you may want to subscribe to the
|
79
|
+
reporter. See [Reporting Errors](#reporting-errors) for more information.
|
80
|
+
|
81
|
+
[rails-error-reporting]: https://guides.rubyonrails.org/error_reporting.html
|
80
82
|
|
81
83
|
### Active Job Dependency
|
82
84
|
|
@@ -114,12 +116,12 @@ The typical Maintenance Tasks workflow is as follows:
|
|
114
116
|
1. [Generate a class describing the Task](#creating-a-task) and the work to be
|
115
117
|
done.
|
116
118
|
2. Run the Task
|
117
|
-
|
118
|
-
|
119
|
-
|
119
|
+
- either by [using the included web UI](#running-a-task-from-the-web-ui),
|
120
|
+
- or by [using the command line](#running-a-task-from-the-command-line),
|
121
|
+
- or by [using Ruby](#running-a-task-from-ruby).
|
120
122
|
3. [Monitor the Task](#monitoring-your-tasks-status)
|
121
|
-
|
122
|
-
|
123
|
+
- either by using the included web UI,
|
124
|
+
- or by manually checking your task’s run’s status in your database.
|
123
125
|
4. Optionally, delete the Task code if you no longer need it.
|
124
126
|
|
125
127
|
### Creating a Task
|
@@ -168,7 +170,8 @@ end
|
|
168
170
|
When processing records from an Active Record Relation, records are fetched in
|
169
171
|
batches internally, and then each record is passed to the `#process` method.
|
170
172
|
Maintenance Tasks will query the database to fetch records in batches of 100 by
|
171
|
-
default, but the batch size can be modified using the `collection_batch_size`
|
173
|
+
default, but the batch size can be modified using the `collection_batch_size`
|
174
|
+
macro:
|
172
175
|
|
173
176
|
```ruby
|
174
177
|
# app/tasks/maintenance/update_posts_task.rb
|
@@ -502,13 +505,44 @@ set of values will be used to populate a dropdown in the user interface. The
|
|
502
505
|
following types are supported:
|
503
506
|
|
504
507
|
* Arrays
|
505
|
-
* Procs and lambdas that optionally accept the Task instance, and return an
|
506
|
-
|
508
|
+
* Procs and lambdas that optionally accept the Task instance, and return an
|
509
|
+
Array.
|
510
|
+
* Callable objects that receive one argument, the Task instance, and return an
|
511
|
+
Array.
|
507
512
|
* Methods that return an Array, called on the Task instance.
|
508
513
|
|
509
514
|
For enumerables that don't match the supported types, a text field will be
|
510
515
|
rendered instead.
|
511
516
|
|
517
|
+
### Masking Task Parameters
|
518
|
+
|
519
|
+
Task attributes can be masked in the UI by adding `mask_attribute` class method
|
520
|
+
in the task class. This will replace the value in the arguments list with
|
521
|
+
`[FILTERED]` in the UI.
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
# app/tasks/maintenance/sensitive_params_task.rb
|
525
|
+
|
526
|
+
module Maintenance
|
527
|
+
class SensitiveParamsTask < MaintenanceTasks::Task
|
528
|
+
attribute :sensitive_content, :string
|
529
|
+
|
530
|
+
mask_attribute :sensitive_content
|
531
|
+
end
|
532
|
+
end
|
533
|
+
```
|
534
|
+
|
535
|
+
If you have any filtered parameters in the global [Rails parameter
|
536
|
+
filter][rails-parameter-filter], they will be automatically taken into account
|
537
|
+
when masking the parameters, which means that you can mask parameters across all
|
538
|
+
tasks by adding them to the global rails parameters filter.
|
539
|
+
|
540
|
+
[rails-parameter-filter]:https://guides.rubyonrails.org/configuring.html#config-filter-parameters
|
541
|
+
|
542
|
+
```ruby
|
543
|
+
Rails.application.config.filter_parameters += %i[token]
|
544
|
+
```
|
545
|
+
|
512
546
|
### Custom cursor columns to improve performance
|
513
547
|
|
514
548
|
The [job-iteration gem][job-iteration], on which this gem depends, adds an
|
@@ -899,10 +933,10 @@ a Task can be in:
|
|
899
933
|
|
900
934
|
The Maintenance Tasks engine uses Rails sessions for flash messages and storing
|
901
935
|
the CSRF token. For the engine to work in an API-only Rails application, you
|
902
|
-
need to add a [session middleware][] and the `ActionDispatch::Flash`
|
903
|
-
|
904
|
-
|
905
|
-
|
936
|
+
need to add a [session middleware][] and the `ActionDispatch::Flash` middleware.
|
937
|
+
The engine also defines a strict [Content Security Policy][], make sure to
|
938
|
+
include `ActionDispatch::ContentSecurityPolicy::Middleware` in your app's
|
939
|
+
middleware stack to ensure the CSP is delivered to the user's browser.
|
906
940
|
|
907
941
|
[session middleware]: https://guides.rubyonrails.org/api_app.html#using-session-middlewares
|
908
942
|
[Content Security Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
@@ -926,8 +960,8 @@ module YourApplication
|
|
926
960
|
end
|
927
961
|
```
|
928
962
|
|
929
|
-
You can read more in the [Using Rails for API-only Applications][rails api]
|
930
|
-
guide.
|
963
|
+
You can read more in the [Using Rails for API-only Applications][rails api]
|
964
|
+
Rails guide.
|
931
965
|
|
932
966
|
[rails api]: https://guides.rubyonrails.org/api_app.html
|
933
967
|
|
@@ -944,9 +978,9 @@ infrastructure or code changes.
|
|
944
978
|
This means a Task can safely be interrupted, re-enqueued and resumed without any
|
945
979
|
intervention at the end of an iteration, after the `process` method returns.
|
946
980
|
|
947
|
-
By default, a running Task will be interrupted after running for more 5
|
948
|
-
This is [configured in the `job-iteration` gem][max-job-runtime] and
|
949
|
-
tweaked in an initializer if necessary.
|
981
|
+
By default, a running Task will be interrupted after running for more than 5
|
982
|
+
minutes. This is [configured in the `job-iteration` gem][max-job-runtime] and
|
983
|
+
can be tweaked in an initializer if necessary.
|
950
984
|
|
951
985
|
[max-job-runtime]: https://github.com/Shopify/job-iteration/blob/-/guides/best-practices.md#max-job-runtime
|
952
986
|
|
@@ -993,45 +1027,45 @@ be placed in a `maintenance_tasks.rb` initializer.
|
|
993
1027
|
Exceptions raised while a Task is performing are rescued and information about
|
994
1028
|
the error is persisted and visible in the UI.
|
995
1029
|
|
996
|
-
Errors are also sent to the `Rails.error.reporter`, which can be configured by
|
997
|
-
application. See the [Error Reporting in Rails
|
1030
|
+
Errors are also sent to the `Rails.error.reporter`, which can be configured by
|
1031
|
+
your application. See the [Error Reporting in Rails
|
1032
|
+
Applications][rails-error-reporting] guide for more details.
|
998
1033
|
|
999
1034
|
Reports to the error reporter will contain the following data:
|
1000
1035
|
|
1001
1036
|
* `error`: The exception that was raised.
|
1002
|
-
* `context`: A hash with additional information about the Task and the
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
raised an exception. If you would like to pass this object to your exception
|
1011
|
-
monitoring service, make sure you **sanitize the object** to avoid leaking
|
1012
|
-
sensitive data and **convert it to a format** that is compatible with your bug
|
1013
|
-
tracker.
|
1014
|
-
* `source`: This will be `maintenance_tasks`
|
1037
|
+
* `context`: A hash with additional information about the Task and the error:
|
1038
|
+
* `task_name`: The name of the Task that errored
|
1039
|
+
* `started_at`: The time the Task started
|
1040
|
+
* `ended_at`: The time the Task errored
|
1041
|
+
* `run_id`: The id of the errored Task run
|
1042
|
+
* `tick_count`: The tick count at the time of the error
|
1043
|
+
* `errored_element`: The element, if any, that was being processed when the
|
1044
|
+
* `source`: This will be `maintenance-tasks`
|
1015
1045
|
|
1016
1046
|
Note that `context` may be empty if the Task produced an error before any
|
1017
|
-
context could be gathered (for example, if deserializing the job to process
|
1018
|
-
|
1047
|
+
context could be gathered (for example, if deserializing the job to process your
|
1048
|
+
Task failed).
|
1019
1049
|
|
1020
1050
|
#### Reporting errors during iteration
|
1021
1051
|
|
1022
|
-
By default, errors raised during task iteration will be raised to the
|
1023
|
-
and iteration will stop. However, you may want to handle some errors
|
1024
|
-
iteration. `MaintenanceTasks::Task.report_on` can be used to rescue
|
1025
|
-
exceptions and report them to the Rails error reporter.
|
1052
|
+
By default, errors raised during task iteration will be raised to the
|
1053
|
+
application and iteration will stop. However, you may want to handle some errors
|
1054
|
+
and continue iteration. `MaintenanceTasks::Task.report_on` can be used to rescue
|
1055
|
+
certain exceptions and report them to the Rails error reporter. Any keyword
|
1056
|
+
arguments are passed to
|
1057
|
+
[ActiveSupport::ErrorReporter#report][as-error-reporter-report]:
|
1058
|
+
|
1059
|
+
[as-error-reporter-report]: https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-report
|
1026
1060
|
|
1027
1061
|
```ruby
|
1028
1062
|
class MyTask < MaintenanceTasks::Task
|
1029
|
-
report_on(MyException)
|
1063
|
+
report_on(MyException, OtherException, severity: :info, context: {task_name: "my_task"})
|
1030
1064
|
end
|
1031
1065
|
```
|
1032
1066
|
|
1033
|
-
`MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can
|
1034
|
-
to implement custom error handling.
|
1067
|
+
`MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can
|
1068
|
+
use to implement custom error handling.
|
1035
1069
|
|
1036
1070
|
```ruby
|
1037
1071
|
class MyTask < MaintenanceTasks::Task
|
@@ -8,16 +8,20 @@ module MaintenanceTasks
|
|
8
8
|
BULMA_CDN = "https://cdn.jsdelivr.net"
|
9
9
|
|
10
10
|
content_security_policy do |policy|
|
11
|
-
policy.
|
11
|
+
policy.style_src_elem(
|
12
12
|
BULMA_CDN,
|
13
|
-
#
|
14
|
-
"'sha256-
|
13
|
+
# <style> tag in app/views/layouts/maintenance_tasks/application.html.erb
|
14
|
+
"'sha256-WHHDQLdkleXnAN5zs0GDXC5ls41CHUaVsJtVpaNx+EM='",
|
15
15
|
)
|
16
|
-
policy.
|
17
|
-
#
|
16
|
+
policy.script_src_elem(
|
17
|
+
# <script> tag in app/views/layouts/maintenance_tasks/application.html.erb
|
18
18
|
"'sha256-NiHKryHWudRC2IteTqmY9v1VkaDUA/5jhgXkMTkgo2w='",
|
19
19
|
)
|
20
|
+
|
21
|
+
policy.require_trusted_types_for # disable because we use new DOMParser().parseFromString
|
20
22
|
policy.frame_ancestors(:self)
|
23
|
+
policy.connect_src(:self)
|
24
|
+
policy.form_action(:self)
|
21
25
|
end
|
22
26
|
|
23
27
|
protect_from_forgery with: :exception
|
@@ -47,7 +47,7 @@ module MaintenanceTasks
|
|
47
47
|
progress_bar = tag.progress(
|
48
48
|
value: progress.value,
|
49
49
|
max: progress.max,
|
50
|
-
class: ["progress"] + STATUS_COLOURS.fetch(run.status),
|
50
|
+
class: ["progress", "mt-4"] + STATUS_COLOURS.fetch(run.status),
|
51
51
|
)
|
52
52
|
progress_text = tag.p(tag.i(progress.text))
|
53
53
|
tag.div(progress_bar + progress_text, class: "block")
|
@@ -60,7 +60,10 @@ module MaintenanceTasks
|
|
60
60
|
# @return [String] the span element containing the status, with the
|
61
61
|
# appropriate tag class attached.
|
62
62
|
def status_tag(status)
|
63
|
-
tag.span(
|
63
|
+
tag.span(
|
64
|
+
status.capitalize,
|
65
|
+
class: ["tag", "has-text-weight-medium", "pr-2", "mr-4"] + STATUS_COLOURS.fetch(status),
|
66
|
+
)
|
64
67
|
end
|
65
68
|
|
66
69
|
# Reports the approximate elapsed time a Run has been processed so far based
|
@@ -101,7 +104,7 @@ module MaintenanceTasks
|
|
101
104
|
)
|
102
105
|
end
|
103
106
|
|
104
|
-
# Resolves values covered by the inclusion validator for a
|
107
|
+
# Resolves values covered by the inclusion validator for a Task attribute.
|
105
108
|
# Supported option types:
|
106
109
|
# - Arrays
|
107
110
|
# - Procs and lambdas that optionally accept the Task instance, and return an Array.
|
@@ -112,7 +115,7 @@ module MaintenanceTasks
|
|
112
115
|
#
|
113
116
|
# Returned values are used to populate a dropdown list of options.
|
114
117
|
#
|
115
|
-
# @param
|
118
|
+
# @param task [Task] The Task for which the value needs to be resolved.
|
116
119
|
# @param parameter_name [String] The parameter name.
|
117
120
|
#
|
118
121
|
# @return [Array] value of the resolved inclusion option.
|
@@ -149,24 +152,27 @@ module MaintenanceTasks
|
|
149
152
|
# If the parameter has a `validates_inclusion_of` validator, return a dropdown list of options instead.
|
150
153
|
def parameter_field(form_builder, parameter_name)
|
151
154
|
inclusion_values = resolve_inclusion_value(form_builder.object, parameter_name)
|
152
|
-
|
155
|
+
if inclusion_values
|
156
|
+
return tag.div(form_builder.select(parameter_name, inclusion_values, prompt: "Select a value"), class: "select")
|
157
|
+
end
|
153
158
|
|
154
159
|
case form_builder.object.class.attribute_types[parameter_name]
|
155
160
|
when ActiveModel::Type::Integer
|
156
|
-
form_builder.number_field(parameter_name)
|
161
|
+
form_builder.number_field(parameter_name, class: "input")
|
157
162
|
when ActiveModel::Type::Decimal, ActiveModel::Type::Float
|
158
|
-
form_builder.number_field(parameter_name, { step: "any" })
|
163
|
+
form_builder.number_field(parameter_name, { step: "any", class: "input" })
|
159
164
|
when ActiveModel::Type::DateTime
|
160
|
-
form_builder.datetime_field(parameter_name) + datetime_field_help_text
|
165
|
+
form_builder.datetime_field(parameter_name, class: "input") + datetime_field_help_text
|
161
166
|
when ActiveModel::Type::Date
|
162
|
-
form_builder.date_field(parameter_name)
|
167
|
+
form_builder.date_field(parameter_name, class: "input")
|
163
168
|
when ActiveModel::Type::Time
|
164
|
-
form_builder.time_field(parameter_name)
|
169
|
+
form_builder.time_field(parameter_name, class: "input")
|
165
170
|
when ActiveModel::Type::Boolean
|
166
|
-
form_builder.check_box(parameter_name)
|
171
|
+
form_builder.check_box(parameter_name, class: "checkbox")
|
167
172
|
else
|
168
173
|
form_builder.text_area(parameter_name, class: "textarea")
|
169
174
|
end
|
175
|
+
.then { |input| tag.div(input, class: "control") }
|
170
176
|
end
|
171
177
|
|
172
178
|
# Return helper text for the datetime-local form field.
|
@@ -182,5 +188,16 @@ module MaintenanceTasks
|
|
182
188
|
class: "content is-small",
|
183
189
|
)
|
184
190
|
end
|
191
|
+
|
192
|
+
# Checks if an attribute is required for a given Task.
|
193
|
+
#
|
194
|
+
# @param task [MaintenanceTasks::TaskDataShow] The TaskDataShow instance.
|
195
|
+
# @param parameter_name [Symbol] The name of the attribute to check.
|
196
|
+
# @return [Boolean] Whether the attribute is required.
|
197
|
+
def attribute_required?(task, parameter_name)
|
198
|
+
task.class.validators_on(parameter_name).any? do |validator|
|
199
|
+
validator.kind == :presence
|
200
|
+
end
|
201
|
+
end
|
185
202
|
end
|
186
203
|
end
|
@@ -422,12 +422,23 @@ module MaintenanceTasks
|
|
422
422
|
if task.attribute_names.any? && arguments.present?
|
423
423
|
task.assign_attributes(arguments)
|
424
424
|
end
|
425
|
+
|
426
|
+
task.metadata = metadata
|
425
427
|
task
|
426
428
|
rescue ActiveModel::UnknownAttributeError
|
427
429
|
task
|
428
430
|
end
|
429
431
|
end
|
430
432
|
|
433
|
+
# Returns all the run arguments with sensitive information masked.
|
434
|
+
#
|
435
|
+
# @return [Hash] The masked arguments.
|
436
|
+
def masked_arguments
|
437
|
+
return unless arguments.present?
|
438
|
+
|
439
|
+
argument_filter.filter(arguments)
|
440
|
+
end
|
441
|
+
|
431
442
|
private
|
432
443
|
|
433
444
|
def instrument_status_change
|
@@ -482,5 +493,11 @@ module MaintenanceTasks
|
|
482
493
|
|
483
494
|
value&.first(limit)
|
484
495
|
end
|
496
|
+
|
497
|
+
def argument_filter
|
498
|
+
@argument_filter ||= ActiveSupport::ParameterFilter.new(
|
499
|
+
Rails.application.config.filter_parameters + task.masked_arguments,
|
500
|
+
)
|
501
|
+
end
|
485
502
|
end
|
486
503
|
end
|
@@ -28,8 +28,15 @@ module MaintenanceTasks
|
|
28
28
|
# @api private
|
29
29
|
class_attribute :collection_builder_strategy, default: NullCollectionBuilder.new
|
30
30
|
|
31
|
+
# The sensitive attributes that will be filtered when fetching a run.
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
class_attribute :masked_arguments, default: []
|
35
|
+
|
31
36
|
define_callbacks :start, :complete, :error, :cancel, :pause, :interrupt
|
32
37
|
|
38
|
+
attr_accessor :metadata
|
39
|
+
|
33
40
|
class << self
|
34
41
|
# Finds a Task with the given name.
|
35
42
|
#
|
@@ -153,6 +160,13 @@ module MaintenanceTasks
|
|
153
160
|
self.active_record_enumerator_batch_size = size
|
154
161
|
end
|
155
162
|
|
163
|
+
# Adds attribute names to sensitive arguments list.
|
164
|
+
#
|
165
|
+
# @param attributes [Array<Symbol>] the attribute names to filter.
|
166
|
+
def mask_attribute(*attributes)
|
167
|
+
self.masked_arguments += attributes
|
168
|
+
end
|
169
|
+
|
156
170
|
# Initialize a callback to run after the task starts.
|
157
171
|
#
|
158
172
|
# @param filter_list apply filters to the callback
|
@@ -205,12 +219,14 @@ module MaintenanceTasks
|
|
205
219
|
# continue iteration.
|
206
220
|
#
|
207
221
|
# @param exceptions list of exceptions to rescue and report
|
208
|
-
|
222
|
+
# @param report_options [Hash] optionally, supply additional options for `Rails.error.report`.
|
223
|
+
# By default: <code>{ source: "maintenance_tasks" }</code> or (Rails <v7.1) <code>{ handled: true }</code>.
|
224
|
+
def report_on(*exceptions, **report_options)
|
209
225
|
rescue_from(*exceptions) do |exception|
|
210
226
|
if Rails.gem_version >= Gem::Version.new("7.1")
|
211
|
-
Rails.error.report(exception, source: "maintenance_tasks")
|
227
|
+
Rails.error.report(exception, source: "maintenance_tasks", **report_options)
|
212
228
|
else
|
213
|
-
Rails.error.report(exception, handled: true)
|
229
|
+
Rails.error.report(exception, handled: true, **report_options)
|
214
230
|
end
|
215
231
|
end
|
216
232
|
end
|
@@ -34,7 +34,8 @@ module MaintenanceTasks
|
|
34
34
|
end
|
35
35
|
|
36
36
|
completed_runs = Run.completed.where(task_name: task_names)
|
37
|
-
last_runs = Run.with_attached_csv
|
37
|
+
last_runs = Run.with_attached_csv
|
38
|
+
.where(created_at: completed_runs.select("MAX(created_at) as created_at").group(:task_name))
|
38
39
|
task_names.map do |task_name|
|
39
40
|
last_run = last_runs.find { |run| run.task_name == task_name }
|
40
41
|
tasks << TaskDataIndex.new(task_name, last_run)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<nav class="navbar is-
|
1
|
+
<nav class="navbar is-light" role="navigation" aria-label="main navigation">
|
2
2
|
<div class="navbar-brand">
|
3
3
|
<%= link_to 'Maintenance Tasks', root_path, class: 'navbar-item is-size-4 has-text-weight-semibold has-text-danger' %>
|
4
4
|
</div>
|
@@ -15,10 +15,10 @@
|
|
15
15
|
<%= csrf_meta_tags %>
|
16
16
|
|
17
17
|
<%=
|
18
|
-
stylesheet_link_tag(URI.join(controller.class::BULMA_CDN,
|
18
|
+
stylesheet_link_tag(URI.join(controller.class::BULMA_CDN, "npm/bulma@1.0.3/css/versions/bulma-no-dark-mode.min.css"),
|
19
19
|
media: :all,
|
20
|
-
integrity:
|
21
|
-
crossorigin:
|
20
|
+
integrity: "sha256-HCNMQcqH/4MnGR0EYg2S3/BXYMM1z9lrFV10ANRd79o",
|
21
|
+
crossorigin: "anonymous") unless request.xhr?
|
22
22
|
%>
|
23
23
|
|
24
24
|
<style>
|
@@ -29,6 +29,36 @@
|
|
29
29
|
.ruby-ivar, .ruby-cvar, .ruby-gvar, .ruby-int, .ruby-imaginary, .ruby-float, .ruby-rational { color: #005cc5; }
|
30
30
|
.ruby-kw { color: #d73a49; }
|
31
31
|
.ruby-label, .ruby-tstring-beg, .ruby-tstring-content, .ruby-tstring-end { color: #032f62; }
|
32
|
+
|
33
|
+
.select, select { width: 100%; }
|
34
|
+
summary { cursor: pointer; }
|
35
|
+
input[type="datetime-local"], input[type="date"], input[type="time"] {
|
36
|
+
width: fit-content;
|
37
|
+
}
|
38
|
+
details > summary {
|
39
|
+
list-style: none;
|
40
|
+
}
|
41
|
+
summary::-webkit-details-marker {
|
42
|
+
display: none
|
43
|
+
}
|
44
|
+
summary::before {
|
45
|
+
content: '► ';
|
46
|
+
position:absolute;
|
47
|
+
font-size: 16px
|
48
|
+
}
|
49
|
+
details[open] summary:before {
|
50
|
+
content: "▼ ";
|
51
|
+
}
|
52
|
+
|
53
|
+
.box {
|
54
|
+
box-shadow: 0 4px 6px -1px #0000001a,
|
55
|
+
0 2px 4px -2px #0000001a;
|
56
|
+
}
|
57
|
+
.label.is-required:after {
|
58
|
+
content: " (required)";
|
59
|
+
color: #ff6685;
|
60
|
+
font-size: 12px;
|
61
|
+
}
|
32
62
|
</style>
|
33
63
|
|
34
64
|
<script>
|
@@ -1,9 +1,15 @@
|
|
1
|
-
<
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
<details class="box" open id="run_<%= run.id %>">
|
2
|
+
<summary class="is-flex is-justify-content-space-between is-align-items-center">
|
3
|
+
<div class="is-flex is-align-items-center">
|
4
|
+
<h5 class="title is-5 has-text-weight-medium pl-5 pr-5 mb-0">
|
5
|
+
<%= time_tag run.created_at, title: run.created_at.utc %>
|
6
|
+
</h5>
|
7
|
+
<%= status_tag run.status %>
|
8
|
+
</div>
|
9
|
+
<div>
|
10
|
+
<a href="#run_<%= run.id %>" class="is-pulled-right" title="Run ID">#<%= run.id %></a>
|
11
|
+
</div>
|
12
|
+
</summary>
|
7
13
|
|
8
14
|
<%= progress run %>
|
9
15
|
|
@@ -17,7 +23,7 @@
|
|
17
23
|
|
18
24
|
<%= render "maintenance_tasks/runs/csv", run: run %>
|
19
25
|
<%= tag.hr if run.csv_file.present? && run.arguments.present? %>
|
20
|
-
<%= render "maintenance_tasks/runs/arguments", arguments: run.
|
26
|
+
<%= render "maintenance_tasks/runs/arguments", arguments: run.masked_arguments %>
|
21
27
|
<%= tag.hr if run.csv_file.present? || run.arguments.present? && run.metadata.present? %>
|
22
28
|
<%= render "maintenance_tasks/runs/metadata", metadata: run.metadata %>
|
23
29
|
|
@@ -42,4 +48,4 @@
|
|
42
48
|
<%= button_to 'Cancel', cancel_task_run_path(@task, run), class: 'button is-danger' %>
|
43
49
|
<% end%>
|
44
50
|
</div>
|
45
|
-
</
|
51
|
+
</details>
|
@@ -1,24 +1,21 @@
|
|
1
1
|
<% if serializable.present? %>
|
2
2
|
<% case serializable %>
|
3
3
|
<% when Hash %>
|
4
|
-
<div class="
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
<% end %>
|
20
|
-
</tbody>
|
21
|
-
</table>
|
4
|
+
<div class="arguments-container grid is-col-min-15">
|
5
|
+
<% serializable.transform_values(&:to_s).each do |key, value| %>
|
6
|
+
<div class="cell mb-4">
|
7
|
+
<div class="is-family-monospace mb-2"><%= key %></div>
|
8
|
+
<div class="is-flex justify-content">
|
9
|
+
<% if !value.empty? %>
|
10
|
+
<% if value.include?("\n") %>
|
11
|
+
<pre><%= value %></pre>
|
12
|
+
<% else %>
|
13
|
+
<code><%= value %></code>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
22
19
|
</div>
|
23
20
|
<% else %>
|
24
21
|
<code><%= serializable.inspect %></code>
|
@@ -1,11 +1,11 @@
|
|
1
|
-
<div class="box">
|
2
|
-
<h3 class="title is-
|
1
|
+
<div class="cell box">
|
2
|
+
<h3 class="title is-5 has-text-weight-medium">
|
3
3
|
<%= link_to task, task_path(task) %>
|
4
4
|
<%= status_tag(task.status) %>
|
5
5
|
</h3>
|
6
6
|
|
7
7
|
<% if (run = task.related_run) %>
|
8
|
-
<h5 class="title is-5">
|
8
|
+
<h5 class="title is-5 has-text-weight-medium">
|
9
9
|
<%= time_tag run.created_at, title: run.created_at.utc %>
|
10
10
|
</h5>
|
11
11
|
|
@@ -21,7 +21,7 @@
|
|
21
21
|
|
22
22
|
<%= render "maintenance_tasks/runs/csv", run: run %>
|
23
23
|
<%= tag.hr if run.csv_file.present? && run.arguments.present? %>
|
24
|
-
<%= render "maintenance_tasks/runs/arguments", arguments: run.
|
24
|
+
<%= render "maintenance_tasks/runs/arguments", arguments: run.masked_arguments %>
|
25
25
|
<%= tag.hr if run.csv_file.present? || run.arguments.present? && run.metadata.present? %>
|
26
26
|
<%= render "maintenance_tasks/runs/metadata", metadata: run.metadata %>
|
27
27
|
<% end %>
|
@@ -9,15 +9,17 @@
|
|
9
9
|
</div>
|
10
10
|
<% else %>
|
11
11
|
<% if active_tasks = @available_tasks[:active] %>
|
12
|
-
<h3 class="title is-
|
12
|
+
<h3 class="title is-4 has-text-weight-bold">Active Tasks</h3>
|
13
13
|
<%= render partial: 'task', collection: active_tasks %>
|
14
14
|
<% end %>
|
15
15
|
<% if new_tasks = @available_tasks[:new] %>
|
16
|
-
<h3 class="title is-
|
17
|
-
|
16
|
+
<h3 class="title is-4 has-text-weight-bold">New Tasks</h3>
|
17
|
+
<div class="grid is-col-min-20">
|
18
|
+
<%= render partial: 'task', collection: new_tasks %>
|
19
|
+
</div>
|
18
20
|
<% end %>
|
19
21
|
<% if completed_tasks = @available_tasks[:completed] %>
|
20
|
-
<h3 class="title is-
|
22
|
+
<h3 class="title is-4 has-text-weight-bold">Completed Tasks</h3>
|
21
23
|
<%= render partial: 'task', collection: completed_tasks %>
|
22
24
|
<% end %>
|
23
25
|
<% end %>
|
@@ -1,27 +1,25 @@
|
|
1
1
|
<% content_for :page_title, @task %>
|
2
2
|
|
3
|
-
<h1 class="title is-
|
3
|
+
<h1 class="title is-3 has-text-weight-bold">
|
4
4
|
<%= @task %>
|
5
5
|
</h1>
|
6
6
|
|
7
|
-
<div class="
|
7
|
+
<div class="container">
|
8
8
|
<%= form_with url: task_runs_path(@task), method: :post do |form| %>
|
9
9
|
<% if @task.csv_task? %>
|
10
|
-
<div class="
|
11
|
-
<%= form.label :csv_file %>
|
10
|
+
<div class="container mb-4">
|
11
|
+
<%= form.label :csv_file, class: "label" %>
|
12
12
|
<%= form.file_field :csv_file, accept: "text/csv" %>
|
13
13
|
</div>
|
14
14
|
<% end %>
|
15
15
|
<% parameter_names = @task.parameter_names %>
|
16
16
|
<% if parameter_names.any? %>
|
17
|
-
<div class="
|
17
|
+
<div class="grid is-col-min-15">
|
18
18
|
<%= fields_for :task, @task.new do |ff| %>
|
19
19
|
<% parameter_names.each do |parameter_name| %>
|
20
|
-
<div class="
|
21
|
-
<%= ff.label parameter_name, parameter_name, class: "label is-family-monospace" %>
|
22
|
-
|
23
|
-
<%= parameter_field(ff, parameter_name) %>
|
24
|
-
</div>
|
20
|
+
<div class="cell">
|
21
|
+
<%= ff.label parameter_name, parameter_name, class: ["label", "is-family-monospace", { "is-required": attribute_required?(ff.object, parameter_name) }] %>
|
22
|
+
<%= parameter_field(ff, parameter_name) %>
|
25
23
|
</div>
|
26
24
|
<% end %>
|
27
25
|
<% end %>
|
@@ -29,13 +27,19 @@
|
|
29
27
|
<% end %>
|
30
28
|
<%= render "maintenance_tasks/tasks/custom", form: form %>
|
31
29
|
<div class="block">
|
32
|
-
<%= form.submit 'Run', class: "button is-success", disabled: @task.deleted? %>
|
30
|
+
<%= form.submit 'Run', class: "button is-success is-rounded mb-4 has-text-white-ter", disabled: @task.deleted? %>
|
33
31
|
</div>
|
34
32
|
<% end %>
|
35
33
|
</div>
|
36
34
|
|
37
35
|
<% if (code = @task.code) %>
|
36
|
+
|
37
|
+
<details class="box">
|
38
|
+
<summary class="is-size-5 is-flex is-align-items-center">
|
39
|
+
<h5 class="pl-5">Source code</h5>
|
40
|
+
</summary>
|
38
41
|
<pre><code><%= highlight_code(code) %></code></pre>
|
42
|
+
</details>
|
39
43
|
<% end %>
|
40
44
|
|
41
45
|
<%= tag.div(data: { refresh: @task.refresh? || "" }) do %>
|
@@ -50,7 +54,7 @@
|
|
50
54
|
<% if @task.runs_page.records.present? %>
|
51
55
|
<hr/>
|
52
56
|
|
53
|
-
<h4 class="title is-
|
57
|
+
<h4 class="title is-5 has-text-weight-bold">Previous Runs</h4>
|
54
58
|
|
55
59
|
<%= render partial: "maintenance_tasks/runs/run", collection: @task.runs_page.records %>
|
56
60
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class CreateMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
|
4
4
|
def change
|
5
|
-
create_table(:maintenance_tasks_runs) do |t|
|
5
|
+
create_table(:maintenance_tasks_runs, id: primary_key_type) do |t|
|
6
6
|
t.string(:task_name, null: false)
|
7
7
|
t.datetime(:started_at)
|
8
8
|
t.datetime(:ended_at)
|
@@ -20,4 +20,12 @@ class CreateMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
|
|
20
20
|
t.index([:task_name, :created_at], order: { created_at: :desc })
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def primary_key_type
|
27
|
+
config = Rails.configuration.generators
|
28
|
+
setting = config.options[config.orm][:primary_key_type]
|
29
|
+
setting || :primary_key
|
30
|
+
end
|
23
31
|
end
|
data/lib/maintenance_tasks.rb
CHANGED
@@ -87,7 +87,7 @@ module MaintenanceTasks
|
|
87
87
|
|
88
88
|
class << self
|
89
89
|
DEPRECATION_MESSAGE = "MaintenanceTasks.error_handler is deprecated and will be removed in the 3.0 release. " \
|
90
|
-
"Instead, reports will be sent to the Rails error reporter. Do not set a handler and subscribe" \
|
90
|
+
"Instead, reports will be sent to the Rails error reporter. Do not set a handler and subscribe " \
|
91
91
|
"to the error reporter instead."
|
92
92
|
private_constant :DEPRECATION_MESSAGE
|
93
93
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maintenance_tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Engineering
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: actionpack
|
@@ -182,7 +182,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
|
|
182
182
|
licenses:
|
183
183
|
- MIT
|
184
184
|
metadata:
|
185
|
-
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.
|
185
|
+
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.12.0
|
186
186
|
allowed_push_host: https://rubygems.org
|
187
187
|
rdoc_options: []
|
188
188
|
require_paths:
|
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
198
|
- !ruby/object:Gem::Version
|
199
199
|
version: '0'
|
200
200
|
requirements: []
|
201
|
-
rubygems_version: 3.6.
|
201
|
+
rubygems_version: 3.6.8
|
202
202
|
specification_version: 4
|
203
203
|
summary: A Rails engine for queuing and managing maintenance tasks
|
204
204
|
test_files: []
|