drupal-migration

star 7

Handles Drupal content migrations: D7 to D10/D11 upgrades, custom migrations from external sources (CSV, JSON, API, SQL), Migrate API configuration, source/process/destination plugins, and incremental migration strategies. Examples: - user: "migrate content from D7 to D10" -> plan and implement migration - user: "create a migration from CSV" -> custom source plugin + YAML config - user: "upgrade from Drupal 7" -> plan and implement migration - user: "import data from external API" -> custom source plugin + YAML config Never run migrations on production without --limit testing first.

trebormc By trebormc schedule Updated 6/10/2026

name: drupal-migration description: >- Handles Drupal content migrations: D7 to D10/D11 upgrades, custom migrations from external sources (CSV, JSON, API, SQL), Migrate API configuration, source/process/destination plugins, and incremental migration strategies. Examples: - user: "migrate content from D7 to D10" -> plan and implement migration - user: "create a migration from CSV" -> custom source plugin + YAML config - user: "upgrade from Drupal 7" -> plan and implement migration - user: "import data from external API" -> custom source plugin + YAML config Never run migrations on production without --limit testing first.

Environment

All commands via ssh web drush.

Required modules

ssh web composer require drupal/migrate_plus drupal/migrate_tools drupal/migrate_upgrade
ssh web drush en migrate migrate_plus migrate_tools -y

Migration YAML structure

Place in config/install/ or migrations/ directory:

id: my_migration
label: 'Migrate content from source'
migration_group: my_group
source:
  plugin: csv
  path: private://import/data.csv
  ids: [id]
  header_offset: 0
process:
  type:
    plugin: default_value
    default_value: article
  title: title
  body/value: body
  body/format:
    plugin: default_value
    default_value: basic_html
  field_date:
    plugin: format_date
    source: date
    from_format: 'm/d/Y'
    to_format: 'Y-m-d'
destination:
  plugin: 'entity:node'
migration_dependencies:
  required: []
  optional: []

Common migration commands

Task Command
List migrations ssh web drush migrate:status
Run migration ssh web drush migrate:import my_migration
Run with limit ssh web drush migrate:import my_migration --limit=10
Rollback (undo imported rows) ssh web drush migrate:rollback my_migration
Reset stuck "Importing" status ssh web drush migrate:reset-status my_migration
Run all in group ssh web drush migrate:import --group=my_group
Update existing ssh web drush migrate:import my_migration --update

D7 to D10/D11 workflow

Step 1: Generate migration config

ssh web drush migrate:upgrade --legacy-db-key=d7 --configure-only

Step 2: Review and customize

ssh web drush cex -y
# Review migrate_plus.migration.* files in config/sync/

Step 3: Test with limits

ssh web drush migrate:import upgrade_d7_node_article --limit=5

Step 4: Verify and run full

ssh web drush migrate:import --all

Custom source plugin template

<?php

declare(strict_types=1);

namespace Drupal\mymodule\Plugin\migrate\source;

use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;

/**
 * @MigrateSource(id = "my_custom_source")
 */
final class MyCustomSource extends SqlBase {

  public function query(): \Drupal\Core\Database\Query\SelectInterface {
    return $this->select('source_table', 's')
      ->fields('s', ['id', 'title', 'body', 'created']);
  }

  public function fields(): array {
    return [
      'id' => $this->t('Unique ID'),
      'title' => $this->t('Title'),
    ];
  }

  public function getIds(): array {
    return ['id' => ['type' => 'integer']];
  }

  public function prepareRow(Row $row): bool {
    return parent::prepareRow($row);
  }

}

Process plugin reference

Plugin Use case
get Direct field mapping (default)
default_value Set static value
migration_lookup Reference migrated entity
format_date Date format conversion
entity_generate Create referenced entity if missing
callback PHP callback transformation
static_map Value mapping table
skip_on_empty Skip row if source empty

Related Skills

  • drupal-testing — Create PHPUnit tests for migration source plugins and process logic
  • drupal-debugging — Debug migration errors, inspect entities, check watchdog logs

Verification (after EVERY import)

# 1. Check status: Imported count must be > 0 and Status must be "Idle"
ssh web drush migrate:status --format=table

# 2. Verify migrated content count matches expectations
ssh web drush sqlq "SELECT COUNT(*) FROM node WHERE type = 'article'"

# 3. Check for migration errors
ssh web drush migrate:messages my_migration
ssh web drush ws --count=10 --severity=Error

If a migration fails

Symptom Fix
Status stuck at "Importing" ssh web drush migrate:reset-status my_migration, then re-import
Wrong/partial data imported ssh web drush migrate:rollback my_migration, fix the migration config, re-import with --limit=5 first
Source DB connection error Verify the d7 database key exists in settings.php and the source DB is reachable
Row-level errors Read them with ssh web drush migrate:messages my_migration and fix the process pipeline for those fields
Install via CLI
npx skills add https://github.com/trebormc/drupal-ai-agents --skill drupal-migration
Repository Details
star Stars 7
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator