maintenance_tasks 1.6.0 → 1.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8affacb1fe5aa5298899bb27875132a4b299b1bd89e239b8fc234df1d00e171d
4
- data.tar.gz: cd9351e3abed12d1c1bede76cea7ac1d664159983b3ece57fe2707a0de971db5
3
+ metadata.gz: 6019b1623a8cc72e877154f52e8437339598f0cab121189d78692e0989e86e61
4
+ data.tar.gz: 21563dc6dffadd2e58dd551fe2b7d71297b2da36500722f5d9fed91892b1fd6e
5
5
  SHA512:
6
- metadata.gz: c0459cf005532667f4d5bbc8a1b05adc5e4aa57bccb4b4a3e9b9338589ff73043272201e5cd0a86ed0abfa38430b18ae5021ec1991a81f064444665be8de4e3f
7
- data.tar.gz: d1f9b671f215a2d4a908a9a2353f02d6800511ea8d3337b4074712ec3aed7bdcd3a6b243960915a11e4ee31ca1b756d1992ce3af5a5cd27457a9016b42136842
6
+ metadata.gz: 93228d08b49fab144297cb44a0aa4b9be53144ff13b88c2eba384ef29fdb47b9a8caefaf94aaf1fbfbf4584f4d544396720c17d86f3bad31508fd66dfd7507b8
7
+ data.tar.gz: cab4cebb685fddf978eeebd62a5c16867215f849d424b9ba948beba0e93f42a5fe11ee1eccc15174c1494fa16a2fa100d3a7745c5eacef2527d425895805cf29
data/README.md CHANGED
@@ -114,8 +114,9 @@ title,content
114
114
  My Title,Hello World!
115
115
  ```
116
116
 
117
- The files uploaded to your Active Storage service provider will be renamed first
117
+ The files uploaded to your Active Storage service provider will be renamed
118
118
  to include an ISO8601 timestamp and the Task name in snake case format.
119
+ The CSV is expected to have a trailing newline at the end of the file.
119
120
 
120
121
  ### Processing Batch Collections
121
122
 
@@ -200,6 +201,20 @@ Tasks can define multiple throttle conditions. Throttle conditions are inherited
200
201
  by descendants, and new conditions will be appended without impacting existing
201
202
  conditions.
202
203
 
204
+ The backoff can also be specified as a proc:
205
+
206
+ ```ruby
207
+ # app/tasks/maintenance/update_posts_throttled_task.rb
208
+
209
+ module Maintenance
210
+ class UpdatePostsThrottledTask < MaintenanceTasks::Task
211
+ throttle_on(backoff: -> { RandomBackoffGenerator.generate_duration } ) do
212
+ DatabaseStatus.unhealthy?
213
+ end
214
+ ...
215
+ end
216
+ end
217
+ ```
203
218
  ### Custom Task Parameters
204
219
 
205
220
  Tasks may need additional information, supplied via parameters, to run.
@@ -241,12 +256,12 @@ The Task provides callbacks that hook into its life cycle.
241
256
 
242
257
  Available callbacks are:
243
258
 
244
- `after_start`
245
- `after_pause`
246
- `after_interrupt`
247
- `after_cancel`
248
- `after_complete`
249
- `after_error`
259
+ * `after_start`
260
+ * `after_pause`
261
+ * `after_interrupt`
262
+ * `after_cancel`
263
+ * `after_complete`
264
+ * `after_error`
250
265
 
251
266
  ```ruby
252
267
  module Maintenance
@@ -542,7 +557,7 @@ you can define an error handler:
542
557
 
543
558
  MaintenanceTasks.error_handler = ->(error, task_context, _errored_element) do
544
559
  Bugsnag.notify(error) do |notification|
545
- notification.add_tab(:task, task_context)
560
+ notification.add_metadata(:task, task_context)
546
561
  end
547
562
  end
548
563
  ```
@@ -24,11 +24,12 @@ module MaintenanceTasks
24
24
  end
25
25
 
26
26
  # Runs a given Task and redirects to the Task page.
27
- def run
27
+ def run(&block)
28
28
  task = Runner.run(
29
29
  name: params.fetch(:id),
30
30
  csv_file: params[:csv_file],
31
31
  arguments: params.fetch(:task_arguments, {}).permit!.to_h,
32
+ &block
32
33
  )
33
34
  redirect_to(task_path(task))
34
35
  rescue ActiveRecord::RecordInvalid => error
@@ -100,5 +100,24 @@ module MaintenanceTasks
100
100
  only_path: true
101
101
  )
102
102
  end
103
+
104
+ # Return the appropriate field tag for the parameter
105
+ def parameter_field(form_builder, parameter_name)
106
+ case form_builder.object.class.attribute_types[parameter_name]
107
+ when ActiveModel::Type::Integer, ActiveModel::Type::Decimal,
108
+ ActiveModel::Type::Float
109
+ form_builder.number_field(parameter_name)
110
+ when ActiveModel::Type::DateTime
111
+ form_builder.datetime_field(parameter_name)
112
+ when ActiveModel::Type::Date
113
+ form_builder.date_field(parameter_name)
114
+ when ActiveModel::Type::Time
115
+ form_builder.time_field(parameter_name)
116
+ when ActiveModel::Type::Boolean
117
+ form_builder.check_box(parameter_name)
118
+ else
119
+ form_builder.text_area(parameter_name, class: "textarea")
120
+ end
121
+ end
103
122
  end
104
123
  end
@@ -12,8 +12,8 @@ module MaintenanceTasks
12
12
  before_perform(:before_perform)
13
13
 
14
14
  on_start(:on_start)
15
- on_complete(:on_complete)
16
15
  on_shutdown(:on_shutdown)
16
+ on_complete(:on_complete)
17
17
 
18
18
  after_perform(:after_perform)
19
19
 
@@ -63,9 +63,16 @@ module MaintenanceTasks
63
63
  Array, or CSV.
64
64
  MSG
65
65
  end
66
+ throttle_enumerator(collection_enum)
67
+ end
66
68
 
69
+ def throttle_enumerator(collection_enum)
67
70
  @task.throttle_conditions.reduce(collection_enum) do |enum, condition|
68
- enumerator_builder.build_throttle_enumerator(enum, **condition)
71
+ enumerator_builder.build_throttle_enumerator(
72
+ enum,
73
+ throttle_on: condition[:throttle_on],
74
+ backoff: condition[:backoff].call
75
+ )
69
76
  end
70
77
  end
71
78
 
@@ -105,33 +112,19 @@ module MaintenanceTasks
105
112
  def on_start
106
113
  count = @task.count
107
114
  count = @enumerator&.size if count == :no_count
108
- @run.update!(started_at: Time.now, tick_total: count)
109
- @task.run_callbacks(:start)
110
- end
111
-
112
- def on_complete
113
- @run.status = :succeeded
114
- @run.ended_at = Time.now
115
- @task.run_callbacks(:complete)
115
+ @run.start(count)
116
116
  end
117
117
 
118
118
  def on_shutdown
119
- if @run.cancelling?
120
- @run.status = :cancelled
121
- @task.run_callbacks(:cancel)
122
- @run.ended_at = Time.now
123
- elsif @run.pausing?
124
- @run.status = :paused
125
- @task.run_callbacks(:pause)
126
- else
127
- @run.status = :interrupted
128
- @task.run_callbacks(:interrupt)
129
- end
130
-
119
+ @run.job_shutdown
131
120
  @run.cursor = cursor_position
132
121
  @ticker.persist
133
122
  end
134
123
 
124
+ def on_complete
125
+ @run.complete
126
+ end
127
+
135
128
  # We are reopening a private part of Job Iteration's API here, so we should
136
129
  # ensure the method is still defined upstream. This way, in the case where
137
130
  # the method changes upstream, we catch it at load time instead of at
@@ -150,7 +143,7 @@ module MaintenanceTasks
150
143
  end
151
144
 
152
145
  def after_perform
153
- @run.save!
146
+ @run.persist_transition
154
147
  if defined?(@reenqueue_iteration_job) && @reenqueue_iteration_job
155
148
  reenqueue_iteration_job(should_ignore: false)
156
149
  end
@@ -171,15 +164,8 @@ module MaintenanceTasks
171
164
  task_context = {}
172
165
  end
173
166
  errored_element = @errored_element if defined?(@errored_element)
174
- run_error_callback
175
167
  ensure
176
168
  MaintenanceTasks.error_handler.call(error, task_context, errored_element)
177
169
  end
178
-
179
- def run_error_callback
180
- @task.run_callbacks(:error) if defined?(@task)
181
- rescue
182
- nil
183
- end
184
170
  end
185
171
  end
@@ -15,9 +15,10 @@ module MaintenanceTasks
15
15
  CSV.new(task.csv_content, headers: true)
16
16
  end
17
17
 
18
- # The number of rows to be processed. Excludes the header row from the count
19
- # and assumed a trailing new line in the CSV file. Note that this number is
20
- # an approximation based on the number of new lines.
18
+ # The number of rows to be processed. Excludes the header row from the
19
+ # count and assumes a trailing newline is at the end of the CSV file.
20
+ # Note that this number is an approximation based on the number of
21
+ # newlines.
21
22
  #
22
23
  # @return [Integer] the approximate number of rows to process.
23
24
  def count(task)
@@ -66,9 +66,40 @@ module MaintenanceTasks
66
66
 
67
67
  # Sets the run status to enqueued, making sure the transition is validated
68
68
  # in case it's already enqueued.
69
+ #
70
+ # Rescues and retries status transition if an ActiveRecord::StaleObjectError
71
+ # is encountered.
69
72
  def enqueued!
70
73
  status_will_change!
71
74
  super
75
+ rescue ActiveRecord::StaleObjectError
76
+ reload_status
77
+ retry
78
+ end
79
+
80
+ CALLBACKS_TRANSITION = {
81
+ cancelled: :cancel,
82
+ interrupted: :interrupt,
83
+ paused: :pause,
84
+ succeeded: :complete,
85
+ }.transform_keys(&:to_s)
86
+ private_constant :CALLBACKS_TRANSITION
87
+
88
+ # Saves the run, persisting the transition of its status, and all other
89
+ # changes to the object.
90
+ def persist_transition
91
+ save!
92
+ callback = CALLBACKS_TRANSITION[status]
93
+ run_task_callbacks(callback) if callback
94
+ rescue ActiveRecord::StaleObjectError
95
+ success = succeeded?
96
+ reload_status
97
+ if success
98
+ self.status = :succeeded
99
+ else
100
+ job_shutdown
101
+ end
102
+ retry
72
103
  end
73
104
 
74
105
  # Increments +tick_count+ by +number_of_ticks+ and +time_running+ by
@@ -86,6 +117,11 @@ module MaintenanceTasks
86
117
  time_running: duration,
87
118
  touch: true
88
119
  )
120
+ if locking_enabled?
121
+ locking_column = self.class.locking_column
122
+ self[locking_column] += 1
123
+ clear_attribute_change(locking_column)
124
+ end
89
125
  end
90
126
 
91
127
  # Marks the run as errored and persists the error data.
@@ -100,20 +136,34 @@ module MaintenanceTasks
100
136
  backtrace: MaintenanceTasks.backtrace_cleaner.clean(error.backtrace),
101
137
  ended_at: Time.now,
102
138
  )
139
+ run_task_callbacks(:error)
140
+ rescue ActiveRecord::StaleObjectError
141
+ reload_status
142
+ retry
103
143
  end
104
144
 
105
- # Refreshes just the status attribute on the Active Record object, and
106
- # ensures ActiveModel::Dirty does not mark the object as changed.
145
+ # Refreshes the status and lock version attributes on the Active Record
146
+ # object, and ensures ActiveModel::Dirty doesn't mark the object as changed.
147
+ #
107
148
  # This allows us to get the Run's most up-to-date status without needing
108
149
  # to reload the entire record.
109
150
  #
110
151
  # @return [MaintenanceTasks::Run] the Run record with its updated status.
111
152
  def reload_status
112
- updated_status = self.class.uncached do
113
- self.class.where(id: id).pluck(:status).first
153
+ columns_to_reload = if locking_enabled?
154
+ [:status, self.class.locking_column]
155
+ else
156
+ [:status]
157
+ end
158
+ updated_status, updated_lock_version = self.class.uncached do
159
+ self.class.where(id: id).pluck(*columns_to_reload).first
114
160
  end
161
+
115
162
  self.status = updated_status
116
- clear_attribute_changes([:status])
163
+ if updated_lock_version
164
+ self[self.class.locking_column] = updated_lock_version
165
+ end
166
+ clear_attribute_changes(columns_to_reload)
117
167
  self
118
168
  end
119
169
 
@@ -173,21 +223,65 @@ module MaintenanceTasks
173
223
  seconds_to_finished.seconds
174
224
  end
175
225
 
176
- # Mark a Run as running.
226
+ # Marks a Run as running.
177
227
  #
178
228
  # If the run is stopping already, it will not transition to running.
229
+ # Rescues and retries status transition if an ActiveRecord::StaleObjectError
230
+ # is encountered.
179
231
  def running
180
- return if stopping?
181
- updated = self.class.where(id: id).where.not(status: STOPPING_STATUSES)
182
- .update_all(status: :running, updated_at: Time.now) > 0
183
- if updated
184
- self.status = :running
185
- clear_attribute_changes([:status])
232
+ if locking_enabled?
233
+ begin
234
+ running! unless stopping?
235
+ rescue ActiveRecord::StaleObjectError
236
+ reload_status
237
+ retry
238
+ end
239
+ else
240
+ # Preserve swap-and-replace solution for data races until users
241
+ # run migration to upgrade to optimistic locking solution
242
+ return if stopping?
243
+ updated = self.class.where(id: id).where.not(status: STOPPING_STATUSES)
244
+ .update_all(status: :running, updated_at: Time.now) > 0
245
+ if updated
246
+ self.status = :running
247
+ clear_attribute_changes([:status])
248
+ else
249
+ reload_status
250
+ end
251
+ end
252
+ end
253
+
254
+ # Starts a Run, setting its started_at timestamp and tick_total.
255
+ #
256
+ # @param count [Integer] the total iterations to be performed, as
257
+ # specified by the Task.
258
+ def start(count)
259
+ update!(started_at: Time.now, tick_total: count)
260
+ run_task_callbacks(:start)
261
+ rescue ActiveRecord::StaleObjectError
262
+ reload_status
263
+ retry
264
+ end
265
+
266
+ # Handles transitioning the status on a Run when the job shuts down.
267
+ def job_shutdown
268
+ if cancelling?
269
+ self.status = :cancelled
270
+ self.ended_at = Time.now
271
+ elsif pausing?
272
+ self.status = :paused
186
273
  else
187
- reload_status
274
+ self.status = :interrupted
188
275
  end
189
276
  end
190
277
 
278
+ # Handles the completion of a Run, setting a status of succeeded and the
279
+ # ended_at timestamp.
280
+ def complete
281
+ self.status = :succeeded
282
+ self.ended_at = Time.now
283
+ end
284
+
191
285
  # Cancels a Run.
192
286
  #
193
287
  # If the Run is paused, it will transition directly to cancelled, since the
@@ -201,10 +295,26 @@ module MaintenanceTasks
201
295
  # will be updated.
202
296
  def cancel
203
297
  if paused? || stuck?
204
- update!(status: :cancelled, ended_at: Time.now)
298
+ self.status = :cancelled
299
+ self.ended_at = Time.now
300
+ persist_transition
205
301
  else
206
302
  cancelling!
207
303
  end
304
+ rescue ActiveRecord::StaleObjectError
305
+ reload_status
306
+ retry
307
+ end
308
+
309
+ # Marks a Run as pausing.
310
+ #
311
+ # Rescues and retries status transition if an ActiveRecord::StaleObjectError
312
+ # is encountered.
313
+ def pausing!
314
+ super
315
+ rescue ActiveRecord::StaleObjectError
316
+ reload_status
317
+ retry
208
318
  end
209
319
 
210
320
  # Returns whether a Run is stuck, which is defined as having a status of
@@ -297,6 +407,12 @@ module MaintenanceTasks
297
407
 
298
408
  private
299
409
 
410
+ def run_task_callbacks(callback)
411
+ task.run_callbacks(callback)
412
+ rescue
413
+ nil
414
+ end
415
+
300
416
  def arguments_match_task_attributes
301
417
  invalid_argument_keys = arguments.keys - task.attribute_names
302
418
  if invalid_argument_keys.any?
@@ -59,7 +59,8 @@ module MaintenanceTasks
59
59
  "To resolve this issue run: bin/rails active_storage:install"
60
60
  end
61
61
 
62
- self.collection_builder_strategy = CsvCollectionBuilder.new
62
+ self.collection_builder_strategy =
63
+ MaintenanceTasks::CsvCollectionBuilder.new
63
64
  end
64
65
 
65
66
  # Returns whether the Task handles CSV.
@@ -98,14 +99,19 @@ module MaintenanceTasks
98
99
 
99
100
  # Add a condition under which this Task will be throttled.
100
101
  #
101
- # @param backoff [ActiveSupport::Duration] optionally, a custom backoff
102
- # can be specified. This is the time to wait before retrying the Task.
103
- # If no value is specified, it defaults to 30 seconds.
102
+ # @param backoff [ActiveSupport::Duration, #call] a custom backoff
103
+ # can be specified. This is the time to wait before retrying the Task,
104
+ # defaulting to 30 seconds. If provided as a Duration, the backoff is
105
+ # wrapped in a proc. Alternatively,an object responding to call can be
106
+ # used. It must return an ActiveSupport::Duration.
104
107
  # @yieldreturn [Boolean] where the throttle condition is being met,
105
108
  # indicating that the Task should throttle.
106
109
  def throttle_on(backoff: 30.seconds, &condition)
110
+ backoff_as_proc = backoff
111
+ backoff_as_proc = -> { backoff } unless backoff.respond_to?(:call)
112
+
107
113
  self.throttle_conditions += [
108
- { throttle_on: condition, backoff: backoff },
114
+ { throttle_on: condition, backoff: backoff_as_proc },
109
115
  ]
110
116
  end
111
117
 
@@ -74,7 +74,11 @@ module MaintenanceTasks
74
74
  def code
75
75
  return if deleted?
76
76
  task = Task.named(name)
77
- file = task.instance_method(:process).source_location.first
77
+ file = if Object.respond_to?(:const_source_location)
78
+ Object.const_source_location(task.name).first
79
+ else
80
+ task.instance_method(:process).source_location.first
81
+ end
78
82
  File.read(file)
79
83
  end
80
84
 
@@ -142,6 +146,13 @@ module MaintenanceTasks
142
146
  end
143
147
  end
144
148
 
149
+ # @return [MaintenanceTasks::Task, nil] an instance of the Task class.
150
+ # @return [nil] if the Task file was deleted.
151
+ def new
152
+ return if deleted?
153
+ MaintenanceTasks::Task.named(name).new
154
+ end
155
+
145
156
  private
146
157
 
147
158
  def runs
@@ -10,6 +10,10 @@
10
10
  <%= render "maintenance_tasks/runs/info/#{run.status}", run: run %>
11
11
  </div>
12
12
 
13
+ <div class="content" id="custom-content">
14
+ <%= render "maintenance_tasks/runs/info/custom", run: run %>
15
+ </div>
16
+
13
17
  <%= render "maintenance_tasks/runs/csv", run: run %>
14
18
  <%= tag.hr if run.csv_file.present? && run.arguments.present? %>
15
19
  <%= render "maintenance_tasks/runs/arguments", run: run %>
@@ -15,6 +15,10 @@
15
15
  <%= render "maintenance_tasks/runs/info/#{run.status}", run: run %>
16
16
  </div>
17
17
 
18
+ <div class="content" id="custom-content">
19
+ <%= render "maintenance_tasks/runs/info/custom", run: run %>
20
+ </div>
21
+
18
22
  <%= render "maintenance_tasks/runs/csv", run: run %>
19
23
  <%= tag.hr if run.csv_file.present? && run.arguments.present? %>
20
24
  <%= render "maintenance_tasks/runs/arguments", run: run %>
@@ -16,6 +16,10 @@
16
16
  <%= render "maintenance_tasks/runs/info/#{last_run.status}", run: last_run %>
17
17
  </div>
18
18
 
19
+ <div class="content" id="custom-content">
20
+ <%= render "maintenance_tasks/runs/info/custom", run: last_run %>
21
+ </div>
22
+
19
23
  <%= render "maintenance_tasks/runs/csv", run: last_run %>
20
24
  <%= tag.hr if last_run.csv_file.present? %>
21
25
  <%= render "maintenance_tasks/runs/arguments", run: last_run %>
@@ -31,20 +35,22 @@
31
35
  <%= form.file_field :csv_file %>
32
36
  </div>
33
37
  <% end %>
34
- <% if @task.parameter_names.any? %>
38
+ <% parameter_names = @task.parameter_names %>
39
+ <% if parameter_names.any? %>
35
40
  <div class="block">
36
- <%= form.fields_for :task_arguments do |ff| %>
37
- <% @task.parameter_names.each do |parameter| %>
41
+ <%= form.fields_for :task_arguments, @task.new do |ff| %>
42
+ <% parameter_names.each do |parameter_name| %>
38
43
  <div class="field">
39
- <%= ff.label parameter, parameter, class: "label is-family-monospace" %>
44
+ <%= ff.label parameter_name, parameter_name, class: "label is-family-monospace" %>
40
45
  <div class="control">
41
- <%= ff.text_area parameter, class: "textarea" %>
46
+ <%= parameter_field(ff, parameter_name) %>
42
47
  </div>
43
48
  </div>
44
49
  <% end %>
45
50
  <% end %>
46
51
  </div>
47
52
  <% end %>
53
+ <%= render "maintenance_tasks/tasks/custom", form: form %>
48
54
  <div class="block">
49
55
  <%= form.submit 'Run', class: "button is-success", disabled: @task.deleted? %>
50
56
  </div>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddLockVersionToMaintenanceTasksRuns < ActiveRecord::Migration[6.0]
4
+ def change
5
+ add_column(:maintenance_tasks_runs, :lock_version, :integer,
6
+ default: 0, null: false)
7
+ end
8
+ end
@@ -8,7 +8,16 @@ module MaintenanceTasks
8
8
  class Engine < ::Rails::Engine
9
9
  isolate_namespace MaintenanceTasks
10
10
 
11
- initializer "eager_load_for_classic_autoloader" do
11
+ initializer "maintenance_tasks.warn_classic_autoloader" do
12
+ unless Rails.autoloaders.zeitwerk_enabled?
13
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
14
+ Autoloading in classic mode is deprecated and support will be removed in the next
15
+ release of Maintenance Tasks. Please use Zeitwerk to autoload your application.
16
+ MSG
17
+ end
18
+ end
19
+
20
+ initializer "maintenance_tasks.eager_load_for_classic_autoloader" do
12
21
  eager_load! unless Rails.autoloaders.zeitwerk_enabled?
13
22
  end
14
23
 
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.6.0
4
+ version: 1.7.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: 2021-11-05 00:00:00.000000000 Z
11
+ date: 2022-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -114,6 +114,7 @@ files:
114
114
  - app/views/maintenance_tasks/runs/_run.html.erb
115
115
  - app/views/maintenance_tasks/runs/info/_cancelled.html.erb
116
116
  - app/views/maintenance_tasks/runs/info/_cancelling.html.erb
117
+ - app/views/maintenance_tasks/runs/info/_custom.html.erb
117
118
  - app/views/maintenance_tasks/runs/info/_enqueued.html.erb
118
119
  - app/views/maintenance_tasks/runs/info/_errored.html.erb
119
120
  - app/views/maintenance_tasks/runs/info/_interrupted.html.erb
@@ -121,6 +122,7 @@ files:
121
122
  - app/views/maintenance_tasks/runs/info/_pausing.html.erb
122
123
  - app/views/maintenance_tasks/runs/info/_running.html.erb
123
124
  - app/views/maintenance_tasks/runs/info/_succeeded.html.erb
125
+ - app/views/maintenance_tasks/tasks/_custom.html.erb
124
126
  - app/views/maintenance_tasks/tasks/_task.html.erb
125
127
  - app/views/maintenance_tasks/tasks/index.html.erb
126
128
  - app/views/maintenance_tasks/tasks/show.html.erb
@@ -128,6 +130,7 @@ files:
128
130
  - db/migrate/20201211151756_create_maintenance_tasks_runs.rb
129
131
  - db/migrate/20210225152418_remove_index_on_task_name.rb
130
132
  - db/migrate/20210517131953_add_arguments_to_maintenance_tasks_runs.rb
133
+ - db/migrate/20211210152329_add_lock_version_to_maintenance_tasks_runs.rb
131
134
  - exe/maintenance_tasks
132
135
  - lib/generators/maintenance_tasks/install_generator.rb
133
136
  - lib/generators/maintenance_tasks/task_generator.rb
@@ -144,7 +147,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
144
147
  licenses:
145
148
  - MIT
146
149
  metadata:
147
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.6.0
150
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.7.0
148
151
  allowed_push_host: https://rubygems.org
149
152
  post_install_message:
150
153
  rdoc_options: []