spree-upgrade-5-to-6 について
Spree 5.x から 6.0 への移行手順をまとめた開発者向けガイドです。前提条件、移行とデータ補完、互換性を損なう変更、移行後の確認事項を扱います。
主な機能
- 01Spree 6.0 へのアップグレード前提
- 02順序に沿った移行とデータ補完
- 03破壊的変更の概要
- 04コード検索と修正の指針
- 05アップグレード後のデータ確認
活用シーン
- 01Spree 5.6 から 6.0 への移行計画
- 02Rails と拡張機能の依存関係の準備
- 03移行後の注文と顧客データの確認
- 04アプリ内の旧 API の特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ spree/ agent-skills --skill spree-upgrade-5-to-6インストール後、Claude Code で/spree-upgrade-5-to-6を入力して読み込まれたか確認します。
同梱ファイルreferences/grep-recipes.md
詳細
Spree 5.x から 6.0 へのアップグレード
Spree 6.0 は破壊的変更を含むリリースです。Cart と Order は別々のモデルになり、ステートマシンは廃止されました(ステータス+ワークフロー)。調整項目は型付きの行(TaxLine / Discount / Fee)になり、マスター商品バリエーションは廃止されました。配送はフルフィルメント/配達になり、タクソンはカテゴリー/コレクションになり、ユーザーは Spree::Customer になりました。スタッフの権限はデータになり、Rails 管理画面、Rails ストアフロント、API v2 は削除され、React ダッシュボード(spree_dashboard)と API v3 に置き換わりました。
アップグレードの手順は、前提条件 → gem → マイグレーション → バックフィル → コード修正 → 動作の確認です。最後の2つには十分な時間を確保してください。rake の作業は簡単な部分です。
一般的な仕組み(spree upgrade のフラグ、STEP=、本番環境のリリースフェーズ)については、spree-upgrade スキルを参照してください。このスキルは、6.0 固有の手順書です。
1. 前提条件 — Gemfile に手を加える前に実施してください
- 5.6.x にアップグレードし、完全に更新してください。 6.0 のマニフェストは 5.6 のスキーマを前提としています。まず 5.6 にアップグレードしてください(
spree upgrade --to 5.6を実行するか、~> 5.6に引き上げてbundle exec rake spree:upgradeを実行します)。6.0 の実行時にも 5.4→5.5 と 5.5→5.6 のバックフィルが再実行されますが、5.6 にアップグレードしておく必要がなくなるわけではありません。 - Rails 8.1。
spree_core6.0 にはrails >= 8.1, < 8.2、Ruby ≥ 3.2 が必要です。可能であれば、Rails のアップグレード(および Rails 独自の非推奨機能への対応)は別のコミットで行ってください。 - データベースをバックアップし、復元したコピーでリハーサルしてください。複数のバックフィルで金額、在庫、顧客のレコードが書き換えられるため、所要時間を計測してください。
- Gemfile の入れ替え:
spree_adminとspree_storefrontを削除します(廃止され、React ダッシュボードと Next.js ストアフロントに置き換えられました)。spree_dashboardを追加します(/dashboardで管理画面を提供します)。API のみのデプロイでは追加しないでください。Spree.search_providerが Meilisearch を参照している場合は、spree_meilisearchを追加します。- その他すべての
spree_*拡張機能(決済、i18n、独自のものなど)には 6.0 対応リリースが必要です。開始前に確認してください。確認しないとbundle updateで依存関係を解決できません。spree_stripe、spree_easypost、spree_meilisearchは現在、Spree モノレポから同じバージョンでリリースされています。
- Action Text / Action Cable。
spree_coreはこれらを必要としなくなりました。独自のコードでhas_rich_text、Action Text ヘルパー、または Action Cable を使用している場合は、config/application.rbにrequire 'action_text/engine'/require 'action_cable/engine'を追加してください。 - 必要であれば、先にリッチテキストの許可リストを拡張してください。 説明やメモは、ダッシュボードのエディターが出力する形式(段落、見出し、インライン書式、コード、引用、リスト、リンク、画像)に再サニタイズされます。テーブル、
div/span、インラインのstyle、任意のクラスは削除されます。これらが必要な場合は、migrate_rich_text_to_columnsの実行前、および 6.0 で何かが保存される前に、イニシャライザーでSpree::RichTextSanitizer.allowed_tags += %w[…]/allowed_attributes += %w[…]を設定してください。 - スタッフロールをデータとして再作成してください。 権限セットは移行処理なしで削除されます。既存のロールレコードには権限がありません。そのロールを持つスタッフはアクセスできなくなります(フェイルクローズ)。デプロイ前にシードしてください:
キーはカタログにあるstore.roles.find_or_create_by!(name: 'support').update!(permissions: %w[read_orders read_customers]) # roles belong to a storeread_<resource>/write_<resource>です(Spree::ApiKey.known_scopesで確認できます)。 - PermissionSets のイニシャライザーを削除してください。
Spree::PermissionSets::*はNameErrorを発生させ、Spree.permissions.assignは起動時にエラーになります。拡張機能ではSpree.permissions.register_scope(:reviews, group: :catalog, resources: -> { [SpreeReviews::Review] })でリソースを登録してください。 - 顧客の移行を確認してください(Devise アプリ)。
spree:upgrade:migrate_users_to_customersはspree_usersをspree_customersにコピーし、Devise のencrypted_passwordをpassword_digestにマッピングします。これは Devise の pepper が設定されていない場合に限り動作します。Devise が読み込まれなくなると、CONFIRM_NO_PEPPER=trueを渡すまで処理が中断されます。メールアドレスが空欄または重複しているレコードがある場合も中断されます(スキップするにはSKIP_INVALID_ROWS=trueを指定します)。pepper を使用している場合、SSO の場合、または bcrypt 以外を使用している場合は、代わりに独自のSpree.customer_classを引き続き使用してください。 - ストアに移動したイニシャライザー設定(
track_inventory_levels、auto_capture、companyなど)は、store_settings_backfill_from_configによってコピーされます。このステップが実行されるまでイニシャライザーに残し、実行後に削除してください。 - Active Record の暗号化を有効にしてください。 Spree 6 では、キーが設定されている場合に限り、Webhook 署名シークレット、ゲートウェイの顧客 ID、OAuth ID トークンが暗号化されます。キーを読み込むように
config/application.rbを設定してください(Applicationクラス内に記述します)。
キーが認証情報にしかなく、この設定がない場合、Webhook シークレットとゲートウェイの顧客 ID は平文のままです(それらの%i[primary_key deterministic_key key_derivation_salt].each do |key| value = ENV["ACTIVE_RECORD_ENCRYPTION_#{key.upcase}"].presence || credentials.dig(:active_record_encryption, key).presence config.active_record.encryption[key] = value if value endencryptsはconfig.active_record.encryptionを確認します)。環境ごとに個別のキーセットを生成し(開発環境の.envにはspree encryption init、本番環境にはspree encryption init --print/bin/rails db:encryption:init)、デプロイ前にホストに設定してバックアップし、決して変更しないでください。暗号化を有効にすると、既存の平文の Webhook シークレットとゲートウェイの顧客 ID が読み取れなくなります。まずconfig.active_record.encryption.support_unencrypted_data = trueとextend_queries = trueを設定してデプロイし、[Spree::WebhookEndpoint, Spree::GatewayCustomer, Spree::UserIdentity].each { |m| m.find_each(&:encrypt) }を実行してから、両方の設定を削除してください。spree-securityを参照してください。 -
settingsで作成されたシークレット API キーでは、/admin_users、/invitations、/rolesが使えなくなります。これらにはread_staff/write_staffが必要になりました。スタッフを管理するインテグレーションキーを再作成してください。
2. アップグレードを実行する
# create-spree-app (ejected dev stack; Rails app in server/, older projects backend/)
spree upgrade
spree upgrade --plan # after the gem bump: list every step
# classic Rails app / no CLI
bundle update
bundle exec rake spree:install:migrations && bin/rails db:migrate
DRY_RUN=1 bundle exec rake spree:upgrade
CONFIRM_NO_PEPPER=true bundle exec rake spree:upgrade
本番環境:デプロイで db:migrate を実行し、その後リリースフェーズで bundle exec rake spree:upgrade を実行します(該当する場合は、こちらでも CONFIRM_NO_PEPPER=true を渡してください)。メンテナンス時間を確保してください。バックフィルによって、処理中のチェックアウトはカートに変換されます。
3. 5.6 → 6.0 のマニフェストで実行される処理(順番)
spree_core/lib/spree/upgrades/5_6_to_6_0/manifest.yml を参照してください。すべて冪等です。spree upgrade --step <id> で個別の処理を再実行できます。
| # | ステップ ID | 内容 |
|---|---|---|
| 1 | consolidate_metadata | セーフティネット:残っている public_metadata/private_metadata のペアを、単一の metadata カラムに統合します |
| 2 | migrate_media_class_names | 保存されている Spree::Asset/Spree::Image のクラス名を Spree::Media に書き換えます |
| 3 | migrate_master_images_to_product_media | バリアントに固定された画像を商品ギャラリーに移動します(ジョブをエンキューするため、処理が完了するまで待ちます) |
| 4 | backfill_media_store_ids | すべてのメディア行に store_id を設定します |
| 5 | remove_master_variant | default_variant_id をバックフィルし、マスターバリアントを廃止します |
| 6 | migrate_taxons_to_categories_and_collections | 自動タクソンを Spree::Collection に移行し、その他は Spree::Category のままにして、Spree::Taxon* 文字列を書き換えます |
| 7 | backfill_library_media_placements | カテゴリー/コレクションの画像をメディアライブラリに追加します |
| 8 | migrate_users_to_customers | spree_users → spree_customers に移行し、user_type カラムの参照先を変更します(Devise pepper の前提条件を参照) |
| 9 | migrate_rich_text_to_columns | Action Text の本文をサニタイズしてテキストカラムに移行します |
| 10 | migrate_shipping_to_delivery | Shipment の文字列/ステータスをフルフィルメントに、フルフィルメントプロバイダーを移行し、display_on を storefront_visible に変更します |
| 11 | migrate_delivery_profiles | 配送カテゴリーをストア所有の配送プロファイルに移行します |
| 12 | migrate_fulfillment_statuses | unfulfilled/fulfilled/delivered に再マッピングします |
| 13 | migrate_deliveries | 追跡番号を spree_deliveries の行に移行します |
| 14 | migrate_zones_to_delivery_zones | 配送で参照されるゾーンを Spree::DeliveryZone に移行します |
| 15 | migrate_country_state_codes | ISO の country_code/state_code カラムを埋めます(国/州は現在、参照データです) |
| 16 | migrate_promotion_option_value_rules | オプション値のプロモーションルールを、安定したオプション値 ID に移行します |
| 17 | migrate_adjustments_to_typed_rows | spree_adjustments を TaxLine/Discount/Fee に移行し、照合できない注文にフラグを設定します |
| 18 | backfill_reason_store_ids | 返品/返金理由をストア所有にします |
| 19 | migrate_returns | ReturnAuthorization を Spree::Return/Spree::Exchange に移行します(失敗した行がある場合は中断します。SKIP_FAILED_ROWS=true) |
| 20 | migrate_stock_movements_to_typed_rows | 在庫移動を型付きにし、未完了のフルフィルメントを再割り当てします(在庫は出荷時に減るようになりました) |
| 21 | backfill_delivery_and_stock_store_ids | 配送方法と在庫ロケーションをストア所有にします |
| 22 | migrate_tax_zones | 税率ゾーンを税率の国/州に移行します |
| 23 | backfill_tax_store_ids | 税率/税カテゴリーをストア所有にします |
| 24 | backfill_order_coupon_codes | 過去に確定した注文の新しい spree_orders.coupon_code を埋めます |
| 25 | backfill_order_markets | 注文にデフォルトのマーケットを割り当てます |
| 26 | migrate_incomplete_orders_to_carts | 未完了の注文を Spree::Cart に移行します(トークンは同じです) |
| 27 | migrate_calculator_bounds_to_delivery_method_rules | FlatRate の最小/最大範囲を DeliveryMethodRule に移行します |
| 28 | store_settings_backfill_from_config | 移動された Spree::Config 設定をすべてのストアにコピーします |
| 29 | migrate_capture_methods | 支払い方法の auto_capture を capture_method に移行します |
| 30 | product_types_backfill | 商品タイプをストア所有にします |
| 31 | search_reindex | インデックス検索プロバイダー(Meilisearch)を再構築します。DB プロバイダーでは何も行いません |
| 32 | fold_store_credit_categories | ストアクレジットカテゴリーをメモに移行します |
| 33 | migrate_stripe_webhook_keys | (任意、spree_stripe のみ) Webhook シークレットをゲートウェイに移行します |
| 34 | backfill_import_export_tenancy | インポートに store_id(および seller_id)を設定します |
| 35 | backfill_custom_field_definition_stores | カスタムフィールド定義をストア所有にします |
| 36 | package_types | ストアのデフォルトボックスをパッケージタイプの行に移行します |
| 37 | migrate_external_receives_to_purchase_orders | 5.x のサプライヤー受け取りを発注書に移行し、移送にストア/ステータスを設定します |
| 38 | backfill_actor_types | 新しいアクターの *_type カラム(canceler、approver、created_by など)に管理者ユーザークラスを設定します。実行されるまでは、古い行の解決時に非推奨の警告が表示されます |
| 39 | recount_stock_levels | reserved_count/incoming_count を埋めます |
gem bump 後は、必ずこの表よりも spree upgrade --plan の結果を信頼してください。インストールする gem が正しい情報源です。 |
事後確認:凍結された注文
migrate_adjustments_to_typed_rows は金額を変更しません。型付き行の合計が保存済みの合計と一致しない注文は変更されず、metadata にフラグが付けられます(ガイドでは private_metadata と記載されていますが、現在のカラム名は metadata です)。それぞれ確認してください:
# spree console
Spree::Order.where.not(completed_at: nil).find_each.select { |o| o.metadata['typed_adjustments_frozen'].present? }
.map { |o| [o.number, o.metadata['typed_adjustments_frozen']] }
理由には totals_do_not_reconcile や、返品承認による凍結(migrate_returns で処理)が含まれます。
4. コードを修正する — grep のレシピ
これらを server/app server/config server/lib server/spec(classic アプリでは app config lib spec)に加え、apps/storefront、apps/dashboard およびすべての連携コードに対して実行してください。パターンから置き換え先までを網羅した表は、references/grep-recipes.md にあります。起動やチェックアウトを妨げる箇所を優先してください:
cd server # or the app root
grep -rnE "checkout_flow|go_to_state|insert_checkout_step|remove_checkout_step|state_machine|before_transition|after_transition" app config lib
grep -rnE "PermittedAttributes|permitted_[[:alnum:]_]+_attributes" app config lib
grep -rnE "PermissionSets|permissions\.assign|ApiKey::SCOPES" app config lib
grep -rnE "Spree::Adjustment|\.adjustments\b|promo_total|ship_total|item_count" app config lib
grep -rnE "\.master\b|is_master|default_price|DefaultPrice|variant\.price\b|\.price =" app config lib
grep -rnE "finalize!|order\.completed|OrderUpdater|register_update_hook|\bresume!?\b" app config lib
grep -rnE "ReturnAuthorization|CustomerReturn|Reimbursement|ReturnItem" app config lib
grep -rnE "_service\s*=" config
非推奨ですが動作する名前(Shipment、ShippingMethod、Taxon、metafields、firstname、Spree.user_class など)は Spree::Deprecation 警告をログに出します。今のうちに修正してください。これらは 6.1 で削除されます。非推奨機能が見える状態でテストスイートを実行し、各警告を対応事項として扱ってください。
5. 動作の確認チェックリスト
これらはエラーを発生させず、動作を変えます。それぞれ確認してください。
- 完了したカートは読み取り専用です。 チェックアウト後の編集は
Spree::Orderに対して行います。カート API を通じて完了後の注文を変更する処理は、注文側の管理サービスに移してください。 -
line_item.orderは nil の場合があります。 LineItem、Fulfillment、Payment、TaxLine、Discount、Fee、StockReservation はcart_idとorder_idを持ちます(設定されるのはどちらか一方のみです)。#ownerを使用してください。 - ステータスは派生値です。
payment_status/fulfillment_statusを書き込むのはSpree::Orders::UpdateStatusesのみです。直接代入しても上書きされます。payment_state/shipment_state/stateは 6.1 までは読み取り用のエイリアスです。注文の絞り込みにはq[status_eq]=placedを使用してください。 - 注文確定後の金額は固定されます。 再計算では型付きの行を再集計しますが、再生成はしません。注文確定後の金額編集には
Spree::Orders::Discounts::*/Orders::Fees::*を使用してください。 - 5.x の拡張機能は、カートに対して Order の名前を呼び出します。 カートが未完了の注文だった時期に作成されたゲートウェイ gem は、
Spree::Cartに対してpromo_total、item_count、shipments、user、available_payment_methods…を呼び出します。これらの非推奨名は、6.1 までは Cart と Order の両方で警告を出して転送されます(grep の例を参照してください)。非推奨警告ログを確認し、gem を更新してください。 - 無料ギフトはしきい値の計算に含まれません。
create_line_itemsによるギフトは、item_totalルールや注文割引率の計算基準に含まれなくなりました。ギフト自身のプロモーションのしきい値も同様です。「100 ドルを超えて購入するとギフトを進呈」の場合、有料商品の金額が 100 ドルになるとギフトは取り消されます。 - キャンセルは取り消せません。 注文やフルフィルメントに
resumeはありません。order.resumed/fulfillment.resumedイベントも廃止されました。すべての商品が返品された注文は、fulfillment_status: unfulfilledとして扱われます。 -
Spree::Store.defaultはnilになる場合があります。 リクエスト外で実行されるジョブ、rake タスク、シード、テストではSpree::Current.storeを設定してください。ストアに紐付くレコードを作成する前に、デフォルトストアが存在することを確認してください。 -
Spree::Baseを継承するすべてのモデルでは、belongs_toはデフォルトで必須です(独自のモデルも含みます)。空の値が正当な場合はoptional: trueを追加してください。エラーは「can't be blank」ではなく「must exist」となります(アサーションと、422 のdetailsを解析するクライアントを修正してください)。 - ストアフロントの仕様。
requirements[]の各項目には{ step, field, code, message }が含まれます。配送要件はfield: 'delivery_method'、code: 'delivery_method_required'です。リッチテキストの読み取り結果はプレーンテキストと*_html(description_html、internal_note_html)を返します。エディターには*_htmlを読み込んでください。カートのnumberは、1 リリースの間、接頭辞付きのcart_ID と同じ値になります。 - Webhook/イベント。
order.placedを購読してください(order.completedは 6.1 まで非推奨のエイリアスです)。report.*、order.resumed、fulfillment.resumedは削除されました。新しいイベント:return.*、exchange.*、claim.*、digital_asset.*、digital_link.downloaded。 - リッチテキストの許可リストが縮小されました(前提条件を参照してください)。コンテンツは次回保存時に再サニタイズされます。
- デジタルダウンロードは、有効期間の短い署名付き URL(最大 1 時間、
digital_asset_link_expire_time)への302を返します。クライアントはリダイレクトに従う必要があります。顧客にはspree_emailsからダウンロードリンクのメールが送信されるようになりました。独自に送信している場合は、独自のメール送信を停止してください。 - カスタムフィールド定義はストアに紐付きます。 マルチストア環境では、すべての定義がデフォルトストアに作成されます。ストアごとに再作成してください。商品 CSV の列では、
metafield.(旧名称)ではなくcustom_field.の接頭辞を使用し、shipping_category(旧名称)ではなくproduct_typeを使用します。 - スタッフ API の権限。 スタッフの JWT にも、シークレットキーと同じコントローラー単位のゲートが適用されます(
details.required_permissionを含む 403)。スタッフ用エンドポイントにはread_staff/write_staffが必要です。 - ストア API の認可では、CanCanCan を参照しなくなりました。顧客アクセスは、所有権によるスコープ設定と
Spree::Storefront::AccessPolicyに基づきます。ストアフロントのcanルールは、アクセス・ポリシーのサブクラスまたはワークフローのvalidateフックに移してください。 - 国と州は参照データです(
countriesgem のプレーンオブジェクトであり、.where/.findは使用できません)。レコードでは ISO のcountry_code/state_codeで指定します。 - 在庫が減るのは注文確定時ではなく、発送時です(未完了のフルフィルメントでは在庫が引き当てられています)。
count_on_handのみを基に構築された在庫レポートは、これまでと異なる値になります。 - ジョブ。 新規プロジェクトでは Puma 内で Solid Queue が実行されます(Mission Control は
/jobs)。アップグレードしたアプリでは、独自のconfig.active_job.queue_adapterが維持されます。Sidekiq は引き続き動作します。Spree の定期ジョブは、使用しているスケジューラーに移してください。 - SDK のメジャーバージョン。 ストアフロントの
@spree/sdkを 2.x に、インテグレーションの@spree/admin-sdkを 1.x に更新してください。Rails 管理画面のカスタマイズはapps/dashboardプラグイン(spree add dashboard)に置き換えてください。
注意点
- バックフィルの順序を変更しないでください。 複数の手順はそれ以前の手順に依存しています(メディアはマスターバリアントの削除より前、タクソンと顧客はリッチテキストより前、調整はカートと返品より前、返品は在庫移動より前)。マニフェストの順序は重要です。
migrate_users_to_customersは意図的に中断します。 Devise の削除後にCONFIRM_NO_PEPPER=trueが設定されていない場合、中断します。無理に続行するのではなく、ペッパーが使用されていないことを確認してください。*_serviceの DI キーを上書きしても、現在は何も起きません。Spree::Dependencies.carts_complete_service = 'MyComplete'のような従来の書き込みは適用されず、保留されます。*_workflowキーに移行する(提供されているワークフローをサブクラス化する)か、さらに推奨される方法としてSpree.hooks.registerハンドラーを使用してください。詳しくは spree-dependencies と spree-workflows を参照してください。Spree::Product.additional_permitted_attributes << :xはFrozenErrorを発生させます。+= [:x]を使用してください。- レガシーテーブルは 6.1 まで残ります(ロールバック元としての
spree_adjustments、返品承認関連テーブル、spree_state_changes、spree_log_entries、spree_users、Action Text の行)。6.1 より前に、保持したいデータをエクスポートしてください。
さらに詳しく読むには
node_modules/@spree/docs/dist/developer/upgrades/5.6-to-6.0.md— 公式ガイド(https://spreecommerce.org/docs/developer/upgrades/5.6-to-6.0)references/grep-recipes.md— パターンと置換の完全な一覧- 関連スキル:spree-upgrade、spree-workflows、spree-checkout、spree-order-totals、spree-fulfillment、spree-returns、spree-auth-permissions、spree-catalog、spree-pricing、spree-dashboard-plugins、spree-typescript-sdk