# migrate

> Run content and preset migration pipelines.
```bash
sb-mig migrate content --all --from <space> --to <space> --migration <name>
sb-mig migrate presets --all --from <space-or-file> --to <space> --migration <name>
sb-mig migrate continue
```

`migrate` loads local migration config files, applies them in memory, writes
artifacts, and then writes changed stories or presets unless `--dry-run` is
passed. `migrate continue` finishes a previous `--dry-run` by replaying its
saved result to Storyblok without re-running the preparation phase.

## Shared flags [#shared-flags]

Exact flag names on this page include fromFilePath, migrationComponentAlias,
migrationComponents, withSlug, startsWith, publicationMode,
publicationLanguages, languagePublishStatePath, and fileName.

| Flag                        | Type              | Applies to       | Effect                                                                                                        |
| --------------------------- | ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------- |
| `--from`                    | string            | content, presets | Source space ID. With `--migrate-from file`, it can be a local backup file name.                              |
| `--fromFilePath`            | string            | content, presets | Direct path to a stories or presets JSON file when `--migrate-from file` is used.                             |
| `--to`                      | string            | content, presets | Target Storyblok space ID.                                                                                    |
| `--migrate-from`            | `space` or `file` | content, presets | Read source items from Storyblok or local JSON. Default: `space`.                                             |
| `--migration`               | repeatable string | content, presets | Migration config name without extension. Content supports multiple ordered values. Presets support one value. |
| `--migrationComponentAlias` | repeatable string | content, presets | Add mapper aliases. Format: `<migration>:<source>=<alias1>,<alias2>`.                                         |
| `--migrationComponents`     | repeatable string | content, presets | Override exact component scope. Format: `<migration>:<component1>,<component2>`.                              |
| `--dry-run`                 | boolean           | content, presets | Apply migration in memory and write artifacts without Storyblok writes.                                       |
| `--fileName`                | string            | content, presets | Stable artifact base name. Also disables datestamped artifact names.                                          |
| `--yes`                     | boolean           | content, presets | Skip confirmation prompt for real writes.                                                                     |

## content [#content]

### Primary form [#primary-form]

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --dry-run
```

`--all` means "select all non-folder stories, then run the component mappers
defined by the migration config." It does not mean every component must change.
Only stories whose JSON changes are written to the `*-to-migrate` artifact and,
in real runs, to Storyblok.

### Content-only flags [#content-only-flags]

| Flag                         | Type              | Effect                                                                                    |
| ---------------------------- | ----------------- | ----------------------------------------------------------------------------------------- |
| `--all`                      | boolean           | Select all non-folder stories in scope.                                                   |
| `--withSlug`                 | repeatable string | Select exact story `full_slug` values.                                                    |
| `--startsWith`               | string            | Select stories whose `full_slug` starts with the prefix.                                  |
| `--publicationMode`          | enum              | `preserve-layers`, `collapse-draft`, or `save-only`. Default: `preserve-layers`.          |
| `--publicationLanguages`     | string            | `default`, `all`, or comma-separated language codes. Default for publishing modes: `all`. |
| `--languagePublishStatePath` | string            | Reuse a JSON map generated by `language-publish-state`.                                   |

### Story selection [#story-selection]

Select all non-folder stories:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name
```

Select exact slugs:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --withSlug blog/home \
  --withSlug docs/getting-started
```

Select a subtree by prefix:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --startsWith blog/
```

### Scoped component migrations [#scoped-component-migrations]

Use `--migrationComponents` to restrict one migration to specific component
names:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponents colorPickerModeValues:sb-section,sb-tour-page-section
```

Use `--migrationComponentAlias` when another component should reuse an existing
mapper:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button
```

You can combine aliases and overrides. Alias first, then include the alias in
the override list if you want the migration scoped to only those components:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration colorPickerModeValues \
  --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button \
  --migrationComponents colorPickerModeValues:sb-button,sb-open-drift-button
```

The help output currently lists `sb-mig migrate content <component-name ...>`.
In `6.1.1-beta.1`, the built CLI rejects that combination because the default
`migrateFrom` flag is present during command matching. Use `--all` plus
`--migrationComponents` for deterministic scoped migrations.

### Multiple migration steps [#multiple-migration-steps]

Content migrations can run multiple configs in order:

```bash
sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-a \
  --migration migration-b \
  --migration migration-c \
  --yes
```

Each step gets its own pipeline summary entry. If a migration has a matching
validator, the validator runs after that step.

### File input [#file-input]

Read from a discovered local backup file name:

```bash
sb-mig migrate content --all \
  --migrate-from file \
  --from file-with-stories \
  --to 12345 \
  --migration migration-name
```

Read from an explicit file path:

```bash
sb-mig migrate content --all \
  --migrate-from file \
  --fromFilePath sbmig/migrations/dry-run--12345---story-to-migrate.json \
  --to 12345 \
  --migration migration-a \
  --migration migration-b
