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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ac2fd148131a277d37ddc609173b8549d01d3e6a9f78786cf729bd5da4f65b0
4
- data.tar.gz: a2db7dfe8a9b0a958d943f88305fbe6d8988fd171901c49cd688070d8125124e
3
+ metadata.gz: d307d92f96c1d1975cc3fbefcddf6ec2e11086097437b63594090c5bc524eea8
4
+ data.tar.gz: 9176d7a34c4a54f450601ea2748a88b1a9f134c9e416e97978e613bf3fbf1538
5
5
  SHA512:
6
- metadata.gz: c8e4c707d55ca479cca9f1592cc65359cc460d70cfd1e4ea4b2caf7c0a92617b4f243feb83c9f3657cb81a76691ed660c91bdafba73634b819a0fa44a719a2c8
7
- data.tar.gz: 0efab90ffbdea1131f72de57443c8ed01fc30550d88c3ebbfe77b8e48a37c27dec8ec2ed7591ebdf9ea463e82ff4df7fcb649b111076a7302e10fce5720eb437
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](https://guides.rubyonrails.org/error_reporting.html) to report errors. If you are using a bug
78
- tracking service you may want to subscribe to the reporter. See [Reporting Errors](#reporting-errors)
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
- - either by [using the included web UI](#running-a-task-from-the-web-ui),
118
- - or by [using the command line](#running-a-task-from-the-command-line),
119
- - or by [using Ruby](#running-a-task-from-ruby).
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
- - either by using the included web UI,
122
- - or by manually checking your task’s run’s status in your database.
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` macro:
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 Array.
506
- * Callable objects that receive one argument, the Task instance, and return an Array.
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
- middleware. The engine also defines a strict [Content Security Policy][], make
904
- sure to include `ActionDispatch::ContentSecurityPolicy::Middleware` in your
905
- app's middleware stack to ensure the CSP is delivered to the user's browser.
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] Rails
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 minutes.
948
- This is [configured in the `job-iteration` gem][max-job-runtime] and can be
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 your
997
- application. See the [Error Reporting in Rails Applications](https://guides.rubyonrails.org/error_reporting.html) guide for more details.
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
- error:
1004
- * `task_name`: The name of the Task that errored
1005
- * `started_at`: The time the Task started
1006
- * `ended_at`: The time the Task errored
1007
- * `run_id`: The id of the errored Task run
1008
- * `tick_count`: The tick count at the time of the error
1009
- * `errored_element`: The element, if any, that was being processed when the Task
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
- your Task failed).
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 application
1023
- and iteration will stop. However, you may want to handle some errors and continue
1024
- iteration. `MaintenanceTasks::Task.report_on` can be used to rescue certain
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 use
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.style_src(
11
+ policy.style_src_elem(
12
12
  BULMA_CDN,
13
- # ruby syntax highlighting
14
- "'sha256-y9V0na/WU44EUNI/HDP7kZ7mfEci4PAOIjYOOan6JMA='",
13
+ # <style> tag in app/views/layouts/maintenance_tasks/application.html.erb
14
+ "'sha256-WHHDQLdkleXnAN5zs0GDXC5ls41CHUaVsJtVpaNx+EM='",
15
15
  )
16
- policy.script_src(
17
- # page refresh script
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(status.capitalize, class: ["tag"] + STATUS_COLOURS.fetch(status))
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 task attribute.
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 task_class [Class<Task>] The task class for which the value needs to be resolved.
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
- return form_builder.select(parameter_name, inclusion_values, prompt: "Select a value") if inclusion_values
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
- def report_on(*exceptions)
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.where(id: completed_runs.select("MAX(id) as id").group(:task_name))
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-dark is-spaced" role="navigation" aria-label="main navigation">
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, 'npm/bulma@0.9.4/css/bulma.css'),
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: 'sha384-qQlNh1kc0FyhUqUDXKkl5wpiiSm8PXQw2ZWhAVfU46tmdMDfq2vXG2CXWYT+Dls3',
21
- crossorigin: 'anonymous') unless request.xhr?
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,4 +1,4 @@
1
1
  <% if arguments.present? %>
2
- <h6 class="title is-6">Arguments:</h6>
2
+ <h6 class="title is-6 has-text-weight-medium">Arguments:</h6>
3
3
  <%= render "maintenance_tasks/runs/serializable", serializable: arguments %>
4
4
  <% end %>
@@ -1,9 +1,15 @@
1
- <div class="box">
2
- <h5 class="title is-5">
3
- <%= time_tag run.created_at, title: run.created_at.utc %>
4
- <%= status_tag run.status %>
5
- <span class="is-pulled-right" title="Run ID">#<%= run.id %></span>
6
- </h5>
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.arguments %>
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
- </div>
51
+ </details>
@@ -1,24 +1,21 @@
1
1
  <% if serializable.present? %>
2
2
  <% case serializable %>
3
3
  <% when Hash %>
4
- <div class="table-container">
5
- <table class="table">
6
- <tbody>
7
- <% serializable.transform_values(&:to_s).each do |key, value| %>
8
- <tr>
9
- <td class="is-family-monospace"><%= key %></td>
10
- <td>
11
- <% next if value.empty? %>
12
- <% if value.include?("\n") %>
13
- <pre><%= value %></pre>
14
- <% else %>
15
- <code><%= value %></code>
16
- <% end %>
17
- </td>
18
- </tr>
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-3">
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.arguments %>
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-3">Active Tasks</h3>
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-3">New Tasks</h3>
17
- <%= render partial: 'task', collection: new_tasks %>
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-3">Completed Tasks</h3>
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-1">
3
+ <h1 class="title is-3 has-text-weight-bold">
4
4
  <%= @task %>
5
5
  </h1>
6
6
 
7
- <div class="buttons">
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="block">
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="block">
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="field">
21
- <%= ff.label parameter_name, parameter_name, class: "label is-family-monospace" %>
22
- <div class="control">
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-4">Previous Runs</h4>
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
@@ -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.11.0
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: 2025-01-28 00:00:00.000000000 Z
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.11.0
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.3
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: []