maintenance_tasks 2.0.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +67 -40
- data/app/controllers/maintenance_tasks/application_controller.rb +1 -1
- data/app/controllers/maintenance_tasks/tasks_controller.rb +7 -3
- data/app/helpers/maintenance_tasks/tasks_helper.rb +3 -2
- data/app/models/maintenance_tasks/run.rb +10 -5
- data/app/models/maintenance_tasks/task.rb +7 -1
- data/app/models/maintenance_tasks/task_data_show.rb +0 -26
- data/app/views/layouts/maintenance_tasks/application.html.erb +11 -9
- 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/20211210152329_add_lock_version_to_maintenance_tasks_runs.rb +7 -2
- data/db/migrate/20220713131925_add_index_on_task_name_and_status_to_runs.rb +11 -5
- data/lib/generators/maintenance_tasks/task_generator.rb +6 -2
- 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: c395c28308277378c5ac627845abc512bba455e11e085558aa4ef106f73b1130
|
4
|
+
data.tar.gz: 7e44c8b5c484aa44008e2ebd3dd35458f84dec2be2741c20cc1ae20871c10f48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 825418a040b4b15f0a98c5e28b06c553e240ec7b0ba10b2adec7b28180203d3e7094cb4e4b2933076d7564050b42f33504ede5b830e1819274393cf9d574f058
|
7
|
+
data.tar.gz: d2d7aa0c97a37851d8540edea5a4e890b1eeb9fbfc740c157070558fbc51e60ef9d3ff882b5ab21cd9b80481f7b2692d9a3f8fbaa03bbdcd4256f1b51aa669ce
|
data/README.md
CHANGED
@@ -33,12 +33,13 @@ take a look at the [Active Job documentation][active-job-docs].
|
|
33
33
|
[async-adapter]: https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html
|
34
34
|
[active-job-docs]: https://guides.rubyonrails.org/active_job_basics.html#setting-the-backend
|
35
35
|
|
36
|
-
|
37
36
|
### Autoloading
|
38
37
|
|
39
38
|
The Maintenance Tasks framework does not support autoloading in `:classic` mode.
|
40
|
-
Please ensure your application is using
|
41
|
-
|
39
|
+
Please ensure your application is using
|
40
|
+
[Zeitwerk](https://github.com/fxn/zeitwerk) to load your code. For more
|
41
|
+
information, please consult the [Rails guides on autoloading and reloading
|
42
|
+
constants](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html).
|
42
43
|
|
43
44
|
## Usage
|
44
45
|
|
@@ -51,7 +52,7 @@ The typical Maintenance Tasks workflow is as follows:
|
|
51
52
|
- or by [using Ruby](#running-a-task-from-ruby).
|
52
53
|
3. [Monitor the Task](#monitoring-your-tasks-status)
|
53
54
|
- either by using the included web UI,
|
54
|
-
- or by manually checking your task
|
55
|
+
- or by manually checking your task’s run’s status in your database.
|
55
56
|
4. Optionally, delete the Task code if you no longer need it.
|
56
57
|
|
57
58
|
### Creating a Task
|
@@ -70,11 +71,12 @@ The generated task is a subclass of `MaintenanceTasks::Task` that implements:
|
|
70
71
|
over.
|
71
72
|
* `process`: do the work of your maintenance task on a single record
|
72
73
|
|
73
|
-
Optionally, tasks can also implement a custom `#count` method, defining the
|
74
|
-
of elements that will be iterated over. Your task
|
75
|
-
automatically based on the collection size, but this value may be
|
76
|
-
using the `#count` method (this might be done, for
|
77
|
-
be produced to determine the size of your
|
74
|
+
Optionally, tasks can also implement a custom `#count` method, defining the
|
75
|
+
number of elements that will be iterated over. Your task’s `tick_total` will be
|
76
|
+
calculated automatically based on the collection size, but this value may be
|
77
|
+
overridden if desired using the `#count` method (this might be done, for
|
78
|
+
example, to avoid the query that would be produced to determine the size of your
|
79
|
+
collection).
|
78
80
|
|
79
81
|
Example:
|
80
82
|
|
@@ -98,10 +100,12 @@ end
|
|
98
100
|
|
99
101
|
You can also write a Task that iterates on a CSV file. Note that writing CSV
|
100
102
|
Tasks **requires Active Storage to be configured**. Ensure that the dependency
|
101
|
-
is specified in your application
|
102
|
-
|
103
|
+
is specified in your application’s Gemfile, and that you’ve followed the [setup
|
104
|
+
instructions][storage-setup]. See also [Customizing which Active Storage service
|
105
|
+
to use][storage-customizing].
|
103
106
|
|
104
|
-
[setup]: https://edgeguides.rubyonrails.org/active_storage_overview.html#setup
|
107
|
+
[storage-setup]: https://edgeguides.rubyonrails.org/active_storage_overview.html#setup
|
108
|
+
[storage-customizing]: #customizing-which-active-storage-service-to-use
|
105
109
|
|
106
110
|
Generate a CSV Task by running:
|
107
111
|
|
@@ -134,12 +138,12 @@ My Title,Hello World!
|
|
134
138
|
```
|
135
139
|
|
136
140
|
The files uploaded to your Active Storage service provider will be renamed
|
137
|
-
to include an
|
141
|
+
to include an ISO 8601 timestamp and the Task name in snake case format.
|
138
142
|
The CSV is expected to have a trailing newline at the end of the file.
|
139
143
|
|
140
144
|
#### Batch CSV Tasks
|
141
145
|
|
142
|
-
Tasks can process CSVs in batches. Add the `in_batches` option to your task
|
146
|
+
Tasks can process CSVs in batches. Add the `in_batches` option to your task’s
|
143
147
|
`csv_collection` macro:
|
144
148
|
|
145
149
|
```ruby
|
@@ -156,12 +160,12 @@ module Maintenance
|
|
156
160
|
end
|
157
161
|
```
|
158
162
|
|
159
|
-
As with a regular CSV task, ensure you
|
163
|
+
As with a regular CSV task, ensure you’ve implemented the following method:
|
160
164
|
|
161
165
|
* `process`: do the work of your Task on a batch (array of `CSV::Row` objects).
|
162
166
|
|
163
167
|
Note that `#count` is calculated automatically based on the number of batches in
|
164
|
-
your collection, and your Task
|
168
|
+
your collection, and your Task’s progress will be displayed in terms of batches
|
165
169
|
(not the total number of rows in your CSV).
|
166
170
|
|
167
171
|
### Processing Batch Collections
|
@@ -188,13 +192,13 @@ module Maintenance
|
|
188
192
|
end
|
189
193
|
```
|
190
194
|
|
191
|
-
Ensure that you
|
195
|
+
Ensure that you’ve implemented the following methods:
|
192
196
|
|
193
197
|
* `collection`: return an `ActiveRecord::Batches::BatchEnumerator`.
|
194
198
|
* `process`: do the work of your Task on a batch (`ActiveRecord::Relation`).
|
195
199
|
|
196
200
|
Note that `#count` is calculated automatically based on the number of batches in
|
197
|
-
your collection, and your Task
|
201
|
+
your collection, and your Task’s progress will be displayed in terms of batches
|
198
202
|
(not the number of records in the relation).
|
199
203
|
|
200
204
|
**Important!** Batches should only be used if `#process` is performing a batch
|
@@ -206,7 +210,7 @@ primary keys of the records of the batch first, and then perform an additional
|
|
206
210
|
query to load the records when calling `each` (or any `Enumerable` method)
|
207
211
|
inside `#process`.
|
208
212
|
|
209
|
-
### Tasks that don
|
213
|
+
### Tasks that don’t need a Collection
|
210
214
|
|
211
215
|
Sometimes, you might want to run a Task that performs a single operation, such
|
212
216
|
as enqueuing another background job or hitting an external API. The gem supports
|
@@ -264,7 +268,7 @@ module Maintenance
|
|
264
268
|
end
|
265
269
|
```
|
266
270
|
|
267
|
-
Note that it
|
271
|
+
Note that it’s up to you to define a throttling condition that makes sense for
|
268
272
|
your app. Shopify implements `DatabaseStatus.healthy?` to check various MySQL
|
269
273
|
metrics such as replication lag, DB threads, whether DB writes are available,
|
270
274
|
etc.
|
@@ -273,7 +277,7 @@ Tasks can define multiple throttle conditions. Throttle conditions are inherited
|
|
273
277
|
by descendants, and new conditions will be appended without impacting existing
|
274
278
|
conditions.
|
275
279
|
|
276
|
-
The backoff can also be specified as a
|
280
|
+
The backoff can also be specified as a Proc:
|
277
281
|
|
278
282
|
```ruby
|
279
283
|
# app/tasks/maintenance/update_posts_throttled_task.rb
|
@@ -287,11 +291,12 @@ module Maintenance
|
|
287
291
|
end
|
288
292
|
end
|
289
293
|
```
|
294
|
+
|
290
295
|
### Custom Task Parameters
|
291
296
|
|
292
297
|
Tasks may need additional information, supplied via parameters, to run.
|
293
298
|
Parameters can be defined as Active Model Attributes in a Task, and then become
|
294
|
-
accessible to any of Task
|
299
|
+
accessible to any of Task’s methods: `#collection`, `#count`, or `#process`.
|
295
300
|
|
296
301
|
```ruby
|
297
302
|
# app/tasks/maintenance/update_posts_via_params_task.rb
|
@@ -315,7 +320,7 @@ end
|
|
315
320
|
Tasks can leverage Active Model Validations when defining parameters. Arguments
|
316
321
|
supplied to a Task accepting parameters will be validated before the Task starts
|
317
322
|
to run. Since arguments are specified in the user interface via text area
|
318
|
-
inputs, it
|
323
|
+
inputs, it’s important to check that they conform to the format your Task
|
319
324
|
expects, and to sanitize any inputs if necessary.
|
320
325
|
|
321
326
|
### Using Task Callbacks
|
@@ -348,7 +353,7 @@ end
|
|
348
353
|
Note: The `after_error` callback is guaranteed to complete,
|
349
354
|
so any exceptions raised in your callback code are ignored.
|
350
355
|
If your `after_error` callback code can raise an exception,
|
351
|
-
you
|
356
|
+
you’ll need to rescue it and handle it appropriately
|
352
357
|
within the callback.
|
353
358
|
|
354
359
|
```ruby
|
@@ -397,7 +402,7 @@ depend on the queue adapter but in general, you should follow these rules:
|
|
397
402
|
safely interrupted and resumed.
|
398
403
|
* Idempotency of `Task#process`: it should be safe to run `process` multiple
|
399
404
|
times for the same element of the collection. Read more in [this Sidekiq best
|
400
|
-
practice][sidekiq-idempotent]. It
|
405
|
+
practice][sidekiq-idempotent]. It’s important if the Task errors and you run
|
401
406
|
it again, because the same element that errored the Task may well be processed
|
402
407
|
again. It especially matters in the situation described above, when the
|
403
408
|
iteration duration exceeds the timeout: if the job is re-enqueued, multiple
|
@@ -422,7 +427,7 @@ callbacks](#using-task-callbacks) to persist or log a report for example.
|
|
422
427
|
### Writing tests for a Task
|
423
428
|
|
424
429
|
The task generator will also create a test file for your task in the folder
|
425
|
-
`test/tasks/maintenance/`. At a minimum, it
|
430
|
+
`test/tasks/maintenance/`. At a minimum, it’s recommended that the `#process`
|
426
431
|
method in your task be tested. You may also want to test the `#collection` and
|
427
432
|
`#count` methods for your task if they are sufficiently complex.
|
428
433
|
|
@@ -477,7 +482,7 @@ end
|
|
477
482
|
|
478
483
|
### Writing tests for a Task with parameters
|
479
484
|
|
480
|
-
Tests for tasks with parameters need to
|
485
|
+
Tests for tasks with parameters need to instantiate the task class in order to
|
481
486
|
assign attributes. Once the task instance is setup, you may test `#process`
|
482
487
|
normally.
|
483
488
|
|
@@ -525,7 +530,8 @@ bundle exec maintenance_tasks perform Maintenance::ImportPostsTask --csv "path/t
|
|
525
530
|
The `--csv` option also works with CSV content coming from the standard input:
|
526
531
|
|
527
532
|
```sh-session
|
528
|
-
curl "some/remote/csv" |
|
533
|
+
curl "some/remote/csv" |
|
534
|
+
bundle exec maintenance_tasks perform Maintenance::ImportPostsTask --csv
|
529
535
|
```
|
530
536
|
|
531
537
|
To run a Task that takes arguments from the command line, use the `--arguments`
|
@@ -564,7 +570,7 @@ MaintenanceTasks::Runner.run(
|
|
564
570
|
)
|
565
571
|
```
|
566
572
|
|
567
|
-
### Monitoring your Task
|
573
|
+
### Monitoring your Task’s status
|
568
574
|
|
569
575
|
The web UI will provide updates on the status of your Task. Here are the states
|
570
576
|
a Task can be in:
|
@@ -620,11 +626,11 @@ When Sidekiq is stopping, it will give workers 25 seconds to finish before
|
|
620
626
|
forcefully terminating them (this is the default but can be configured with the
|
621
627
|
`--timeout` option). Before the worker threads are terminated, Sidekiq will try
|
622
628
|
to re-enqueue the job so your Task will be resumed. However, the position in
|
623
|
-
the collection won
|
629
|
+
the collection won’t be persisted so at least one iteration may run again.
|
624
630
|
|
625
631
|
#### Help! My Task is stuck
|
626
632
|
|
627
|
-
Finally, if the queue adapter configured for your application doesn
|
633
|
+
Finally, if the queue adapter configured for your application doesn’t have this
|
628
634
|
property, or if Sidekiq crashes, is forcefully terminated, or is unable to
|
629
635
|
re-enqueue the jobs that were in progress, the Task may be in a seemingly stuck
|
630
636
|
situation where it appears to be running but is not. In that situation, pausing
|
@@ -690,6 +696,26 @@ MaintenanceTasks.tasks_module = "TaskModule"
|
|
690
696
|
|
691
697
|
If no value is specified, it will default to `Maintenance`.
|
692
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
|
+
|
693
719
|
#### Customizing the underlying job class
|
694
720
|
|
695
721
|
`MaintenanceTasks.job` can be configured to define a Job class for your tasks to
|
@@ -699,7 +725,7 @@ maintenance tasks in your application.
|
|
699
725
|
```ruby
|
700
726
|
# config/initializers/maintenance_tasks.rb
|
701
727
|
|
702
|
-
MaintenanceTasks.job =
|
728
|
+
MaintenanceTasks.job = "CustomTaskJob"
|
703
729
|
|
704
730
|
# app/jobs/custom_task_job.rb
|
705
731
|
|
@@ -730,9 +756,9 @@ If no value is specified, it will default to 1 second.
|
|
730
756
|
#### Customizing which Active Storage service to use
|
731
757
|
|
732
758
|
The Active Storage framework in Rails 6.1 and up supports multiple storage
|
733
|
-
services
|
734
|
-
`MaintenanceTasks.active_storage_service` can be configured with the service
|
735
|
-
key, as specified in your application
|
759
|
+
services. To specify which service to use,
|
760
|
+
`MaintenanceTasks.active_storage_service` can be configured with the service’s
|
761
|
+
key, as specified in your application’s `config/storage.yml`:
|
736
762
|
|
737
763
|
```yaml
|
738
764
|
# config/storage.yml
|
@@ -757,7 +783,8 @@ MaintenanceTasks.active_storage_service = :internal
|
|
757
783
|
```
|
758
784
|
|
759
785
|
There is no need to configure this option if your application uses only one
|
760
|
-
storage service
|
786
|
+
storage service. `Rails.configuration.active_storage.service` is used by
|
787
|
+
default.
|
761
788
|
|
762
789
|
#### Customizing the backtrace cleaner
|
763
790
|
|
@@ -788,11 +815,11 @@ bin/rails generate maintenance_tasks:install
|
|
788
815
|
|
789
816
|
This ensures that new migrations are installed and run as well.
|
790
817
|
|
791
|
-
**What if I
|
818
|
+
**What if I’ve deleted my previous Maintenance Task migrations?**
|
792
819
|
|
793
820
|
The install command will attempt to reinstall these old migrations and migrating
|
794
821
|
the database will cause problems. Use `bin/rails
|
795
|
-
maintenance_tasks:install:migrations` to copy the gem
|
822
|
+
maintenance_tasks:install:migrations` to copy the gem’s migrations to your
|
796
823
|
`db/migrate` folder. Check the release notes to see if any new migrations were
|
797
824
|
added since your last gem upgrade. Ensure that these are kept, but remove any
|
798
825
|
migrations that already ran.
|
@@ -820,8 +847,8 @@ Once a release is ready, follow these steps:
|
|
820
847
|
* Deploy via [Shipit][shipit] and see the new version on
|
821
848
|
<https://rubygems.org/gems/maintenance_tasks>.
|
822
849
|
* Ensure the release has documented all changes and publish it.
|
823
|
-
* Create a new [draft release on GitHub][release] with the title
|
824
|
-
Release
|
850
|
+
* Create a new [draft release on GitHub][release] with the title “Upcoming
|
851
|
+
Release”. The tag version can be left blank. This will be the starting point
|
825
852
|
for documenting changes related to the next release.
|
826
853
|
|
827
854
|
[release]: https://help.github.com/articles/creating-releases/
|
@@ -18,10 +18,14 @@ module MaintenanceTasks
|
|
18
18
|
# Renders the page responsible for providing Task actions to users.
|
19
19
|
# Shows running and completed instances of the Task.
|
20
20
|
def show
|
21
|
-
|
22
|
-
@
|
23
|
-
|
21
|
+
task_name = params.fetch(:id)
|
22
|
+
@task = TaskDataShow.new(task_name)
|
23
|
+
@task.active_runs.load
|
24
|
+
set_refresh if @task.active_runs.any?
|
24
25
|
@runs_page = RunsPage.new(@task.completed_runs, params[:cursor])
|
26
|
+
if @task.active_runs.none? && @runs_page.records.none?
|
27
|
+
Task.named(task_name)
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
@@ -104,9 +104,10 @@ module MaintenanceTasks
|
|
104
104
|
# Return the appropriate field tag for the parameter
|
105
105
|
def parameter_field(form_builder, parameter_name)
|
106
106
|
case form_builder.object.class.attribute_types[parameter_name]
|
107
|
-
when ActiveModel::Type::Integer
|
108
|
-
ActiveModel::Type::Float
|
107
|
+
when ActiveModel::Type::Integer
|
109
108
|
form_builder.number_field(parameter_name)
|
109
|
+
when ActiveModel::Type::Decimal, ActiveModel::Type::Float
|
110
|
+
form_builder.number_field(parameter_name, { step: "any" })
|
110
111
|
when ActiveModel::Type::DateTime
|
111
112
|
form_builder.datetime_field(parameter_name)
|
112
113
|
when ActiveModel::Type::Date
|
@@ -37,17 +37,22 @@ module MaintenanceTasks
|
|
37
37
|
|
38
38
|
enum status: STATUSES.to_h { |status| [status, status.to_s] }
|
39
39
|
|
40
|
-
validates :task_name, on: :create, inclusion: {
|
41
|
-
Task.available_tasks.map(&:to_s)
|
42
|
-
}
|
40
|
+
validates :task_name, on: :create, inclusion: {
|
41
|
+
in: ->(_) { Task.available_tasks.map(&:to_s) },
|
42
|
+
}
|
43
43
|
validate :csv_attachment_presence, on: :create
|
44
44
|
validate :csv_content_type, on: :create
|
45
45
|
validate :validate_task_arguments, on: :create
|
46
46
|
|
47
47
|
attr_readonly :task_name
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
if Rails.gem_version >= Gem::Version.new("7.1.alpha")
|
50
|
+
serialize :backtrace, coder: YAML
|
51
|
+
serialize :arguments, coder: JSON
|
52
|
+
else
|
53
|
+
serialize :backtrace
|
54
|
+
serialize :arguments, JSON
|
55
|
+
end
|
51
56
|
|
52
57
|
scope :active, -> { where(status: ACTIVE_STATUSES) }
|
53
58
|
scope :completed, -> { where(status: COMPLETED_STATUSES) }
|
@@ -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
|
|
@@ -11,26 +11,6 @@ module MaintenanceTasks
|
|
11
11
|
#
|
12
12
|
# @api private
|
13
13
|
class TaskDataShow
|
14
|
-
class << self
|
15
|
-
# Initializes a Task Data by name, raising if the Task does not exist.
|
16
|
-
#
|
17
|
-
# For the purpose of this method, a Task does not exist if it's deleted
|
18
|
-
# and doesn't have a Run. While technically, it could have existed and
|
19
|
-
# been deleted since, if it never had a Run we may as well consider it
|
20
|
-
# non-existent since we don't have interesting data to show.
|
21
|
-
#
|
22
|
-
# @param name [String] the name of the Task subclass.
|
23
|
-
# @return [TaskDataShow] a Task Data instance.
|
24
|
-
# @raise [Task::NotFoundError] if the Task does not exist and doesn't have
|
25
|
-
# a Run.
|
26
|
-
def find(name)
|
27
|
-
task_data = new(name)
|
28
|
-
task_data.active_runs.load
|
29
|
-
task_data.has_any_run? || Task.named(name)
|
30
|
-
task_data
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
14
|
# Initializes a Task Data with a name and optionally a related run.
|
35
15
|
#
|
36
16
|
# @param name [String] the name of the Task subclass.
|
@@ -107,12 +87,6 @@ module MaintenanceTasks
|
|
107
87
|
MaintenanceTasks::Task.named(name).new
|
108
88
|
end
|
109
89
|
|
110
|
-
# @return [Boolean] whether the Task has any Run.
|
111
|
-
# @api private
|
112
|
-
def has_any_run?
|
113
|
-
active_runs.any? || completed_runs.any?
|
114
|
-
end
|
115
|
-
|
116
90
|
private
|
117
91
|
|
118
92
|
def runs
|
@@ -15,9 +15,9 @@
|
|
15
15
|
<%= csrf_meta_tags %>
|
16
16
|
|
17
17
|
<%=
|
18
|
-
stylesheet_link_tag(URI.join(controller.class::BULMA_CDN, 'npm/bulma@0.9.
|
18
|
+
stylesheet_link_tag(URI.join(controller.class::BULMA_CDN, 'npm/bulma@0.9.4/css/bulma.css'),
|
19
19
|
media: :all,
|
20
|
-
integrity: 'sha384-
|
20
|
+
integrity: 'sha384-qQlNh1kc0FyhUqUDXKkl5wpiiSm8PXQw2ZWhAVfU46tmdMDfq2vXG2CXWYT+Dls3',
|
21
21
|
crossorigin: 'anonymous') unless request.xhr?
|
22
22
|
%>
|
23
23
|
|
@@ -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 %>
|
@@ -2,7 +2,12 @@
|
|
2
2
|
|
3
3
|
class AddLockVersionToMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
|
4
4
|
def change
|
5
|
-
add_column(
|
6
|
-
|
5
|
+
add_column(
|
6
|
+
:maintenance_tasks_runs,
|
7
|
+
:lock_version,
|
8
|
+
:integer,
|
9
|
+
default: 0,
|
10
|
+
null: false,
|
11
|
+
)
|
7
12
|
end
|
8
13
|
end
|
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
class AddIndexOnTaskNameAndStatusToRuns < ActiveRecord::Migration[6.0]
|
4
4
|
def change
|
5
|
-
remove_index(
|
6
|
-
|
7
|
-
|
5
|
+
remove_index(
|
6
|
+
:maintenance_tasks_runs,
|
7
|
+
column: [:task_name, :created_at],
|
8
|
+
order: { created_at: :desc },
|
9
|
+
name: :index_maintenance_tasks_runs_on_task_name_and_created_at,
|
10
|
+
)
|
8
11
|
|
9
|
-
add_index(
|
12
|
+
add_index(
|
13
|
+
:maintenance_tasks_runs,
|
14
|
+
[:task_name, :status, :created_at],
|
10
15
|
name: :index_maintenance_tasks_runs,
|
11
|
-
order: { created_at: :desc }
|
16
|
+
order: { created_at: :desc },
|
17
|
+
)
|
12
18
|
end
|
13
19
|
end
|
@@ -9,10 +9,14 @@ module MaintenanceTasks
|
|
9
9
|
desc "This generator creates a task file at app/tasks and a corresponding "\
|
10
10
|
"test."
|
11
11
|
|
12
|
-
class_option :csv,
|
12
|
+
class_option :csv,
|
13
|
+
type: :boolean,
|
14
|
+
default: false,
|
13
15
|
desc: "Generate a CSV Task."
|
14
16
|
|
15
|
-
class_option :no_collection,
|
17
|
+
class_option :no_collection,
|
18
|
+
type: :boolean,
|
19
|
+
default: false,
|
16
20
|
desc: "Generate a collection-less Task."
|
17
21
|
|
18
22
|
check_class_collision suffix: "Task"
|
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.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -154,7 +154,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
|
|
154
154
|
licenses:
|
155
155
|
- MIT
|
156
156
|
metadata:
|
157
|
-
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.
|
157
|
+
source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.1.1
|
158
158
|
allowed_push_host: https://rubygems.org
|
159
159
|
post_install_message:
|
160
160
|
rdoc_options: []
|
@@ -171,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '0'
|
173
173
|
requirements: []
|
174
|
-
rubygems_version: 3.
|
174
|
+
rubygems_version: 3.4.10
|
175
175
|
signing_key:
|
176
176
|
specification_version: 4
|
177
177
|
summary: A Rails engine for queuing and managing maintenance tasks
|