Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrations rollback cmd #159

Merged
merged 6 commits into from
Mar 27, 2025
Merged

Conversation

alvarosabu
Copy link
Contributor

@alvarosabu alvarosabu commented Mar 19, 2025

Migrations Rollback Command

The storyblok migrations rollback command allows you to revert content migrations previously applied to your Storyblok stories. This is particularly useful when you need to undo changes made by a migration, restore content to its previous state, or recover from migration errors.

Rollback files are automatically generated when running storyblok migrations run as of the introduction of this PR

Prerequisites

Before rolling back migrations, ensure you have:

  1. The necessary rollback files in your .storyblok/migrations/{spaceId}/rollbacks directory
  2. Proper access to the target space
  3. The migration file name you want to rollback

Basic Usage

# Rollback a specific migration
storyblok migrations rollback migration-name.12345678 --space 12345

# Use custom path for rollback files
storyblok migrations rollback migration-name.12345678 --space 12345 --path /custom/path

# Detailed output during rollback
storyblok migrations rollback migration-name.12345678 --space 12345 --verbose

Architecture & Flow

The rollback command is organized in three main layers with a specific processing flow to ensure safe and efficient content restoration.

Command Structure

// 1. Command Layer (index.ts)
rollbackCommand
  .command('rollback [migrationFile]')
  // ... options

// 2. Operations Layer (index.ts)
  - handleRollback()
  - restoreFromRollback()

// 3. Actions Layer (actions.ts)
  - readRollbackFile()
  - updateStory();

Processing Flow Examples

1. Simple Rollback

storyblok migrations rollback migration-name.12345678 --space 12345

Flow:

  1. Read Phase
// 1. Read rollback file from .storyblok/migrations/12345/rollbacks/
const rollbackData = await readRollbackFile({
  space: '12345',
  path: undefined,
  migrationFile: 'migration-name.12345678'
});

// 2. Parse rollback data
const stories = rollbackData.stories;
  1. Restore Phase
// 3. Process rollback for each story
const rollbackResults = await restoreFromRollback({
  stories,
  space: '12345',
  // ... other options
});
  1. Update Phase
// 4. Update stories with original content
for (const story of stories) {
  await updateStory('12345', token, region, story.id, {
    story: {
      content: story.content,
      name: story.name
    },
    force_update: '1'
  });
}

2. Custom Path Rollback

storyblok migrations rollback migration-name.12345678 --space 12345 --path /custom/path

Flow:

  1. Path Resolution
// 1. Resolve custom rollback file path
const rollbackPath = path.join(
  customPath,
  'migrations',
  spaceId,
  'rollbacks',
  migrationFile
);

// 2. Read rollback file from custom path
const rollbackData = await readRollbackFile({
  space: '12345',
  path: customPath,
  migrationFile: 'migration-name.12345678'
});
  1. Process & Update
// 3. Restore stories from custom path
const results = await restoreFromRollback({
  stories: rollbackData.stories,
  space: '12345',
  // ... other options
});

Key Features

  1. Safe Restoration

    • Restores stories to their exact pre-migration state
    • Preserves story metadata and relationships
    • Handles errors gracefully without affecting other stories
  2. Flexible Configuration

    • Custom path support for rollback files
    • Verbose mode for detailed progress tracking
    • Space-specific rollback organization
  3. Progress Tracking

    • Real-time progress indicators
    • Detailed error reporting
    • Summary of restored stories
  4. Error Handling

    • Graceful failure handling
    • Continues processing remaining stories on error
    • Detailed error messages in verbose mode

Rollback File Structure

Rollback files are automatically generated during migrations and follow this structure:

{
  "stories": [
    {
      "id": "123456",
      "name": "Story Name",
      "slug": "story-slug",
      "content": {
        // Original story content before migration
      },
      "published": true,
      "publish_at": null
    }
  ]
}

File Organization

.storyblok/
└── migrations/
        ├── hero.js
        └── article.amount.js
        └── rollbacks/
            ├── hero.1647123456789.json
            └── article.amount.1647123456790.json

Testing Checklist

General Requirements

  • Command shows proper title and initialization
  • Validates user authentication
  • Requires space ID argument
  • Requires migration file argument

Required Arguments

[migrationFile]

  • Validates file existence
  • Reads correct rollback data
  • Processes story restoration

-s, --space=TARGET_SPACE_ID

  • Validates space access
  • Uses correct space context
  • Handles space-specific rollbacks

Error Handling

  • Not logged in error
  • Missing space ID error
  • Missing rollback file error
  • Invalid rollback data error
  • Story update failure handling

Options

--path=<path>

  • Custom path resolution
  • Maintains directory structure
  • Proper file reading

--verbose

  • Detailed progress output
  • Error message verbosity
  • Operation timing information

Notes

  1. Rollback files are automatically created during migration runs
  2. Each rollback file contains the complete state of affected stories
  3. Rollbacks are processed sequentially to ensure data consistency
  4. Stories are restored individually to minimize risk
  5. The process continues even if individual story updates fail
  6. Verbose mode provides detailed operation information
  7. Custom paths must maintain the expected directory structure
  8. Rollback files use timestamps to ensure uniqueness

- Added `saveRollbackData` function to capture original states of stories before modifications.
- Improved error handling for migration function loading failures, ensuring all stories are marked as failed if the migration function cannot be loaded.
- Refactored story processing logic to filter out stories without content and ensure valid stories are processed for each migration file.
- Enhanced comments for clarity on the migration process and rollback functionality.
- Added `rollback` command to support rolling back migrations.
- Introduced `restoreFromRollback` function to restore stories from rollback files.
- Enhanced error handling and user feedback during the rollback process.
- Updated migration index to include rollback actions and constants.
- Introduced unit tests for `saveRollbackData` and `readRollbackFile` functions to ensure correct behavior during rollback operations.
- Refactored `restoreFromRollback` to `readRollbackFile` for clarity and improved error handling.
- Enhanced the rollback command to read rollback data and restore stories, including handling various edge cases.
- Updated test cases to cover scenarios such as successful rollbacks, error handling, and directory creation.
- Created a comprehensive README.md for the `rollback` command, outlining a detailed testing checklist.
- Included sections on general requirements, required arguments, error handling, options, rollback process, examples, and file structure.
- Documented the expected behavior and error messages for various scenarios to ensure clarity during testing.
- Fixed the JSON structure in `.vscode/launch.json` by properly closing the `env` object and ensuring correct formatting for the subsequent configuration object.
- This change enhances the clarity and functionality of the launch configuration for debugging migrations.
@alvarosabu alvarosabu self-assigned this Mar 19, 2025
@alvarosabu alvarosabu requested a review from edodusi March 19, 2025 11:27
@alvarosabu alvarosabu added p3-significant [Priority] Moderate issues, major enhancements feature [Issue] New feature or request labels Mar 19, 2025
Copy link

rollback-migration

@edodusi
Copy link
Contributor

edodusi commented Mar 21, 2025

@alvarosabu I cannot run migrations 🤔

Even providing a correct migration path, this is what I see in the console

  Migrations   Rolling back migration undefined...

 Error

▲ error Failed to rollback migration: Failed to read rollback file: The "path" argument must be of type string. Received undefined

ℹ For more information about the error, run the command with the `--verbose` flag

This was the command I run (space masked)

$ pnpm dev mig rollback --space ***** --path .storyblok/migrations/*****/rollbacks/simple_component.1742559178779.json

Same error message with --verbose

@alvarosabu
Copy link
Contributor Author

alvarosabu commented Mar 21, 2025

@alvarosabu I cannot run migrations 🤔

Even providing a correct migration path, this is what I see in the console

  Migrations   Rolling back migration undefined...

 Error

▲ error Failed to rollback migration: Failed to read rollback file: The "path" argument must be of type string. Received undefined

ℹ For more information about the error, run the command with the `--verbose` flag

This was the command I run (space masked)

$ pnpm dev mig rollback --space ***** --path .storyblok/migrations/*****/rollbacks/simple_component.1742559178779.json

Same error message with --verbose

What is mig 😜?

@edodusi did you follow the instructions in the readme? That's not a valid rollback command. Rollback always need a file name:

storyblok migrations rollback migration-name.12345678 --space 12345

That's why is undefined because you didn't pass any here migrations rollback [migrationName] Also it doesn't need .json

path is only if you generate the migration in a custom path before, not to pass the migration file you want to run

@edodusi
Copy link
Contributor

edodusi commented Mar 21, 2025

@alvarosabu mig is alias for migrations 😅

Ok sorry I misread, I found the --path arg and thought it was the migration file path!

Sorry, that works 😉

- Updated the `readRollbackFile` function to ensure the correct file path is used by appending `.json` if not already present, enhancing robustness in file reading.
- Refactored the `handleMigrations` function to utilize `structuredClone` for creating a deep copy of the story content, improving performance and avoiding potential issues with JSON serialization.
@alvarosabu alvarosabu requested a review from edodusi March 27, 2025 07:30
@alvarosabu alvarosabu merged commit ed05986 into next Mar 27, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature [Issue] New feature or request p3-significant [Priority] Moderate issues, major enhancements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants