spree_core 6.0.0.rc7 → 6.0.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/jobs/spree/maintenance_tasks/run_job.rb +321 -0
  3. data/app/jobs/spree/seller_transfers/execute_pending_due_job.rb +7 -5
  4. data/app/jobs/spree/seller_transfers/execute_pending_job.rb +37 -17
  5. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/backfill_order_markets.rb +48 -0
  6. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/capture_methods.rb +40 -0
  7. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/deliveries.rb +15 -0
  8. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/order_coupon_codes.rb +15 -0
  9. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/promotion_option_value_rules.rb +15 -0
  10. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/rake_step.rb +83 -0
  11. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/returns.rb +20 -0
  12. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/store_regional_settings.rb +63 -0
  13. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade/users_to_customers.rb +28 -0
  14. data/app/maintenance_tasks/spree/maintenance_tasks/upgrade_step.rb +44 -0
  15. data/app/models/concerns/spree/translatable_resource.rb +19 -0
  16. data/app/models/spree/ability.rb +9 -0
  17. data/app/models/spree/invitation.rb +11 -2
  18. data/app/models/spree/maintenance_task.rb +221 -0
  19. data/app/models/spree/maintenance_task_run.rb +78 -0
  20. data/app/models/spree/order_routing/strategy/rules.rb +8 -48
  21. data/app/models/spree/seller_transfer.rb +51 -0
  22. data/app/models/spree/stock/coordinator.rb +37 -17
  23. data/app/models/spree/stock/packer.rb +19 -9
  24. data/app/models/spree/stock/prioritizer.rb +30 -15
  25. data/app/models/spree/store.rb +35 -2
  26. data/app/models/spree/upgrade_record.rb +62 -0
  27. data/app/services/spree/carts/update.rb +2 -1
  28. data/app/services/spree/maintenance_tasks/argument_parser.rb +23 -0
  29. data/app/services/spree/maintenance_tasks/cancel.rb +51 -0
  30. data/app/services/spree/maintenance_tasks/start.rb +127 -0
  31. data/app/services/spree/seeds/all.rb +22 -0
  32. data/app/services/spree/stock_reservations/reserve.rb +73 -25
  33. data/app/workflows/spree/seller_transfers/reverse.rb +5 -44
  34. data/config/locales/en.yml +1 -0
  35. data/db/migrate/20260813130001_add_capture_method_to_payment_methods.rb +1 -1
  36. data/db/migrate/20260815000001_create_spree_maintenance_task_runs.rb +40 -0
  37. data/db/migrate/20260816000001_create_spree_upgrade_records.rb +17 -0
  38. data/db/migrate/20261029100001_add_lockout_columns_to_spree_admin_users.rb +8 -0
  39. data/lib/generators/spree/api_resource/api_resource_generator.rb +2 -2
  40. data/lib/spree/checkout/default_requirements.rb +33 -5
  41. data/lib/spree/core/configuration.rb +6 -0
  42. data/lib/spree/core/engine.rb +10 -1
  43. data/lib/spree/core/permissions/default_catalog.rb +7 -0
  44. data/lib/spree/core/version.rb +1 -1
  45. data/lib/spree/core.rb +17 -1
  46. data/lib/spree/testing_support/factories/maintenance_task_run_factory.rb +7 -0
  47. data/lib/spree/time_zones.rb +34 -0
  48. data/lib/spree/upgrade.rb +189 -0
  49. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +30 -10
  50. data/lib/tasks/maintenance_tasks.rake +42 -0
  51. data/lib/tasks/upgrade.rake +81 -60
  52. metadata +28 -6
  53. data/lib/tasks/capture_methods_migration.rake +0 -30
  54. data/lib/tasks/order_market_backfill.rake +0 -27
