npx skills add ...
npx skills add spree/agent-skills --skill spree-upgrade
Use when the user wants to upgrade Spree to a newer version, or asks how the upgrade machinery works. Common phrasings include "upgrade Spree", "bump Spree", "how do I upgrade", "what's the upgrade process", "run the Spree upgrade", "spree upgrade --plan", "rake spree:upgrade", "re-run one upgrade step", "upgrade in production / release phase". Covers the version-agnostic flow for Spree 6 projects — the `spree upgrade` CLI, the `rake spree:upgrade` task and its manifests, retrying single steps, and running backfills in production. For the Spree 5.x → 6.0 major hop, also load spree-upgrade-5-to-6.
npx skills add spree/agent-skills --skill spree-upgrade
Every Spree upgrade is the same three stages:
bundle update the spree* gems.spree:install:migrations) and run db:migrate.rake spree:upgrade runs the version-specific data tasks listed in the upgrade manifests shipped inside spree_core.Then a fourth, manual stage: review the upgrade guide for your hop (behavior changes, config moves, code that needs editing). The rake-runnable part never covers everything.
Going from 5.x to 6.0? That's a major, breaking hop with preconditions (you must be on 5.6 first, Rails 8.1, Gemfile swaps, roles-as-data). Read the spree-upgrade-5-to-6 skill before starting.
| Project | How you run it |
|---|---|
create-spree-app (Docker + @spree/cli, Rails app in server/ — older projects: backend/) | spree upgrade |
| Classic Rails app with Spree gems at the repo root (no Docker/CLI) | the native commands below |
| Production (either flavor) | your deploy pipeline + bundle exec rake spree:upgrade |
spree upgrade prompts before bundle update and before migrations (answer No to skip one). --yes skips the prompts for non-interactive use. The rake backfills always run.
It needs the ejected dev stack. Fresh projects run the prebuilt ghcr.io/spree/spree image with no source bind mount, so bundle update inside it can't touch your server/Gemfile.lock and copied migrations never land in server/db/migrate/. Run spree eject first (switches to the build-from-source compose), then upgrade.
The CLI refuses to run in a monorepo edge project (SPREE_PATH set in .env) — use the pnpm server:* scripts from the monorepo root there.
CLI flags map 1:1 to env vars on the rake task: --plan → DRY_RUN=1, --step <id> → STEP=<id>, --to <x.y> → TO=<x.y>.
rake spree:upgrade worksspree_core/lib/spree/upgrades/<from>_to_<to>/manifest.yml (dots become underscores: 5_6_to_6_0). Each lists ordered steps: id, name, task (a rake task), notes, and optionally optional: true (skipped when the task isn't defined, e.g. a provider gem you don't have).to is ≤ the installed minor version, oldest first. An app that skipped a backfill two versions ago catches up automatically.bundle update, migrations, cron/recurring jobs and code changes are never in them.List what the installed gem ships:
Prints every step (id, rake task, notes) that a real run would execute, in order — plan mode uses the same manifest selection as a real run (every manifest whose to ≤ the installed minor, or ≤ --to when given). Nothing is executed and the bundle/migrate pre-steps are skipped.
The plan reads manifests from the installed gem, so bump the gems first — a 5.6 bundle doesn't contain the 5.6 → 6.0 manifest.
After a partial failure, fix the cause and re-run only that step (skips bundle + migrate):
Step ids come from the plan output. If the same id exists in two manifests the task aborts and asks for TO= to disambiguate. Some tasks take their own env knobs (BATCH_SIZE, SKIP_INVALID_ROWS=true, …) — they're documented in each step's notes; pass them the same way (spree rake spree:upgrade STEP=… BATCH_SIZE=200, or spree exec env BATCH_SIZE=200 bin/rake …).
You can also invoke the underlying task directly — spree rake <task> / bundle exec rake <task> — which is handy for tasks that aren't in any manifest.
Eligible manifests become those whose to ≤ the cap. Useful when you're landing on an intermediate minor before the next hop (5.x apps must land on 5.6 before going to 6.0).
spree upgrade bumps every installed spree* gem (it lists them with bundle list --name-only inside the container), but each extension's own migrations, install generators and breaking changes are yours to run — check its CHANGELOG. Extensions must have a release compatible with the target Spree version or bundle update won't resolve.config/recurring.yml, or your Sidekiq/cron setup), moved settings and new env vars are listed in the upgrade guide, not the manifest./spree:audit-upgrade command in this plugin does a read-only readiness pass.@spree/sdk, @spree/admin-sdk, @spree/dashboard) are bumped through your own package manager — the CLI prints the apps/storefront @spree/sdk version in its "Next steps" panel as a reminder.Don't run spree upgrade against production — it's a dev CLI. Your deploy already does bundle install and db:migrate (the starter's Docker entrypoint runs bin/rails db:prepare on boot). Add the backfills as one more command:
Wire it as a release-phase command (Heroku), pre-deploy command (Render), init container / Job (Kubernetes) or post-deploy hook (Kamal, Capistrano). Because every step is idempotent, running it on every deploy is safe and cheap once the data is migrated.
Order matters: migrate → backfill → serve new code. Some steps are safety nets for the migration and some must run before later migrations (they say so in their notes). For big hops, run the backfills in a maintenance window or against a restored copy first to measure duration — several tasks honor BATCH_SIZE.
node_modules/@spree/docs/dist/developer/upgrades/<from>-to-<to>.md or https://spreecommerce.org/docs/developer/upgrades.spree upgrade --plan → read every step's notes.--step.bundle exec rake spree:upgrade in the release phase.--plan printing nothing (or only old manifests) means the gems aren't bumped yet — the plan comes from the installed spree_core.bundle update fails in the container → you're on the prebuilt image; spree eject first, or the bundle is out of sync (spree bundle install).migrate_users_to_customers, migrate_returns) and print the offending ids plus the env var that overrides. Read the message; don't blindly re-run.from version's schema. Jumping minors is fine (the walk catches up); skipping a required landing version (5.6 before 6.0) is not.node_modules/@spree/docs/dist/developer/upgrades/ — per-hop upgrade guidesnode_modules/@spree/docs/dist/developer/cli/quickstart.md — spree upgrade referencespree exec sh -c 'ls "$(bundle info spree_core --path)/lib/spree/upgrades/"'
# classic: ls "$(bundle info spree_core --path)/lib/spree/upgrades/"spree upgrade --plan # or: DRY_RUN=1 bundle exec rake spree:upgradespree upgrade --step migrate_returns # or: STEP=migrate_returns bundle exec rake spree:upgradespree upgrade --to 5.6bundle exec rake spree:upgrade