```

When `--fromFilePath` is used and `--from` is omitted, sb-mig derives the
artifact base name from the file path.

## Publication modes [#publication-modes]

| Mode              | Behavior                                                                                                                                                              | Constraints                                                                                       |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `preserve-layers` | Default. Migrates draft/current and latest published layer for dirty published stories, publishes the migrated published layer, then restores migrated draft/current. | Requires `--migrate-from space` and `--from` equal to `--to`.                                     |
| `collapse-draft`  | Migrates draft/current only. Published stories can be republished from the migrated draft/current layer. Dirty draft content becomes the published content.           | Works for duplicated-space workflows where preserving a separate published layer is not required. |
| `save-only`       | Saves migrated draft/current content and never publishes.                                                                                                             | Cannot be combined with `--publicationLanguages` or `--languagePublishStatePath`.                 |

Legacy flags are rejected:

| Old flag                   | Replacement                                                               |
| -------------------------- | ------------------------------------------------------------------------- |
| `--publish`                | `--publicationMode preserve-layers` or `--publicationMode collapse-draft` |
| `--publishLanguages`       | `--publicationLanguages`                                                  |
| `--preservePublishedLayer` | `--publicationMode preserve-layers`                                       |

## Publication languages [#publication-languages]

```bash
--publicationLanguages default
--publicationLanguages all
--publicationLanguages default,fr,de
```

`default` maps to Storyblok's default-language publish token. `all` resolves the
target space languages and publishes `[default]` plus configured language codes.

When a publishing mode is active and `--languagePublishStatePath` is omitted,
sb-mig builds a language publish-state map automatically for the selected
stories. For sensitive runs, generate the map first:

```bash
sb-mig language-publish-state \
  --from 12345 \
  --startsWith blog/ \
  --languages all \
  --fileName blog-prod

sb-mig migrate content --all \
  --from 12345 \
  --to 12345 \
  --migration migration-name \
  --publicationMode preserve-layers \
  --publicationLanguages all \
  --languagePublishStatePath sbmig/language-publish-state/blog-prod.json \
  --yes
```

## presets [#presets]

### Valid forms [#valid-forms]

```bash
sb-mig migrate presets --all \
  --from 12345 \
  --to 12345 \
  --migration preset-migration \
  --dry-run

sb-mig migrate presets --all \
  --migrate-from file \
  --fromFilePath sbmig/presets/presets-backup.json \
  --to 12345 \
  --migration preset-migration
```

### Preset constraints [#preset-constraints]

| Constraint                               | Reason                                                                  |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| Exactly one `--migration` value          | Preset migration pipelines do not support multiple ordered configs yet. |
| `--publicationMode` is rejected          | Presets cannot be published.                                            |
| `--publicationLanguages` is rejected     | Presets cannot be published by language.                                |
| `--languagePublishStatePath` is rejected | Presets do not use story publish-state maps.                            |

Before a real preset run, sb-mig backs up all remote presets to
`presets-backup`.

## continue [#continue]

`continue` finishes a previous `migrate content --dry-run` by writing its
already-computed result to Storyblok. It skips the expensive preparation a dry
run already did — pulling stories, applying mappers in memory, and fetching
published layers and language state — and replays only the writes. On large
spaces this turns the "dry run, then real run" workflow from roughly double the
cost into one preparation plus a fast write, so always running a dry run first
no longer doubles the wait.

`continue` assumes a content freeze between the dry run and the continue run.
The source space must not change in between: continue trusts the dry run's
result and does not re-read or diff the space (no drift detection).

### Primary form [#primary-form-1]

```bash
sb-mig migrate continue
```

`continue` discovers the dry run's continue manifest in `sbmig/migrations`,
prints a summary of what it will write (space, publication mode, languages,
story counts, and the artifacts it will use), and prompts for confirmation
before writing.

### continue-only flags [#continue-only-flags]

| Flag         | Type    | Effect                                                                                                            |
| ------------ | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `--manifest` | string  | Continue manifest filename inside `sbmig/migrations`. Only needed when more than one dry-run manifest is present. |
| `--yes`      | boolean | Skip the confirmation prompt for the write.                                                                       |

### Manifest discovery [#manifest-discovery]

| Situation                                           | Behavior                                                        |
| --------------------------------------------------- | --------------------------------------------------------------- |
| Exactly one continue manifest in `sbmig/migrations` | Used automatically.                                             |
| More than one                                       | Stops and lists them; pass `--manifest <filename>` to pick one. |
| None                                                | Errors and points you at `migrate content … --dry-run`.         |

### Equivalent to a real run [#equivalent-to-a-real-run]

`continue` writes exactly what a real `migrate content` would: it updates
`applied-backpack-migrations.json`, performs the same Storyblok writes
(including the preserve-layers dual-layer write for dirty published stories),
and writes a migration run log tagged with the originating manifest.

It performs no bulk story fetch, no in-memory migration, no published-layer
fetch, and no language-state fetch. The only read it still makes is the
existing per-story safety re-fetch inside the preserve-layers dual-layer write.

## Artifacts and side effects [#artifacts-and-side-effects]

For `--migrate-from space` real runs, sb-mig writes a pre-migration backup under
`sbmig/backup/story` or `sbmig/backup/preset`.

All content and preset runs write migration artifacts under `sbmig/migrations`.
Dry runs return before Storyblok writes. Real runs also write a JSONL migration
run log after Storyblok write attempts complete.

A content dry run additionally writes a `*-continue-manifest` file (and, for
preserve-layers, a `*-dirty-published-records` file) that `migrate continue`
uses to replay the run. The dry run's `*-input-full` artifacts are the
pre-migration snapshot for that workflow, since `continue` does not re-read the
space to take its own backup.