@@ -0,0 +1,189 @@
1
+ module Spree
2
+ # Reads the upgrade manifests shipped inside spree_core — the list of data
3
+ # steps an installation must run after `db:migrate` for a given release
4
+ # boundary (docs/plans/6.0-maintenance-tasks.md).
5
+ #
6
+ # Extracted from lib/tasks/upgrade.rake so the dashboard and the maintenance
7
+ # task runner can read the same manifests the rake walk does; the rake file
8
+ # keeps the walking and printing.
9
+ module Upgrade
10
+ # The first release whose upgrade steps run as maintenance tasks. Nothing
11
+ # recorded the steps of earlier releases, so the dashboard starts here; the
12
+ # command line still walks every manifest.
13
+ FIRST_TRACKED_RELEASE = '6.0'.freeze
14
+
15
+ # Two-segment "5.5" form of the installed Spree version.
16
+ #
17
+ # @return [String]
18
+ def self.installed_minor_version
19
+ Spree.version.split('.').first(2).join('.')
20
+ end
21
+
22
+ # Root directory containing N_M_to_O_P/manifest.yml files inside the
23
+ # spree_core gem.
24
+ #
25
+ # @return [String]
26
+ def self.manifests_root
27
+ File.expand_path('upgrades', __dir__)
28
+ end
29
+
30
+ # All available manifest directories, parsed into { from:, to:, dir: }.
31
+ # Sorted by `to` ascending, with `from` as a tiebreaker (smallest first)
32
+ # for the rare case where two manifests share a `to` boundary.
33
+ #
34
+ # @return [Array<Hash>]
35
+ def self.available_manifests
36
+ Dir.glob(File.join(manifests_root, '*_to_*')).filter_map do |dir|
37
+ name = File.basename(dir)
38
+ match = name.match(/\A([\d_]+)_to_([\d_]+)\z/)
39
+ next unless match
40
+
41
+ { from: match[1].tr('_', '.'), to: match[2].tr('_', '.'), dir: dir }
42
+ end.sort_by { |manifest| version_parts(manifest[:to]) + version_parts(manifest[:from]) }
43
+ end
44
+
45
+ # Every manifest with its steps loaded, in walk order. Read once per
46
+ # process: the files ship inside the gem and change only with a deploy.
47
+ #
48
+ # @return [Array<Hash>]
49
+ def self.manifests
50
+ @manifests ||= available_manifests.map do |entry|
51
+ YAML.safe_load_file(File.join(entry[:dir], 'manifest.yml')).freeze
52
+ end.freeze
53
+ end
54
+
55
+ # Every step of every manifest, in walk order, each carrying the manifest
56
+ # boundary it belongs to so callers can group without re-reading the files.
57
+ #
58
+ # @return [Array<Hash>]
59
+ def self.steps
60
+ @steps ||= manifests.flat_map do |manifest|
61
+ manifest['steps'].map do |step|
62
+ step.merge('from' => manifest['from'], 'to' => manifest['to'], 'docs' => manifest['docs'])
63
+ end
64
+ end.freeze
65
+ end
66
+
67
+ # Whether a step belongs to a release whose runs are recorded, and so has a
68
+ # place in the dashboard's checklist.
69
+ #
70
+ # @param step [Hash] a manifest step
71
+ # @return [Boolean]
72
+ def self.tracked?(step)
73
+ compare(step['to'], FIRST_TRACKED_RELEASE) >= 0
74
+ end
75
+
76
+ # The task a step runs: its own class, or the shared runner for a step
77
+ # that names a rake task.
78
+ #
79
+ # @param step [Hash] a manifest step
80
+ # @return [String] a task class name
81
+ def self.task_class_name(step)
82
+ step['task_class'] || 'Spree::MaintenanceTasks::UpgradeStep'
83
+ end
84
+
85
+ # The arguments that start a step's task: a rake-backed step is named by
86
+ # its id, so the shared runner knows which rake task to invoke.
87
+ #
88
+ # @param step [Hash] a manifest step
89
+ # @return [Hash]
90
+ def self.task_arguments(step)
91
+ step['task_class'] ? {} : { 'step_id' => step['id'] }
92
+ end
93
+
94
+ # @param step_id [String]
95
+ # @return [Hash, nil]
96
+ def self.find_step(step_id)
97
+ return nil if step_id.blank?
98
+
99
+ steps.find { |step| step['id'] == step_id.to_s }
100
+ end
101
+
102
+ # The boundary this installation has already completed, and therefore the
103
+ # point a pending list starts from.
104
+ #
105
+ # Falls back to the release before the installed one when nothing has been
106
+ # stamped: an installation running 6.0 has, by far most often, come from
107
+ # 5.6, and showing every step of every manifest ever written is worse than
108
+ # showing the one boundary that is almost certainly right. A store that
109
+ # skipped releases runs the earlier steps from the command line, which
110
+ # walks every manifest whatever the boundary.
111
+ #
112
+ # @return [String, nil]
113
+ def self.completed_boundary
114
+ Spree::UpgradeRecord.current_version || previous_boundary
115
+ end
116
+
117
+ # The `to` of the manifest immediately below the installed version.
118
+ #
119
+ # @return [String, nil]
120
+ def self.previous_boundary
121
+ boundaries = manifests.map { |manifest| manifest['from'] }.uniq
122
+ boundaries.select { |version| compare(version, installed_minor_version) < 0 }.
123
+ max_by { |version| version_parts(version) }
124
+ end
125
+
126
+ # Whether an upgrade applies to this installation at all.
127
+ #
128
+ # False for a store installed fresh at its current version: it has no
129
+ # historical data to convert, so neither the outstanding steps nor the
130
+ # history of past releases describes anything real for it. True again
131
+ # once a later release ships steps it has not run.
132
+ #
133
+ # @return [Boolean]
134
+ def self.relevant?
135
+ !Spree::UpgradeRecord.exists? || Spree::UpgradeRecord.upgraded? || pending_steps.any?
136
+ end
137
+
138
+ # Whether a step can run here. An optional step belongs to a gem that may
139
+ # not be installed — a payment gateway, for instance — and is skipped
140
+ # where it is absent rather than offered as work that cannot start.
141
+ #
142
+ # @param step [Hash] a manifest step
143
+ # @return [Boolean]
144
+ def self.available?(step)
145
+ return true unless step['optional']
146
+ return Spree::MaintenanceTask.find_registered(step['task_class']).present? if step['task_class']
147
+
148
+ Spree::MaintenanceTask.new.rake_file_defining(step['task']).present?
149
+ end
150
+
151
+ # Steps this installation still has to run: every manifest above the
152
+ # boundary it has completed, up to and including the installed version.
153
+ #
154
+ # @return [Array<Hash>]
155
+ def self.pending_steps
156
+ boundary = completed_boundary
157
+
158
+ steps.select do |step|
159
+ next false if compare(step['to'], installed_minor_version) > 0
160
+ next false unless available?(step)
161
+ next true if boundary.nil?
162
+
163
+ compare(step['from'], boundary) >= 0
164
+ end
165
+ end
166
+
167
+ # Steps of manifests below the completed boundary — the ones a fresh or
168
+ # already-upgraded installation does not need. When the boundary was
169
+ # assumed rather than recorded the dashboard says so, and points a store
170
+ # that skipped a release at the command line, which still runs them.
171
+ #
172
+ # @return [Array<Hash>]
173
+ def self.superseded_steps
174
+ boundary = completed_boundary
175
+ return [] if boundary.nil?
176
+
177
+ steps.select { |step| compare(step['from'], boundary) < 0 }
178
+ end
179
+
180
+ def self.version_parts(version)
181
+ version.split('.').map { |segment| Integer(segment, 10) rescue 0 }
182
+ end
183
+
184
+ # Compare two dotted-version strings (returns -1, 0, +1).
185
+ def self.compare(first, second)
186
+ version_parts(first) <=> version_parts(second)
187
+ end
188
+ end
189
+ end
@@ -12,8 +12,15 @@
12
12
  # live in the upgrade doc at docs/developer/upgrades/5.6-to-6.0.mdx.
13
13
  #
14
14
  # This manifest is the machine-runnable shape — what `bin/rake spree:upgrade`
15
- # (in production) and `spree upgrade` (in dev) execute. Every step must be a
16
- # rake task and must be idempotent. Re-running the full manifest is safe.
15
+ # (in production) and `spree upgrade` (in dev) execute. Every step must be
16
+ # idempotent; re-running the full manifest is safe.
17
+ #
18
+ # Steps name a `task_class` — a Spree::MaintenanceTask that the walk runs
19
+ # through the maintenance task runner, so a shell walk and a dashboard run
20
+ # leave the same audit record and the same resume behaviour
21
+ # (docs/plans/6.0-maintenance-tasks.md). Released manifests keep their `task`
22
+ # rake names, which have shipped and may be scripted; the walker runs both
23
+ # kinds.
17
24
  ---
18
25
  from: "5.6"
19
26
  to: "6.0"
@@ -151,7 +158,7 @@ steps:
151
158
 
152
159
  - id: migrate_users_to_customers
153
160
  name: "Move users onto the gem-owned Customer/AdminUser models"
154
- task: "spree:upgrade:migrate_users_to_customers"
161
+ task_class: "Spree::MaintenanceTasks::Upgrade::UsersToCustomers"
155
162
  notes: |
156
163
  Copies the legacy Devise user table (default spree_users) into
157
164
  spree_customers, preserving ids so the renamed customer_id foreign keys
@@ -261,7 +268,7 @@ steps:
261
268
 
262
269
  - id: migrate_deliveries
263
270
  name: "Move tracking numbers onto deliveries"
264
- task: "spree:migrate_deliveries"
271
+ task_class: "Spree::MaintenanceTasks::Upgrade::Deliveries"
265
272
  notes: |
266
273
  Every fulfillment carrying a 5.6 tracking number gets one delivery row
267
274
  with that number: the carrier axis (tracking number, carrier, carrier
@@ -322,7 +329,7 @@ steps:
322
329
 
323
330
  - id: migrate_promotion_option_value_rules
324
331
  name: "Rewrite Option Value promotion rules to stable option value ids"
325
- task: "spree:upgrade:migrate_promotion_option_value_rules"
332
+ task_class: "Spree::MaintenanceTasks::Upgrade::PromotionOptionValueRules"
326
333
  notes: |
327
334
  Spree 6.0 stores Option Value promotion rules against Spree::OptionValue
328
335
  ids. Upgrades from 5.6 may still carry join-row ids from
@@ -365,7 +372,7 @@ steps:
365
372
 
366
373
  - id: migrate_returns
367
374
  name: "Move returns onto the new returns models"
368
- task: "spree:upgrade:migrate_returns"
375
+ task_class: "Spree::MaintenanceTasks::Upgrade::Returns"
369
376
  notes: |
370
377
  Converts each ReturnAuthorization into a Spree::Return, or a
371
378
  Spree::Exchange when any of its return items named an exchange variant,
@@ -494,7 +501,7 @@ steps:
494
501
 
495
502
  - id: backfill_order_coupon_codes
496
503
  name: "Backfill coupon codes on placed orders"
497
- task: "spree:backfill_order_coupon_codes"
504
+ task_class: "Spree::MaintenanceTasks::Upgrade::OrderCouponCodes"
498
505
  notes: |
499
506
  spree_orders gains a coupon_code column in Spree 6, matching carts.
500
507
  Historical placed orders applied coupons only through the promotion
@@ -510,7 +517,7 @@ steps:
510
517
 
511
518
  - id: backfill_order_markets
512
519
  name: "Backfill order markets"
513
- task: "spree:backfill_order_markets"
520
+ task_class: "Spree::MaintenanceTasks::Upgrade::BackfillOrderMarkets"
514
521
  notes: |
515
522
  Market is mandatory in Spree 6. Assigns the store's default market to
516
523
  orders that predate markets. Channel needs no step here — the 5.4→5.5
@@ -573,7 +580,7 @@ steps:
573
580
 
574
581
  - id: migrate_capture_methods
575
582
  name: "Convert payment method auto_capture into capture_method"
576
- task: "spree:migrate_capture_methods"
583
+ task_class: "Spree::MaintenanceTasks::Upgrade::CaptureMethods"
577
584
  notes: |
578
585
  Payment methods say when they charge with a capture_method string
579
586
  (checkout, on_dispatch or manual) rather than an auto_capture boolean,
@@ -590,6 +597,19 @@ steps:
590
597
  is dropped in 6.1. Idempotent — only rows without a capture_method are
591
598
  written.
592
599
 
600
+ - id: store_regional_settings
601
+ name: "Convert store time zones and admin languages to standard codes"
602
+ task_class: "Spree::MaintenanceTasks::Upgrade::StoreRegionalSettings"
603
+ notes: |
604
+ A store's time zone is now always an IANA name such as America/New_York,
605
+ and its admin language a locale code Spree lists, the forms the API
606
+ accepts and browsers understand. Rails time zone names such as
607
+ "Eastern Time (US & Canada)" and locale spellings such as en_US are
608
+ converted. A time zone that names no zone is reset to the store default,
609
+ which is what the store already ran on; an admin language Spree does not
610
+ list is cleared, so staff see their own language. Idempotent — stores
611
+ already on standard codes are skipped.
612
+
593
613
  - id: product_types_backfill
594
614
  name: "Backfill product type store ownership"
595
615
  task: "spree:product_types:backfill"
@@ -625,7 +645,7 @@ steps:
625
645
 
626
646
  - id: migrate_stripe_webhook_keys
627
647
  name: "Move Stripe webhook signing secrets onto the gateway"
628
- task: "spree:upgrade:migrate_stripe_webhook_keys"
648
+ task_class: "SpreeStripe::MaintenanceTasks::MigrateWebhookKeys"
629
649
  optional: true
630
650
  notes: |
631
651
  Only runs when spree_stripe is installed; skipped otherwise.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Command-line entry to the same runner the dashboard drives, so a shell run
4
+ # is audited exactly like an operator's (docs/plans/6.0-maintenance-tasks.md).
5
+ #
6
+ # bin/rake "spree:maintenance_tasks:perform[backfill_order_markets]"
7
+ # DRY_RUN=1 bin/rake "spree:maintenance_tasks:perform[capture_methods]"
8
+ # ARGS='batch_size=200' bin/rake "spree:maintenance_tasks:perform[deliveries]"
9
+ namespace :spree do
10
+ namespace :maintenance_tasks do
11
+ desc 'Run a maintenance task synchronously'
12
+ task :perform, [:task_name] => :environment do |_task, args|
13
+ task_name = args[:task_name] || ENV['TASK']
14
+ abort ' Pass a task name: rake "spree:maintenance_tasks:perform[Your::Task]"' if task_name.blank?
15
+
16
+ # Either the class name or the short name the dashboard shows.
17
+ lookup = Spree::MaintenanceTask.find_registered(task_name) ? { task_name: task_name } : { api_type: task_name }
18
+
19
+ result = Spree::MaintenanceTasks::Start.call(
20
+ **lookup,
21
+ arguments: Spree::MaintenanceTasks::ArgumentParser.parse(ENV['ARGS']),
22
+ dry_run: ENV['DRY_RUN'] == '1',
23
+ initiated_via: 'cli',
24
+ inline: true
25
+ )
26
+
27
+ abort " #{result.error}" if result.failure?
28
+
29
+ run = result.value.reload
30
+ puts " #{run.task_name}: #{run.status} — #{run.tick_count} processed#{run.dry_run? ? ' (dry run)' : ''}"
31
+ (run.tallies || {}).each { |key, value| puts " #{key}: #{value}" }
32
+
33
+ if run.errored?
34
+ puts " #{run.error_class}: #{run.error_message}"
35
+ abort ' The run failed. Fix the cause, then re-run this task.'
36
+ end
37
+
38
+ # A refused duplicate or a cancellation did not do the work either.
39
+ abort " The run did not finish (#{run.status})." unless run.succeeded?
40
+ end
41
+ end
42
+ end
@@ -33,41 +33,10 @@ namespace :spree do
33
33
  end
34
34
  end
35
35
 
36
+ require 'spree/upgrade'
37
+
36
38
  module Spree
37
39
  module Upgrade
38
- # Two-segment "5.5" form of the installed Spree version.
39
- def self.installed_minor_version
40
- Spree.version.split('.').first(2).join('.')
41
- end
42
-
43
- # Root directory containing N_M_to_O_P/manifest.yml files inside
44
- # the spree_core gem.
45
- def self.manifests_root
46
- File.expand_path('../spree/upgrades', __dir__)
47
- end
48
-
49
- # All available manifest directories, parsed into { from:, to:, dir: }.
50
- # Sorted by `to` ascending, with `from` as a tiebreaker (smallest first)
51
- # for the rare case where two manifests share a `to` boundary.
52
- def self.available_manifests
53
- Dir.glob(File.join(manifests_root, '*_to_*')).filter_map do |dir|
54
- name = File.basename(dir)
55
- match = name.match(/\A([\d_]+)_to_([\d_]+)\z/)
56
- next unless match
57
-
58
- { from: match[1].tr('_', '.'), to: match[2].tr('_', '.'), dir: dir }
59
- end.sort_by { |m| version_parts(m[:to]) + version_parts(m[:from]) }
60
- end
61
-
62
- def self.version_parts(v)
63
- v.split('.').map { |s| Integer(s, 10) rescue 0 }
64
- end
65
-
66
- # Compare two dotted-version strings (returns -1, 0, +1).
67
- def self.compare(a, b)
68
- version_parts(a) <=> version_parts(b)
69
- end
70
-
71
40
  # The runner is a class (not a method) so individual concerns (selection,
72
41
  # rendering, invocation) stay separable and the plan can be inspected
73
42
  # without execution.
@@ -100,13 +69,7 @@ module Spree
100
69
  # Plan mode selects exactly what a real run executes, so a dry run after
101
70
  # bumping the gem lists every backfill the upgrade is about to perform.
102
71
  def eligible_manifests
103
- Spree::Upgrade.available_manifests
104
- .select { |manifest| Spree::Upgrade.compare(manifest[:to], target_version) <= 0 }
105
- .map { |entry| load_manifest_yaml(entry) }
106
- end
107
-
108
- def load_manifest_yaml(entry)
109
- YAML.safe_load_file(File.join(entry[:dir], 'manifest.yml'))
72
+ Spree::Upgrade.manifests.select { |manifest| Spree::Upgrade.compare(manifest['to'], target_version) <= 0 }
110
73
  end
111
74
 
112
75
  def run_full_walk(manifests)
@@ -122,10 +85,22 @@ module Spree
122
85
  end
123
86
  end
124
87
 
88
+ stamp_completed_boundaries(manifests) unless dry_run
89
+
125
90
  puts
126
91
  puts dry_run ? ' (dry run — nothing executed)' : ' Upgrade tasks complete.'
127
92
  end
128
93
 
94
+ # Records the boundaries this walk finished, so a later dashboard visit
95
+ # shows only what is still outstanding rather than every step of every
96
+ # manifest ever written. A partial walk stamps nothing — `invoke` aborts
97
+ # on a failed step, so reaching here means every step succeeded.
98
+ def stamp_completed_boundaries(manifests)
99
+ manifests.each do |manifest|
100
+ Spree::UpgradeRecord.stamp!(manifest['to'], source: 'walk')
101
+ end
102
+ end
103
+
129
104
  # STEP=<id> looks across every eligible manifest; we need exactly one
130
105
  # match. Multiple matches are likely a manifest bug (two manifests
131
106
  # referencing the same step id) but we surface it rather than picking
@@ -162,38 +137,84 @@ module Spree
162
137
  puts
163
138
  puts " Step #{index}/#{total} [#{step['id']}]"
164
139
  puts " #{step['name']}"
165
- puts " > bin/rake #{step['task']}"
140
+ puts " > #{step_command(step)}"
166
141
  return unless step['notes']
167
142
 
168
143
  step['notes'].each_line { |line| puts " #{line.chomp}" }
169
144
  end
170
145
 
171
146
  def print_step_complete(step)
172
- puts " ✓ #{step['task']} done."
147
+ puts " ✓ #{step_label(step)} done."
173
148
  end
174
149
 
150
+ def step_label(step)
151
+ step['task_class'] || step['task']
152
+ end
153
+
154
+ def step_command(step)
155
+ if step['task_class']
156
+ "bin/rake \"spree:maintenance_tasks:perform[#{step['task_class']}]\""
157
+ else
158
+ "bin/rake #{step['task']}"
159
+ end
160
+ end
161
+
162
+ # Both step kinds run through the maintenance task runner, so a shell
163
+ # walk records the same audit rows a dashboard run does: a class-backed
164
+ # step runs its own task, a rake-backed step runs inside UpgradeStep.
165
+ #
166
+ # Returns false when the step was skipped, so the caller can leave the
167
+ # "done" line off a step that never ran.
175
168
  def invoke(step)
176
- task = step.fetch('task')
177
-
178
- # Steps contributed by optional gems (payment gateways, for instance)
179
- # are absent unless that gem is installed. Skipping keeps the upgrade
180
- # runnable on installs that never had the gem, while an install that
181
- # does have it still gets the backfill.
182
- if step['optional'] && !Rake::Task.task_defined?(task)
183
- puts " Skipping #{task} — not installed."
184
- return false
169
+ return false if skip_uninstalled?(step)
170
+
171
+ puts " Running #{step_label(step)}..."
172
+
173
+ result = Spree::MaintenanceTasks::Start.call(
174
+ task_name: Spree::Upgrade.task_class_name(step),
175
+ arguments: step_arguments(step),
176
+ initiated_via: 'cli',
177
+ inline: true
178
+ )
179
+
180
+ abort " #{result.error}" if result.failure?
181
+
182
+ run = result.value.reload
183
+ (run.tallies || {}).each { |key, value| puts " #{key}: #{value}" }
184
+
185
+ if run.errored?
186
+ puts " #{run.error_class}: #{run.error_message}"
187
+ abort ' Step failed. Fix the cause, then re-run this step.'
188
+ end
189
+
190
+ # Anything short of success — cancelled or still running — means
191
+ # the step's data is not converted, and the steps after it depend on it.
192
+ unless run.succeeded?
193
+ puts " The step did not finish (#{run.status})."
194
+ abort ' Re-run this step once it can finish.'
185
195
  end
186
196
 
187
- puts " Running #{task}..."
197
+ true
198
+ end
199
+
200
+ # A rake-backed step is named by id; a class-backed step that reads
201
+ # environment variables gets the ones the operator exported, so
202
+ # `CONFIRM_NO_PEPPER=true bin/rake spree:upgrade` reaches it.
203
+ def step_arguments(step)
204
+ return Spree::Upgrade.task_arguments(step) unless step['task_class']
205
+
206
+ task_class = Spree::MaintenanceTask.find_registered(step['task_class'])
207
+ task_class.respond_to?(:arguments_from_environment) ? task_class.arguments_from_environment : {}
208
+ end
209
+
210
+ # Steps contributed by optional gems (payment gateways, for instance)
211
+ # are absent unless that gem is installed. Skipping keeps the upgrade
212
+ # runnable on installs that never had the gem, while an install that
213
+ # does have it still gets the backfill.
214
+ def skip_uninstalled?(step)
215
+ return false if Spree::Upgrade.available?(step)
188
216
 
189
- # Rake caches invoked tasks in-process; explicit reenable lets a
190
- # single `rake spree:upgrade` run re-invoke aggregators that share
191
- # subtasks across multiple manifests (e.g. two manifests both
192
- # depending on `spree:install:migrations` would otherwise only
193
- # invoke it once).
194
- rake_task = Rake::Task[task]
195
- rake_task.reenable
196
- rake_task.invoke
217
+ puts " Skipping #{step_label(step)} — not installed."
197
218
  true
198
219
  end
199
220
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.rc7
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Schofield
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-10-26 00:00:00.000000000 Z
13
+ date: 2026-10-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n-tasks
@@ -745,6 +745,7 @@ files:
745
745
  - app/jobs/spree/imports/process_group_job.rb
746
746
  - app/jobs/spree/imports/process_job.rb
747
747
  - app/jobs/spree/imports/process_rows_job.rb
748
+ - app/jobs/spree/maintenance_tasks/run_job.rb
748
749
  - app/jobs/spree/orders/finalize_stale_drafts_job.rb
749
750
  - app/jobs/spree/payments/cancel_unused_sessions_job.rb
750
751
  - app/jobs/spree/payments/handle_webhook_job.rb
@@ -777,6 +778,16 @@ files:
777
778
  - app/mailers/spree/invitation_mailer.rb
778
779
  - app/mailers/spree/seller_user_mailer.rb
779
780
  - app/mailers/spree/webhook_mailer.rb
781
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/backfill_order_markets.rb
782
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/capture_methods.rb
783
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/deliveries.rb
784
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/order_coupon_codes.rb
785
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/promotion_option_value_rules.rb
786
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/rake_step.rb
787
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/returns.rb
788
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/store_regional_settings.rb
789
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade/users_to_customers.rb
790
+ - app/maintenance_tasks/spree/maintenance_tasks/upgrade_step.rb
780
791
  - app/models/concerns/spree/account_deactivation.rb
781
792
  - app/models/concerns/spree/account_lockout.rb
782
793
  - app/models/concerns/spree/acted_by.rb
@@ -1064,6 +1075,8 @@ files:
1064
1075
  - app/models/spree/label_purchase.rb
1065
1076
  - app/models/spree/line_item.rb
1066
1077
  - app/models/spree/locale.rb
1078
+ - app/models/spree/maintenance_task.rb
1079
+ - app/models/spree/maintenance_task_run.rb
1067
1080
  - app/models/spree/market.rb
1068
1081
  - app/models/spree/market_country.rb
1069
1082
  - app/models/spree/media.rb
@@ -1291,6 +1304,7 @@ files:
1291
1304
  - app/models/spree/taxonomy.rb
1292
1305
  - app/models/spree/translations/batch.rb
1293
1306
  - app/models/spree/update_status.rb
1307
+ - app/models/spree/upgrade_record.rb
1294
1308
  - app/models/spree/user_identity.rb
1295
1309
  - app/models/spree/variant.rb
1296
1310
  - app/models/spree/variant_media.rb
@@ -1389,6 +1403,9 @@ files:
1389
1403
  - app/services/spree/imports/row_processors/purchase_order.rb
1390
1404
  - app/services/spree/locales.rb
1391
1405
  - app/services/spree/locales/set_fallback_locale_for_store.rb
1406
+ - app/services/spree/maintenance_tasks/argument_parser.rb
1407
+ - app/services/spree/maintenance_tasks/cancel.rb
1408
+ - app/services/spree/maintenance_tasks/start.rb
1392
1409
  - app/services/spree/media/destroy.rb
1393
1410
  - app/services/spree/media/usage.rb
1394
1411
  - app/services/spree/newsletter/link_user.rb
@@ -1931,8 +1948,10 @@ files:
1931
1948
  - db/migrate/20260814000001_rename_stock_items_to_stock_levels.rb
1932
1949
  - db/migrate/20260814000002_add_typed_stock_movements.rb
1933
1950
  - db/migrate/20260815000001_add_audience_to_spree_refresh_tokens.rb
1951
+ - db/migrate/20260815000001_create_spree_maintenance_task_runs.rb
1934
1952
  - db/migrate/20260815000002_add_resource_to_spree_roles.rb
1935
1953
  - db/migrate/20260816000001_create_spree_sellers.rb
1954
+ - db/migrate/20260816000001_create_spree_upgrade_records.rb
1936
1955
  - db/migrate/20260816000002_add_seller_to_spree_products.rb
1937
1956
  - db/migrate/20260816210001_create_spree_commission_rates.rb
1938
1957
  - db/migrate/20260816210002_create_spree_commission_lines.rb
@@ -2009,6 +2028,7 @@ files:
2009
2028
  - db/migrate/20261017100001_add_tax_exemption_reason_to_spree_tax_exemption_certificates.rb
2010
2029
  - db/migrate/20261022000001_store_credit_actor_polymorphic.rb
2011
2030
  - db/migrate/20261023100001_add_deactivated_at_to_spree_admin_users.rb
2031
+ - db/migrate/20261029100001_add_lockout_columns_to_spree_admin_users.rb
2012
2032
  - db/sample_data/channels.rb
2013
2033
  - db/sample_data/collections.rb
2014
2034
  - db/sample_data/custom_field_definitions.rb
@@ -2198,6 +2218,7 @@ files:
2198
2218
  - lib/spree/testing_support/factories/integration_factory.rb
2199
2219
  - lib/spree/testing_support/factories/invitation_factory.rb
2200
2220
  - lib/spree/testing_support/factories/line_item_factory.rb
2221
+ - lib/spree/testing_support/factories/maintenance_task_run_factory.rb
2201
2222
  - lib/spree/testing_support/factories/market_factory.rb
2202
2223
  - lib/spree/testing_support/factories/media_factory.rb
2203
2224
  - lib/spree/testing_support/factories/newsletter_subscriber_factory.rb
@@ -2293,7 +2314,9 @@ files:
2293
2314
  - lib/spree/testing_support/store.rb
2294
2315
  - lib/spree/testing_support/tax_identifier_validators.rb
2295
2316
  - lib/spree/testing_support/url_helpers.rb
2317
+ - lib/spree/time_zones.rb
2296
2318
  - lib/spree/translations.rb
2319
+ - lib/spree/upgrade.rb
2297
2320
  - lib/spree/upgrades/5_4_to_5_5/manifest.yml
2298
2321
  - lib/spree/upgrades/5_5_to_5_6/manifest.yml
2299
2322
  - lib/spree/upgrades/5_6_to_6_0/manifest.yml
@@ -2308,7 +2331,6 @@ files:
2308
2331
  - lib/tasks/backfill_media_store_ids.rake
2309
2332
  - lib/tasks/backfill_reason_store_ids.rake
2310
2333
  - lib/tasks/backfill_tagging_tenants.rake
2311
- - lib/tasks/capture_methods_migration.rake
2312
2334
  - lib/tasks/carts_migration.rake
2313
2335
  - lib/tasks/categories_and_collections.rake
2314
2336
  - lib/tasks/channels.rake
@@ -2323,6 +2345,7 @@ files:
2323
2345
  - lib/tasks/fold_store_credit_categories.rake
2324
2346
  - lib/tasks/fulfillment_statuses_migration.rake
2325
2347
  - lib/tasks/inventory_operations_migration.rake
2348
+ - lib/tasks/maintenance_tasks.rake
2326
2349
  - lib/tasks/markets.rake
2327
2350
  - lib/tasks/media.rake
2328
2351
  - lib/tasks/media_class_rename.rake
@@ -2333,7 +2356,6 @@ files:
2333
2356
  - lib/tasks/migrate_users_to_customers.rake
2334
2357
  - lib/tasks/money.rake
2335
2358
  - lib/tasks/order_coupon_code_backfill.rake
2336
- - lib/tasks/order_market_backfill.rake
2337
2359
  - lib/tasks/package_types.rake
2338
2360
  - lib/tasks/preferences_json.rake
2339
2361
  - lib/tasks/price_history.rake
@@ -2378,9 +2400,9 @@ licenses:
2378
2400
  - BSD-3-Clause
2379
2401
  metadata:
2380
2402
  bug_tracker_uri: https://github.com/spree/spree/issues
2381
- changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.rc7
2403
+ changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.1
2382
2404
  documentation_uri: https://docs.spreecommerce.org/
2383
- source_code_uri: https://github.com/spree/spree/tree/v6.0.0.rc7
2405
+ source_code_uri: https://github.com/spree/spree/tree/v6.0.1
2384
2406
  post_install_message:
2385
2407
  rdoc_options: []
2386
2408
  require_paths: