maintenance_tasks 1.0.0 → 1.2.1

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -22
  3. data/Rakefile +16 -16
  4. data/app/controllers/maintenance_tasks/application_controller.rb +3 -6
  5. data/app/controllers/maintenance_tasks/runs_controller.rb +1 -12
  6. data/app/controllers/maintenance_tasks/tasks_controller.rb +7 -4
  7. data/app/helpers/maintenance_tasks/application_helper.rb +2 -48
  8. data/app/helpers/maintenance_tasks/{task_helper.rb → tasks_helper.rb} +26 -18
  9. data/app/jobs/maintenance_tasks/task_job.rb +49 -6
  10. data/app/models/maintenance_tasks/application_record.rb +2 -2
  11. data/app/models/maintenance_tasks/csv_collection.rb +33 -0
  12. data/app/models/maintenance_tasks/progress.rb +19 -14
  13. data/app/models/maintenance_tasks/run.rb +39 -1
  14. data/app/models/maintenance_tasks/runner.rb +20 -5
  15. data/app/models/maintenance_tasks/runs_page.rb +55 -0
  16. data/app/models/maintenance_tasks/task_data.rb +9 -5
  17. data/app/models/maintenance_tasks/ticker.rb +0 -1
  18. data/app/tasks/maintenance_tasks/task.rb +43 -16
  19. data/app/validators/maintenance_tasks/run_status_validator.rb +15 -13
  20. data/app/views/layouts/maintenance_tasks/_navbar.html.erb +0 -6
  21. data/app/views/maintenance_tasks/runs/_info.html.erb +6 -0
  22. data/app/views/maintenance_tasks/runs/_run.html.erb +1 -9
  23. data/app/views/maintenance_tasks/runs/info/_running.html.erb +0 -2
  24. data/app/views/maintenance_tasks/tasks/show.html.erb +31 -4
  25. data/config/routes.rb +4 -6
  26. data/db/migrate/20210225152418_remove_index_on_task_name.rb +14 -0
  27. data/exe/maintenance_tasks +2 -2
  28. data/lib/generators/maintenance_tasks/install_generator.rb +4 -5
  29. data/lib/generators/maintenance_tasks/task_generator.rb +15 -9
  30. data/lib/generators/maintenance_tasks/templates/csv_task.rb.tt +13 -0
  31. data/lib/generators/maintenance_tasks/templates/task_spec.rb.tt +1 -1
  32. data/lib/generators/maintenance_tasks/templates/task_test.rb.tt +1 -1
  33. data/lib/maintenance_tasks.rb +34 -30
  34. data/lib/maintenance_tasks/cli.rb +18 -4
  35. data/lib/maintenance_tasks/engine.rb +6 -3
  36. metadata +9 -31
  37. data/app/views/maintenance_tasks/runs/index.html.erb +0 -15
  38. data/app/views/maintenance_tasks/tasks/actions/_cancelled.html.erb +0 -1
  39. data/app/views/maintenance_tasks/tasks/actions/_cancelling.html.erb +0 -4
  40. data/app/views/maintenance_tasks/tasks/actions/_enqueued.html.erb +0 -2
  41. data/app/views/maintenance_tasks/tasks/actions/_errored.html.erb +0 -1
  42. data/app/views/maintenance_tasks/tasks/actions/_interrupted.html.erb +0 -2
  43. data/app/views/maintenance_tasks/tasks/actions/_new.html.erb +0 -1
  44. data/app/views/maintenance_tasks/tasks/actions/_paused.html.erb +0 -2
  45. data/app/views/maintenance_tasks/tasks/actions/_pausing.html.erb +0 -2
  46. data/app/views/maintenance_tasks/tasks/actions/_running.html.erb +0 -2
  47. data/app/views/maintenance_tasks/tasks/actions/_succeeded.html.erb +0 -1
  48. data/lib/maintenance_tasks/integrations/bugsnag_handler.rb +0 -4
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= tasks_module %>
4
+ <% module_namespacing do -%>
5
+ class <%= class_name %>Task < MaintenanceTasks::Task
6
+ csv_collection
7
+
8
+ def process(row)
9
+ # The work to be done on a row of the CSV
10
+ end
11
+ end
12
+ <% end -%>
13
+ end
@@ -6,7 +6,7 @@ module <%= tasks_module %>
6
6
  RSpec.describe <%= class_name %>Task do
7
7
  # describe '#process' do
8
8
  # it 'performs a task iteration' do
9
- # <%= tasks_module %>::<%= class_name %>Task.new.process(element)
9
+ # <%= tasks_module %>::<%= class_name %>Task.process(element)
10
10
  # end
11
11
  # end
12
12
  end
@@ -5,7 +5,7 @@ module <%= tasks_module %>
5
5
  <% module_namespacing do -%>
6
6
  class <%= class_name %>TaskTest < ActiveSupport::TestCase
7
7
  # test "#process performs a task iteration" do
8
- # <%= tasks_module %>::<%= class_name %>Task.new.process(element)
8
+ # <%= tasks_module %>::<%= class_name %>Task.process(element)
9
9
  # end
10
10
  end
11
11
  <% end -%>
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
- require 'action_controller'
3
- require 'action_view'
4
- require 'active_job'
5
- require 'active_record'
2
+ require "action_controller"
3
+ require "action_view"
4
+ require "active_job"
5
+ require "active_record"
6
6
 
7
- require 'job-iteration'
8
- require 'maintenance_tasks/engine'
9
- require 'pagy'
10
- require 'pagy/extras/bulma'
7
+ require "job-iteration"
8
+ require "maintenance_tasks/engine"
9
+
10
+ # Force the TaskJob class to load so we can verify upstream compatibility with
11
+ # the JobIteration gem
12
+ require_relative "../app/jobs/maintenance_tasks/task_job"
11
13
 
12
14
  # The engine's namespace module. It provides isolation between the host
13
15
  # application's code and the engine-specific code. Top-level engine constants
@@ -15,13 +17,13 @@ require 'pagy/extras/bulma'
15
17
  module MaintenanceTasks
16
18
  # The module to namespace Tasks in, as a String. Defaults to 'Maintenance'.
17
19
  # @param [String] the tasks_module value.
18
- mattr_accessor :tasks_module, default: 'Maintenance'
20
+ mattr_accessor :tasks_module, default: "Maintenance"
19
21
 
20
22
  # Defines the job to be used to perform Tasks. This job must be either
21
23
  # `MaintenanceTasks::TaskJob` or a class that inherits from it.
22
24
  #
23
25
  # @param [String] the name of the job class.
24
- mattr_writer :job, default: 'MaintenanceTasks::TaskJob'
26
+ mattr_accessor :job, default: "MaintenanceTasks::TaskJob"
25
27
 
26
28
  # After each iteration, the progress of the task may be updated. This duration
27
29
  # in seconds limits these updates, skipping if the duration since the last
@@ -32,27 +34,29 @@ module MaintenanceTasks
32
34
  # the ticker during Task iterations.
33
35
  mattr_accessor :ticker_delay, default: 1.second
34
36
 
35
- # Defines a callback to be performed when an error occurs in the task.
36
- mattr_accessor :error_handler, default: ->(_error) {}
37
-
38
- class << self
39
- # Retrieves the class that is configured as the Task Job to be used to
40
- # perform Tasks.
41
- #
42
- # @return [TaskJob] the job class.
43
- def job
44
- @@job.constantize
45
- end
37
+ # Specifies which Active Storage service to use for uploading CSV file blobs.
38
+ #
39
+ # @param [Symbol] the key for the storage service, as specified in the app's
40
+ # config/storage.yml.
41
+ mattr_accessor :active_storage_service
42
+
43
+ # Retrieves the callback to be performed when an error occurs in the task.
44
+ def self.error_handler
45
+ return @error_handler if defined?(@error_handler)
46
+ @error_handler = ->(_error, _task_context, _errored_element) {}
47
+ end
46
48
 
47
- # Attempts to configure Bugsnag integration. If the application uses
48
- # Bugsnag, it is automatically configured to report on errors raised while
49
- # a Task is performing.
50
- def configure_bugsnag_integration
51
- load('maintenance_tasks/integrations/bugsnag_handler.rb')
52
- rescue LoadError
53
- nil
49
+ # Defines a callback to be performed when an error occurs in the task.
50
+ def self.error_handler=(error_handler)
51
+ unless error_handler.arity == 3
52
+ ActiveSupport::Deprecation.warn(
53
+ "MaintenanceTasks.error_handler should be a lambda that takes three "\
54
+ "arguments: error, task_context, and errored_element."
55
+ )
56
+ @error_handler = ->(error, _task_context, _errored_element) do
57
+ error_handler.call(error)
58
+ end
54
59
  end
60
+ @error_handler = error_handler
55
61
  end
56
62
  end
57
-
58
- MaintenanceTasks.configure_bugsnag_integration
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
3
+ require "thor"
4
4
 
5
5
  module MaintenanceTasks
6
6
  # Defines the command line interface commands exposed by Maintenance Tasks in
@@ -13,7 +13,7 @@ module MaintenanceTasks
13
13
  end
14
14
  end
15
15
 
16
- desc 'perform [TASK NAME]', 'Runs the given Maintenance Task'
16
+ desc "perform [TASK NAME]", "Runs the given Maintenance Task"
17
17
 
18
18
  long_desc <<-LONGDESC
19
19
  `maintenance_tasks perform` will run the Maintenance Task specified by the
@@ -24,17 +24,31 @@ module MaintenanceTasks
24
24
  #{MaintenanceTasks::Task.available_tasks.join("\n\n")}
25
25
  LONGDESC
26
26
 
27
+ # Specify the CSV file to process for CSV Tasks
28
+ option :csv, desc: "Supply a CSV file to be processed by a CSV Task, "\
29
+ '--csv "path/to/csv/file.csv"'
30
+
27
31
  # Command to run a Task.
28
32
  #
29
33
  # It instantiates a Runner and sends a run message with the given Task name.
34
+ # If a CSV file is supplied using the --csv option, an attachable with the
35
+ # File IO object is sent along with the Task name to run.
30
36
  #
31
37
  # @param name [String] the name of the Task to be run.
32
38
  def perform(name)
33
- task = Runner.new.run(name: name)
39
+ task = Runner.run(name: name, csv_file: csv_file)
34
40
  say_status(:success, "#{task.name} was enqueued.", :green)
35
41
  rescue => error
36
42
  say_status(:error, error.message, :red)
37
43
  end
44
+
45
+ private
46
+
47
+ def csv_file
48
+ csv_option = options[:csv]
49
+ if csv_option
50
+ { io: File.open(csv_option), filename: File.basename(csv_option) }
51
+ end
52
+ end
38
53
  end
39
- private_constant :CLI
40
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'active_record/railtie'
2
+ require "active_record/railtie"
3
3
 
4
4
  module MaintenanceTasks
5
5
  # The engine's main class, which defines its namespace. The engine is mounted
@@ -7,6 +7,10 @@ module MaintenanceTasks
7
7
  class Engine < ::Rails::Engine
8
8
  isolate_namespace MaintenanceTasks
9
9
 
10
+ initializer "eager_load_for_classic_autoloader" do
11
+ eager_load! unless Rails.autoloaders.zeitwerk_enabled?
12
+ end
13
+
10
14
  config.to_prepare do
11
15
  unless Rails.autoloaders.zeitwerk_enabled?
12
16
  tasks_module = MaintenanceTasks.tasks_module.underscore
@@ -17,12 +21,11 @@ module MaintenanceTasks
17
21
  end
18
22
 
19
23
  config.after_initialize do
20
- eager_load! unless Rails.autoloaders.zeitwerk_enabled?
21
24
  JobIteration.max_job_runtime ||= 5.minutes
22
25
  end
23
26
 
24
27
  config.action_dispatch.rescue_responses.merge!(
25
- 'MaintenanceTasks::Task::NotFoundError' => :not_found,
28
+ "MaintenanceTasks::Task::NotFoundError" => :not_found,
26
29
  )
27
30
  end
28
31
  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: 1.0.0
4
+ version: 1.2.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: 2021-01-06 00:00:00.000000000 Z
11
+ date: 2021-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
- - !ruby/object:Gem::Dependency
70
- name: pagy
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.9'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.9'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: railties
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -107,12 +93,14 @@ files:
107
93
  - app/controllers/maintenance_tasks/runs_controller.rb
108
94
  - app/controllers/maintenance_tasks/tasks_controller.rb
109
95
  - app/helpers/maintenance_tasks/application_helper.rb
110
- - app/helpers/maintenance_tasks/task_helper.rb
96
+ - app/helpers/maintenance_tasks/tasks_helper.rb
111
97
  - app/jobs/maintenance_tasks/task_job.rb
112
98
  - app/models/maintenance_tasks/application_record.rb
99
+ - app/models/maintenance_tasks/csv_collection.rb
113
100
  - app/models/maintenance_tasks/progress.rb
114
101
  - app/models/maintenance_tasks/run.rb
115
102
  - app/models/maintenance_tasks/runner.rb
103
+ - app/models/maintenance_tasks/runs_page.rb
116
104
  - app/models/maintenance_tasks/task_data.rb
117
105
  - app/models/maintenance_tasks/ticker.rb
118
106
  - app/tasks/maintenance_tasks/task.rb
@@ -121,7 +109,6 @@ files:
121
109
  - app/views/layouts/maintenance_tasks/application.html.erb
122
110
  - app/views/maintenance_tasks/runs/_info.html.erb
123
111
  - app/views/maintenance_tasks/runs/_run.html.erb
124
- - app/views/maintenance_tasks/runs/index.html.erb
125
112
  - app/views/maintenance_tasks/runs/info/_cancelled.html.erb
126
113
  - app/views/maintenance_tasks/runs/info/_cancelling.html.erb
127
114
  - app/views/maintenance_tasks/runs/info/_enqueued.html.erb
@@ -132,38 +119,29 @@ files:
132
119
  - app/views/maintenance_tasks/runs/info/_running.html.erb
133
120
  - app/views/maintenance_tasks/runs/info/_succeeded.html.erb
134
121
  - app/views/maintenance_tasks/tasks/_task.html.erb
135
- - app/views/maintenance_tasks/tasks/actions/_cancelled.html.erb
136
- - app/views/maintenance_tasks/tasks/actions/_cancelling.html.erb
137
- - app/views/maintenance_tasks/tasks/actions/_enqueued.html.erb
138
- - app/views/maintenance_tasks/tasks/actions/_errored.html.erb
139
- - app/views/maintenance_tasks/tasks/actions/_interrupted.html.erb
140
- - app/views/maintenance_tasks/tasks/actions/_new.html.erb
141
- - app/views/maintenance_tasks/tasks/actions/_paused.html.erb
142
- - app/views/maintenance_tasks/tasks/actions/_pausing.html.erb
143
- - app/views/maintenance_tasks/tasks/actions/_running.html.erb
144
- - app/views/maintenance_tasks/tasks/actions/_succeeded.html.erb
145
122
  - app/views/maintenance_tasks/tasks/index.html.erb
146
123
  - app/views/maintenance_tasks/tasks/show.html.erb
147
124
  - config/routes.rb
148
125
  - db/migrate/20201211151756_create_maintenance_tasks_runs.rb
126
+ - db/migrate/20210225152418_remove_index_on_task_name.rb
149
127
  - exe/maintenance_tasks
150
128
  - lib/generators/maintenance_tasks/install_generator.rb
151
129
  - lib/generators/maintenance_tasks/task_generator.rb
130
+ - lib/generators/maintenance_tasks/templates/csv_task.rb.tt
152
131
  - lib/generators/maintenance_tasks/templates/task.rb.tt
153
132
  - lib/generators/maintenance_tasks/templates/task_spec.rb.tt
154
133
  - lib/generators/maintenance_tasks/templates/task_test.rb.tt
155
134
  - lib/maintenance_tasks.rb
156
135
  - lib/maintenance_tasks/cli.rb
157
136
  - lib/maintenance_tasks/engine.rb
158
- - lib/maintenance_tasks/integrations/bugsnag_handler.rb
159
137
  - lib/tasks/maintenance_tasks_tasks.rake
160
138
  homepage: https://github.com/Shopify/maintenance_tasks
161
139
  licenses: []
162
140
  metadata:
163
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.0.0
141
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.2.1
164
142
  allowed_push_host: https://rubygems.org
165
143
  post_install_message: |-
166
- Thank you for installing Maintenance Tasks 1.0.0. To complete, please run:
144
+ Thank you for installing Maintenance Tasks 1.2.1. To complete, please run:
167
145
 
168
146
  rails generate maintenance_tasks:install
169
147
  rdoc_options: []
@@ -1,15 +0,0 @@
1
- <div class="block">
2
- <%= form_with url: runs_path, method: :get do |form| %>
3
- <div class="field has-addons">
4
- <div class="control">
5
- <%= form.search_field :task_name, value: params[:task_name], placeholder: "Task name", class: "input" %>
6
- </div>
7
- <div class="control">
8
- <%= form.submit "Search", class: "button is-link" %>
9
- </div>
10
- </div>
11
- <% end %>
12
- </div>
13
-
14
- <%= render @runs, with_task_name: true %>
15
- <%= pagination(@pagy) %>
@@ -1 +0,0 @@
1
- <%= button_to 'Run', run_task_path(task), method: :put, class: 'button is-success', disabled: task.deleted? %>
@@ -1,4 +0,0 @@
1
- <%= button_to 'Run', run_task_path(task), method: :put, class: 'button is-success', disabled: true %>
2
- <% if task.last_run.stuck? %>
3
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger', disabled: task.deleted? %>
4
- <% end %>
@@ -1,2 +0,0 @@
1
- <%= button_to 'Pause', pause_task_run_path(task, task.last_run), method: :put, class: 'button is-warning', disabled: task.deleted? %>
2
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger' %>
@@ -1 +0,0 @@
1
- <%= button_to 'Run', run_task_path(task), method: :put, class: 'button is-success', disabled: task.deleted? %>
@@ -1,2 +0,0 @@
1
- <%= button_to 'Pause', pause_task_run_path(task, task.last_run), method: :put, class: 'button is-warning', disabled: task.deleted? %>
2
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger' %>
@@ -1 +0,0 @@
1
- <%= button_to 'Run', run_task_path(task), method: :put, class: 'button is-success', disabled: task.deleted? %>
@@ -1,2 +0,0 @@
1
- <%= button_to 'Resume', run_task_path(task), method: :put, class: 'button is-primary', disabled: task.deleted? %>
2
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger' %>
@@ -1,2 +0,0 @@
1
- <%= button_to 'Pausing', pause_task_run_path(task, task.last_run), method: :put, class: 'button is-warning', disabled: true %>
2
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger', disabled: task.deleted? %>
@@ -1,2 +0,0 @@
1
- <%= button_to 'Pause', pause_task_run_path(task, task.last_run), method: :put, class: 'button is-warning', disabled: task.deleted? %>
2
- <%= button_to 'Cancel', cancel_task_run_path(task, task.last_run), method: :put, class: 'button is-danger' %>
@@ -1 +0,0 @@
1
- <%= button_to 'Run', run_task_path(task), method: :put, class: 'button is-success', disabled: task.deleted? %>
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'bugsnag'
3
-
4
- MaintenanceTasks.error_handler = ->(error) { Bugsnag.notify(error) }