Skip to content

feat: move migration topic into CLI#1322

Open
rexxars wants to merge 13 commits into
mainfrom
feat/move-migration
Open

feat: move migration topic into CLI#1322
rexxars wants to merge 13 commits into
mainfrom
feat/move-migration

Conversation

@rexxars

@rexxars rexxars commented Jun 17, 2026

Copy link
Copy Markdown
Member

Description

Currently the @sanity/migrate module contains both the migration logic as well as CLI logic. This last part is a bit tedious, especially due to peer dependency version conflicts whenever we release a new major of @sanity/cli-core etc.

This PR moves the CLI logic into the CLI instead of using it as an oclif plugin. This is backwards compatible, but once sanity-io/migrate#92 is released we should upgrade to v8 of that to avoid @sanity/cli-core dependencies of different versions etc.


Note

Medium Risk
Large relocation of dataset-mutating migration run behavior, but defaults to dry-run, requires confirmation for live runs, and is backed by extensive tests including a real-engine smoke test.

Overview
Content migration CLI is now built into @sanity/cli instead of registering @sanity/migrate as an oclif plugin, so sanity migration create, list, and run (and the migrations topic) ship from first-party commands without duplicate command IDs.

The new surface includes on-disk migration discovery (migrations/ files and folders, deduped IDs), scaffolding templates, dry-run / live run via @sanity/migrate’s dryRun and run, flags for project/dataset/export/concurrency, and TTY-friendly mutation output. Oclif config drops the migrate plugin, documents why it must stay out, and adds a migrations topic plus a migration topic alias.

Coverage is added with broad unit tests and an offline smoke test that exercises the real migrate engine against a local export.

Reviewed by Cursor Bugbot for commit 017b5ac. Bugbot is set up for automated code reviews on this repo. Configure here.

rexxars and others added 10 commits June 17, 2026 13:29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…igrate plugin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ke test

Add boundary assertions verifying the CLI hands @sanity/migrate the full API
config (incl. token and apiVersion), concurrency, and the resolved migration.

Add an offline smoke test that exercises the real @sanity/migrate dryRun engine
against a local export archive, so a runtime contract break (e.g. after a
dependency upgrade) is caught even when TypeScript types still line up.

This surfaced a latent bug: dryRunHandler was not awaited, so dry-run output
could be truncated if the process exits before the async work completes. Await it.
@rexxars rexxars requested review from binoy14 and runeb June 17, 2026 22:27
@rexxars rexxars requested a review from a team as a code owner June 17, 2026 22:27
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against main (987a6226)

@sanity/cli

Metric Value vs main (987a622)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 11.13 MB -
Bundled (gzip) 2.10 MB -
Import time 846ms -3ms, -0.4%

bin:sanity

Metric Value vs main (987a622)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.87 MB -
Bundled (gzip) 1.77 MB -
Import time 1.96s +38ms, +2.0%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (987a6226)

Metric Value vs main (987a622)
Internal (raw) 98.2 KB -
Internal (gzip) 23.3 KB -
Bundled (raw) 21.70 MB -
Bundled (gzip) 3.45 MB -
Import time 754ms -1ms, -0.2%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (987a6226)

Metric Value vs main (987a622)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

Comment thread packages/@sanity/cli/src/actions/migration/resolveMigrations.ts
Comment thread packages/@sanity/cli/src/commands/migrations/create.ts
Comment thread packages/@sanity/cli/src/commands/migrations/run.ts Outdated
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli/src/actions/migration/constants.ts 100.0% (new)
packages/@sanity/cli/src/actions/migration/ensureApiVersionFormat.ts 80.0% (new)
packages/@sanity/cli/src/actions/migration/fileExists.ts 0.0% (new)
packages/@sanity/cli/src/actions/migration/prettyMutationFormatter.ts 64.8% (new)
packages/@sanity/cli/src/actions/migration/resolveMigrationScript.ts 17.4% (new)
packages/@sanity/cli/src/actions/migration/resolveMigrations.ts 100.0% (new)
packages/@sanity/cli/src/actions/migration/tree.ts 83.3% (new)
packages/@sanity/cli/src/commands/migrations/create.ts 94.7% (new)
packages/@sanity/cli/src/commands/migrations/list.ts 100.0% (new)
packages/@sanity/cli/src/commands/migrations/run.ts 95.0% (new)
packages/@sanity/cli/src/topicAliases.ts 100.0% (±0%)

Comparing 11 changed files against main @ 987a62265aad678f5f6d32cfbebaa8ed48318558

Overall Coverage

Metric Coverage
Statements 87.4% (+ 0.4%)
Branches 77.0% (+ 0.3%)
Functions 86.6% (+ 1.4%)
Lines 87.7% (+ 0.4%)

- resolveMigrations: dedupe so a migration id is listed only once, even when
  multiple loadable candidates resolve or a file and directory share a base
  name (no more duplicate rows in `migrations list` / run-without-id table)
- create: error instead of writing index.ts into the migrations folder itself
  when the title slugifies to an empty name (e.g. punctuation-only titles)
- run: show friendly guidance instead of crashing with ENOENT when invoked
  without an id and the migrations folder is missing

Each fix is covered by a test that fails without it (TDD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/@sanity/cli/src/commands/migrations/run.ts Outdated
Comment thread packages/@sanity/cli/src/commands/migrations/run.ts Outdated
…id-run

- run: pass deprecatedFlagName 'project' to getProjectId so `--project` is
  honored even when sanity.cli.js has no api.projectId. Previously the command
  failed with "Unable to determine project ID" even when valid --project and
  --dataset flags were supplied.
- run: only finalize the progress spinner (stopAndPersist) once the migration
  reports done. While running it now updates the spinner text instead of
  persisting a success line on every in-progress callback, which repeatedly
  finalized the spinner.

Covered by tests (the run.test.ts spinner tests now assert on the spinner mock
rather than incidental stderr output).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/@sanity/cli/src/commands/migrations/run.ts Outdated
Comment thread packages/@sanity/cli/src/commands/migrations/run.ts
- run: move the no-id listing branch ahead of the api.projectId/dataset
  resolution and validation, so `sanity migrations run` (no id) lists available
  migrations with only a project root, matching `sanity migrations list`
- run: surface a clear CLI error when listing fails for a non-ENOENT reason
  (e.g. a syntax error in a migration file) instead of rethrowing the raw error

Both covered by tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 017b5ac. Configure here.

Comment thread packages/@sanity/cli/src/actions/migration/resolveMigrations.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant