Migrating from v0.1.x to v0.2.0

Guide for upgrading from v0.1.x to v0.2.0 of Firebase Migration Script Runner.

Table of Contents

  1. Overview
    1. What’s New in v0.2.0
    2. Migration Effort
  2. Breaking Changes
    1. 1. Package Name Changed
      1. BEFORE (v0.1.x):
      2. AFTER (v0.2.0):
  3. Step-by-Step Upgrade Guide
    1. Step 1: Update Dependencies
    2. Step 2: Update Package Name in package.json
    3. Step 3: Update Imports
      1. In Your Migration Scripts
      2. In Your Application Code
    4. Step 4: Update CLI Usage (if applicable)
    5. Step 5: Build Your Project
    6. Step 6: Run Tests
    7. Step 7: Test Migrations
  4. Code Examples
    1. Complete Before/After Examples
      1. Example 1: Basic Migration Script
      2. Example 2: Programmatic Usage
      3. Example 3: Using Services
  5. CLI Migration (Optional)
    1. 1. Install Package Globally (Optional)
    2. 2. Create Configuration File
    3. 3. Set Environment Variables
    4. 4. Run Migrations
    5. 5. Use New Lock Commands (v0.2.0)
  6. Troubleshooting
    1. Issue 1: Module Not Found Error
    2. Issue 2: TypeScript Compilation Errors
    3. Issue 3: Import Errors in Migration Scripts
    4. Issue 4: CLI Command Not Found
    5. Issue 5: Lock Errors After Upgrade
  7. Verification Checklist
    1. Package Updates
    2. Import Statements
    3. Build & Tests
    4. Migration Testing
    5. Production Readiness (Optional)
    6. Documentation
  8. New Features You Can Use (Optional)
    1. 1. Migration Locking (Recommended for Production)
    2. 2. Lock CLI Commands
    3. 3. Type-Safe Handler Access
  9. Common Migration Scenarios
    1. Scenario 1: Basic Application
    2. Scenario 2: Kubernetes/Docker Deployment
    3. Scenario 3: Monorepo with Multiple Services
  10. Testing Your Migration
    1. 1. Local Testing with Emulator
    2. 2. Staging Environment Testing
    3. 3. Lock Behavior Testing (if enabled)
  11. Rollback Plan
  12. Getting Help
  13. References
  14. Summary
    1. Key Takeaways
    2. Recommended Actions
    3. Migration at a Glance

Overview

v0.2.0 is a major upgrade that brings production-ready features and aligns with the MSR Core v0.8.1 ecosystem. This release contains one breaking change (package name) and introduces powerful new features for distributed deployments.

What’s New in v0.2.0

  • 🔒 Migration Locking - Prevent concurrent migrations in Kubernetes, Docker, and multi-instance deployments
  • ⬆️ MSR Core v0.8.1 - Latest core with bug fixes, handler access API, and improved locking lifecycle
  • 🔧 Type-Safe API - Full TypeScript support with generic type parameters
  • 🖥️ Lock Management CLI - Commands for monitoring and managing migration locks
  • 📚 Comprehensive Documentation - 300+ line locking guide with deployment examples
  • 🧪 Production Tested - Battle-tested in distributed environments

Migration Effort

Estimated Time: 5-15 minutes

Complexity: Low

  • One breaking change (package name)
  • Simple find-and-replace for imports
  • All existing code continues to work after package update
  • Optional new features can be adopted incrementally

Breaking Changes

1. Package Name Changed

The npm package name has changed to follow the organization’s scoped naming convention.

BEFORE (v0.1.x):

{
  "dependencies": {
    "msr-firebase": "^0.1.6"
  }
}

AFTER (v0.2.0):

{
  "dependencies": {
    "@migration-script-runner/firebase": "^0.2.0"
  }
}

Impact: You must update your package.json and all import statements.

Migration Required: Yes


Step-by-Step Upgrade Guide

Follow these steps carefully to ensure a smooth migration.

Step 1: Update Dependencies

Update your package.json:

# Remove old package
npm uninstall msr-firebase

# Install new scoped package
npm install @migration-script-runner/firebase@^0.2.0

Or with yarn:

yarn remove msr-firebase
yarn add @migration-script-runner/firebase@^0.2.0

Step 2: Update Package Name in package.json

BEFORE:

{
  "dependencies": {
    "msr-firebase": "^0.1.6"
  }
}

AFTER:

{
  "dependencies": {
    "@migration-script-runner/firebase": "^0.2.0"
  }
}

Step 3: Update Imports

Find and replace all import statements in your codebase.

In Your Migration Scripts

BEFORE:

import { IEntity } from 'msr-firebase';
import * as admin from 'firebase-admin';

export const up = async (db: admin.database.Database) => {
  // Migration code
};

AFTER:

import { IEntity } from '@migration-script-runner/firebase';
import * as admin from 'firebase-admin';

export const up = async (db: admin.database.Database) => {
  // Migration code
};

In Your Application Code

BEFORE:

import { FirebaseRunner, FirebaseConfig } from 'msr-firebase';

AFTER:

import { FirebaseRunner, FirebaseConfig } from '@migration-script-runner/firebase';

Step 4: Update CLI Usage (if applicable)

If you’re using the CLI directly, update your npm scripts:

BEFORE:

{
  "scripts": {
    "migrate": "msr-firebase migrate",
    "rollback": "msr-firebase down"
  }
}

AFTER:

{
  "scripts": {
    "migrate": "msr-firebase migrate",
    "rollback": "msr-firebase down"
  }
}

Note: The CLI command name remains msr-firebase (no change).

Step 5: Build Your Project

Compile TypeScript to ensure no import errors:

npm run build

Step 6: Run Tests

Verify everything works:

npm test

Step 7: Test Migrations

Test migrations in a development environment:

# Using Firebase Emulator
firebase emulators:start --only database

# In another terminal, run migrations
export FIREBASE_DATABASE_URL=http://localhost:9000
npx msr-firebase migrate

Code Examples

Complete Before/After Examples

Example 1: Basic Migration Script

BEFORE (v0.1.x):

// migrations/1234567890-add-users-table.ts
import { IEntity } from 'msr-firebase';
import * as admin from 'firebase-admin';

export const up = async (db: admin.database.Database) => {
  await db.ref('users').set({
    user1: { name: 'Alice', email: 'alice@example.com' }
  });
};

export const down = async (db: admin.database.Database) => {
  await db.ref('users').remove();
};

AFTER (v0.2.0):

// migrations/1234567890-add-users-table.ts
import { IEntity } from '@migration-script-runner/firebase';
import * as admin from 'firebase-admin';

export const up = async (db: admin.database.Database) => {
  await db.ref('users').set({
    user1: { name: 'Alice', email: 'alice@example.com' }
  });
};

export const down = async (db: admin.database.Database) => {
  await db.ref('users').remove();
};

Changes:

  • Line 2: Import path changed to @migration-script-runner/firebase

Example 2: Programmatic Usage

BEFORE (v0.1.x):

// src/migrate.ts
import { FirebaseHandler, FirebaseRunner, FirebaseConfig } from 'msr-firebase';

async function runMigrations() {
  const config = new FirebaseConfig();
  config.folder = './migrations';
  config.tableName = 'schema_version';
  config.databaseUrl = process.env.FIREBASE_DATABASE_URL;

  const handler = await FirebaseHandler.getInstance(config);
  const runner = new FirebaseRunner({ handler, config });

  const result = await runner.migrate();
  console.log(`Migrations completed: ${result.executed.length}`);
}

runMigrations().catch(console.error);

AFTER (v0.2.0):

// src/migrate.ts
import { FirebaseHandler, FirebaseRunner, FirebaseConfig } from '@migration-script-runner/firebase';

async function runMigrations() {
  const config = new FirebaseConfig();
  config.folder = './migrations';
  config.tableName = 'schema_version';
  config.databaseUrl = process.env.FIREBASE_DATABASE_URL;

  const handler = await FirebaseHandler.getInstance(config);
  const runner = new FirebaseRunner({ handler, config });

  const result = await runner.migrate();
  console.log(`Migrations completed: ${result.executed.length}`);
}

runMigrations().catch(console.error);

Changes:

  • Line 2: Import path changed to @migration-script-runner/firebase

Example 3: Using Services

BEFORE (v0.1.x):

// src/data-service.ts
import { FirebaseDataService, IEntity } from 'msr-firebase';
import * as admin from 'firebase-admin';

interface IUser extends IEntity {
  name: string;
  email: string;
}

class UserService {
  private dataService: FirebaseDataService;

  constructor(db: admin.database.Database) {
    this.dataService = new FirebaseDataService(db);
  }

  async getUser(path: string): Promise<IUser | null> {
    return await this.dataService.getObject<IUser>(path);
  }
}

AFTER (v0.2.0):

// src/data-service.ts
import { FirebaseDataService, IEntity } from '@migration-script-runner/firebase';
import * as admin from 'firebase-admin';

interface IUser extends IEntity {
  name: string;
  email: string;
}

class UserService {
  private dataService: FirebaseDataService;

  constructor(db: admin.database.Database) {
    this.dataService = new FirebaseDataService(db);
  }

  async getUser(path: string): Promise<IUser | null> {
    return await this.dataService.getObject<IUser>(path);
  }
}

Changes:

  • Line 2: Import path changed to @migration-script-runner/firebase

CLI Migration (Optional)

If you’re upgrading to use the CLI for the first time, follow these steps:

1. Install Package Globally (Optional)

npm install -g @migration-script-runner/firebase

Or use npx (recommended):

npx msr-firebase --version

2. Create Configuration File

Create msr.config.js in your project root:

module.exports = {
  folder: './migrations',
  tableName: 'schema_version',
  locking: {
    enabled: process.env.NODE_ENV === 'production',
    timeout: 600000  // 10 minutes
  }
};

3. Set Environment Variables

export FIREBASE_DATABASE_URL=https://your-project.firebaseio.com
export GOOGLE_APPLICATION_CREDENTIALS=./serviceAccountKey.json

4. Run Migrations

# Create new migration
npx msr-firebase create add-posts-table

# Apply all pending migrations
npx msr-firebase migrate

# Roll back last migration
npx msr-firebase down

# Check migration status
npx msr-firebase list

5. Use New Lock Commands (v0.2.0)

# Check lock status
npx msr-firebase lock:status

# Force-release stuck lock
npx msr-firebase lock:release --force

Troubleshooting

Issue 1: Module Not Found Error

Error:

Error: Cannot find module 'msr-firebase'

Solution:

  1. Ensure you’ve uninstalled the old package: npm uninstall msr-firebase
  2. Install the new package: npm install @migration-script-runner/firebase
  3. Update all import statements to use @migration-script-runner/firebase
  4. Clear node_modules and reinstall: rm -rf node_modules && npm install

Issue 2: TypeScript Compilation Errors

Error:

Cannot find module 'msr-firebase' or its corresponding type declarations

Solution:

  1. Update all imports to @migration-script-runner/firebase
  2. Delete dist/ directory: rm -rf dist
  3. Rebuild: npm run build

Issue 3: Import Errors in Migration Scripts

Error:

Error: Cannot find module 'msr-firebase' from 'migrations/...'

Solution: Update imports in all migration script files:

# On Linux/Mac
find ./migrations -type f -name "*.ts" -exec sed -i '' 's/msr-firebase/@migration-script-runner\/firebase/g' {} +

# On Windows (PowerShell)
Get-ChildItem -Path ./migrations -Filter *.ts -Recurse | ForEach-Object {
    (Get-Content $_.FullName) -replace 'msr-firebase', '@migration-script-runner/firebase' | Set-Content $_.FullName
}

Issue 4: CLI Command Not Found

Error:

msr-firebase: command not found

Solution:

  1. Use npx instead: npx msr-firebase migrate
  2. Or install globally: npm install -g @migration-script-runner/firebase
  3. Verify installation: npm list -g @migration-script-runner/firebase

Issue 5: Lock Errors After Upgrade

Error:

LockOperationError: Failed to acquire lock

Solution: This is expected if you have an existing migration running or a stale lock:

  1. Check lock status: npx msr-firebase lock:status
  2. If lock is stale, force-release: npx msr-firebase lock:release --force
  3. Retry migration: npx msr-firebase migrate

Verification Checklist

Use this checklist to ensure your migration is complete:

Package Updates

  • Removed old msr-firebase package from package.json
  • Installed new @migration-script-runner/firebase package
  • Updated package version to ^0.2.0

Import Statements

  • Updated imports in all migration scripts
  • Updated imports in application code
  • Updated imports in test files
  • No references to msr-firebase remain in codebase

Build & Tests

  • TypeScript compiles without errors
  • All unit tests pass
  • All integration tests pass
  • No import errors in console

Migration Testing

  • Tested migration creation: npx msr-firebase create test-migration
  • Tested migration execution: npx msr-firebase migrate (in dev environment)
  • Tested rollback: npx msr-firebase down (in dev environment)
  • Tested migration status: npx msr-firebase list

Production Readiness (Optional)

  • Reviewed locking configuration for production
  • Tested lock commands: lock:status and lock:release
  • Updated deployment scripts/CI/CD pipelines
  • Verified environment variables are set correctly

Documentation

  • Updated README if you have project-specific docs
  • Updated package.json scripts if needed
  • Updated deployment documentation
  • Informed team members of the upgrade

New Features You Can Use (Optional)

After completing the migration, consider adopting these new v0.2.0 features:

Prevent concurrent migrations in distributed environments:

// msr.config.js
module.exports = {
  folder: './migrations',
  tableName: 'schema_version',
  locking: {
    enabled: process.env.NODE_ENV === 'production',
    timeout: 600000,  // 10 minutes
    retryAttempts: 3,
    retryDelay: 5000
  }
};

Benefits:

  • Prevents race conditions in Kubernetes/Docker deployments
  • Automatic lock expiration and cleanup
  • Force-release stuck locks with CLI

2. Lock CLI Commands

Manage migration locks:

# Check current lock status
npx msr-firebase lock:status

# Force-release stuck lock
npx msr-firebase lock:release --force

Output Example:

Lock Status: LOCKED
Locked by: server-prod-12345-a1b2c3d4
Locked at: 2025-12-18T10:00:00.000Z
Expires at: 2025-12-18T10:10:00.000Z
Process ID: 12345

Another migration is currently running.
If you believe this is a stale lock, use: npx msr-firebase lock:release --force

3. Type-Safe Handler Access

Use generic type parameters for better type safety:

import { FirebaseRunner, FirebaseHandler } from '@migration-script-runner/firebase';

// Type-safe handler access
const runner = new FirebaseRunner({ handler, config });
const handler: FirebaseHandler = runner.getHandler();

// Full IDE autocomplete support
const connectionInfo = runner.getConnectionInfo();
console.log(connectionInfo.databaseUrl);

Common Migration Scenarios

Scenario 1: Basic Application

Time: 5 minutes

  1. Update package.json dependencies
  2. Run find-and-replace: msr-firebase@migration-script-runner/firebase
  3. Rebuild and test
npm uninstall msr-firebase
npm install @migration-script-runner/firebase@^0.2.0
npm run build
npm test

Scenario 2: Kubernetes/Docker Deployment

Time: 10-15 minutes

  1. Complete basic migration (Scenario 1)
  2. Enable locking in configuration
  3. Test lock behavior in staging
  4. Update deployment manifests
  5. Deploy to production
// msr.config.js (production)
module.exports = {
  folder: './migrations',
  tableName: 'schema_version',
  locking: {
    enabled: true,
    timeout: 600000
  }
};

Scenario 3: Monorepo with Multiple Services

Time: 15-30 minutes

  1. Update root package.json (if shared)
  2. Update each service’s imports
  3. Update shared migration scripts
  4. Test each service independently
  5. Test inter-service migration coordination
# Update all services
for dir in services/*/; do
  cd "$dir"
  npm uninstall msr-firebase
  npm install @migration-script-runner/firebase@^0.2.0
  npm run build && npm test
  cd ../..
done

Testing Your Migration

1. Local Testing with Emulator

# Start Firebase Emulator
firebase emulators:start --only database

# In another terminal
export FIREBASE_DATABASE_URL=http://localhost:9000
npx msr-firebase migrate

2. Staging Environment Testing

# Set staging credentials
export FIREBASE_DATABASE_URL=https://your-project-staging.firebaseio.com
export GOOGLE_APPLICATION_CREDENTIALS=./staging-key.json

# Run migrations
npx msr-firebase migrate

# Verify
npx msr-firebase list

3. Lock Behavior Testing (if enabled)

# Start first migration
npx msr-firebase migrate &

# Try concurrent migration (should fail with lock error)
npx msr-firebase migrate

# Check lock status
npx msr-firebase lock:status

Rollback Plan

If you encounter critical issues, you can rollback:

# Reinstall old package
npm uninstall @migration-script-runner/firebase
npm install msr-firebase@^0.1.6

# Revert import changes (if you used git)
git checkout HEAD -- .

# Or manually revert imports
# (reverse all @migration-script-runner/firebase → msr-firebase)

Note: It’s recommended to test the upgrade in a development/staging environment before production.


Getting Help

If you encounter issues during migration:

  1. Check Documentation: https://migration-script-runner.github.io/msr-firebase/
  2. GitHub Issues: https://github.com/migration-script-runner/msr-firebase/issues
  3. MSR Core Docs: https://migration-script-runner.github.io/msr-core/
  4. Open New Issue: Use v0.2.0 and migration labels

References


Summary

v0.2.0 brings production-ready features with minimal migration effort.

Key Takeaways

  • ✅ One breaking change: package name msr-firebase@migration-script-runner/firebase
  • 🔄 Simple migration: update package + find-and-replace imports
  • 🔒 New features: Migration locking, lock CLI commands, type-safe API
  • ⏱️ 5-15 minutes for most projects
  • 🔙 Easy rollback if needed
  1. Upgrade immediately - Simple find-and-replace migration
  2. Test in development - Verify all migrations work
  3. Enable locking for production - Prevent concurrent migration issues
  4. Update CI/CD pipelines - Use new package name in deployment scripts

Migration at a Glance

# Quick migration (5 minutes)
npm uninstall msr-firebase
npm install @migration-script-runner/firebase@^0.2.0

# Update imports (use your IDE's find-and-replace)
# Find: from 'msr-firebase'
# Replace: from '@migration-script-runner/firebase'

npm run build && npm test

Happy migrating! 🚀