Skip to content

A lightweight, production-ready MySQL migration tool for Go applications with support for both CLI usage and library integration.

License

Notifications You must be signed in to change notification settings

mirajehossain/gomigratex

Repository files navigation

gomigratex

Go Report Card Go Reference License: MIT Go Version

A lightweight, production-ready MySQL migration tool for Go applications with support for both CLI usage and library integration.

Overview

gomigratex is designed to help you manage database schema changes in a safe, reliable, and version-controlled manner. Whether you're building a microservice, a monolith, or a distributed system, gomigratex provides the tools you need to handle database migrations with confidence.

Key Features

  • 🚀 Dual Interface - Use as a standalone CLI tool or embed as a library in your Go applications
  • 🗄️ MySQL Optimized - Built specifically for MySQL with proper connection handling and syntax support
  • 📦 Embedded Migrations - Bundle SQL migration files directly into your application binary using Go's embed package
  • 🔒 Checksum Validation - Automatically detect drift between migration files and database records
  • 🔐 Advisory Locking - Prevent concurrent migrations with MySQL's advisory lock mechanism
  • Rollback Support - Full support for down migrations with proper ordering
  • 📍 Baseline Support - Mark existing database schemas as migrated without applying changes
  • 📊 Structured Logging - JSON output for easy integration with logging and monitoring systems
  • 🧪 Dry Run Mode - Preview migration plans without executing them
  • Production Ready - Comprehensive test coverage and battle-tested in production environments

Table of Contents

Installation

CLI Tool

Option 1: Download Pre-Built Binary (Recommended)

Download the latest release from GitHub Releases:

Linux:

wget https://github.com/mirajehossain/gomigratex/releases/latest/download/migratex-linux
chmod +x migratex-linux
sudo mv migratex-linux /usr/local/bin/migratex

macOS (Intel):

wget https://github.com/mirajehossain/gomigratex/releases/latest/download/migratex-darwin
chmod +x migratex-darwin
sudo mv migratex-darwin /usr/local/bin/migratex

macOS (Apple Silicon):

wget https://github.com/mirajehossain/gomigratex/releases/latest/download/migratex-darwin-arm64
chmod +x migratex-darwin-arm64
sudo mv migratex-darwin-arm64 /usr/local/bin/migratex

Windows: Download migratex-windows.exe and add to your PATH.

Verify installation:

migratex -v
# Output: gomigratex v1.0.0

Option 2: Install via go install

Install the latest version directly from GitHub:

go install github.com/mirajehossain/gomigratex/cmd/migratex@latest

This will automatically use the latest release version.

Library

Add to your Go project:

go get github.com/mirajehossain/gomigratex@latest

Use a specific version:

go get github.com/mirajehossain/gomigratex@v1.0.0

Quick Start

Step 1: Create Migration Files

Create your first migration using the CLI:

migratex create add_users_table --dir ./migrations

This generates two files:

  • 20250109120000_add_users_table.up.sql - Forward migration
  • 20250109120000_add_users_table.down.sql - Rollback migration

Step 2: Write Your SQL

20250109120000_add_users_table.up.sql:

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_email (email),
    INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

20250109120000_add_users_table.down.sql:

DROP TABLE IF EXISTS users;

Step 3: Apply Migrations

Set up your database connection and apply migrations:

export DB_DSN="user:password@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"

# Apply all pending migrations
migratex up --dsn "$DB_DSN" --dir ./migrations

# Check migration status
migratex status --dsn "$DB_DSN" --dir ./migrations

Output:

Applied 1 migration(s) successfully
✓ 20250109120000_add_users_table.up.sql

CLI Usage

Available Commands

Command Description
up Apply all pending migrations
down <n> Rollback last N migrations (use all to rollback everything)
status Display migration status (applied/pending)
create <name> Create a new migration file pair
repair Update checksums after manual file edits
force <version> Mark specific migrations as applied (baseline)
version, -v, --version Display version information

Global Flags

Flag Environment Variable Description Default
--dsn DB_DSN MySQL connection string -
--dir MIGRATIONS_DIR Directory containing migration files ./migrations
--table MIGRATIONS_TABLE Name of migrations tracking table schema_migrations
--json - Output in JSON format false
--dry-run - Show execution plan without applying false
--verbose - Enable detailed per-migration logging false
--lock-timeout LOCK_TIMEOUT_SEC Advisory lock timeout in seconds 30

Command Examples

Apply all pending migrations:

migratex up --dsn "$DB_DSN" --dir ./migrations

Apply with verbose output:

migratex up --dsn "$DB_DSN" --dir ./migrations --verbose

Preview migrations without applying (dry run):

migratex up --dsn "$DB_DSN" --dir ./migrations --dry-run

Get JSON output for automation:

migratex status --dsn "$DB_DSN" --dir ./migrations --json

Create a new migration:

migratex create add_user_indexes --dir ./migrations

Rollback the last migration:

migratex down 1 --dsn "$DB_DSN" --dir ./migrations

Rollback all migrations:

migratex down all --dsn "$DB_DSN" --dir ./migrations

Baseline an existing database:

# Mark all migrations up to version 20250109120000 as applied
migratex force 20250109120000 --dsn "$DB_DSN" --dir ./migrations

Repair checksums after manual edits:

migratex repair --dsn "$DB_DSN" --dir ./migrations

Using Configuration Files

Create migratex.yaml:

dsn: "user:password@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"
dir: "./migrations"
table: "schema_migrations"
lock_timeout_sec: 30
applied_by: "deployment-system"
verbose: true
json: false

Use the configuration file:

migratex up --config migratex.yaml

Library Usage

Basic Integration

Integrate gomigratex directly into your Go application:

package main

import (
    "context"
    "database/sql"
    "log"

    "github.com/mirajehossain/gomigratex/internal/db"
    "github.com/mirajehossain/gomigratex/internal/migrator"
)

func main() {
    ctx := context.Background()

    // Connect to MySQL database
    dsn := "user:password@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"
    database, err := db.OpenMySQL(dsn)
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }
    defer database.Close()

    // Create migration runner
    runner := migrator.NewRunner(database, "schema_migrations", "myapp")

    // Ensure migration table exists
    if err := runner.Ensure(ctx); err != nil {
        log.Fatalf("Failed to create migration table: %v", err)
    }

    // Set up file source
    src := migrator.FileSource{
        RootDir: "./migrations",
    }

    // Discover and plan migrations
    plan, err := migrator.DiscoverAndPlan(ctx, src, runner.Storage)
    if err != nil {
        log.Fatalf("Failed to discover migrations: %v", err)
    }

    // Apply pending migrations
    if len(plan.Pending) > 0 {
        log.Printf("Applying %d pending migration(s)...", len(plan.Pending))

        applied, err := runner.ApplyUp(ctx, plan.Pending, false, nil)
        if err != nil {
            log.Fatalf("Migration failed: %v", err)
        }

        log.Printf("Successfully applied %d migration(s)", len(applied))
    } else {
        log.Println("No pending migrations")
    }
}

Embedded Migrations

Bundle migrations directly into your application binary:

package main

import (
    "context"
    "embed"
    "log"

    "github.com/mirajehossain/gomigratex/internal/db"
    "github.com/mirajehossain/gomigratex/internal/migrator"
)

//go:embed migrations/*.sql
var migrationFS embed.FS

func main() {
    ctx := context.Background()

    // Connect to database
    dsn := "user:password@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"
    database, err := db.OpenMySQL(dsn)
    if err != nil {
        log.Fatalf("Failed to connect: %v", err)
    }
    defer database.Close()

    // Create runner
    runner := migrator.NewRunner(database, "schema_migrations", "myapp")
    if err := runner.Ensure(ctx); err != nil {
        log.Fatalf("Failed to ensure schema: %v", err)
    }

    // Use embedded filesystem
    src := migrator.FileSource{
        FS:       migrationFS,
        RootDir:  "migrations",
        Embedded: true,
    }

    // Plan and apply
    plan, err := migrator.DiscoverAndPlan(ctx, src, runner.Storage)
    if err != nil {
        log.Fatalf("Failed to plan: %v", err)
    }

    if len(plan.Pending) > 0 {
        applied, err := runner.ApplyUp(ctx, plan.Pending, false, nil)
        if err != nil {
            log.Fatalf("Migration failed: %v", err)
        }
        log.Printf("Applied %d migration(s)", len(applied))
    }
}

Advanced: Custom Logger Integration

Integrate with your existing logging infrastructure:

package main

import (
    "context"
    "log/slog"
    "os"

    "github.com/mirajehossain/gomigratex/internal/db"
    "github.com/mirajehossain/gomigratex/internal/migrator"
)

func main() {
    ctx := context.Background()

    // Set up structured logger
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))

    database, err := db.OpenMySQL(os.Getenv("DB_DSN"))
    if err != nil {
        logger.Error("database connection failed", "error", err)
        os.Exit(1)
    }
    defer database.Close()

    runner := migrator.NewRunner(database, "schema_migrations", "myapp")

    // Custom callback for migration events
    callback := func(version, name, direction string, success bool, duration int64) {
        logger.Info("migration completed",
            "version", version,
            "name", name,
            "direction", direction,
            "success", success,
            "duration_ms", duration,
        )
    }

    if err := runner.Ensure(ctx); err != nil {
        logger.Error("ensure failed", "error", err)
        os.Exit(1)
    }

    src := migrator.FileSource{RootDir: "./migrations"}
    plan, err := migrator.DiscoverAndPlan(ctx, src, runner.Storage)
    if err != nil {
        logger.Error("plan failed", "error", err)
        os.Exit(1)
    }

    if len(plan.Pending) > 0 {
        applied, err := runner.ApplyUp(ctx, plan.Pending, false, callback)
        if err != nil {
            logger.Error("migration failed", "error", err)
            os.Exit(1)
        }
        logger.Info("migrations completed", "count", len(applied))
    }
}

Migration Files

Naming Convention

Migration files must follow this strict naming pattern:

{timestamp}_{description}.{direction}.sql

Components:

  • timestamp: 14-digit timestamp in format YYYYMMDDHHmmss
  • description: Lowercase descriptive name with underscores
  • direction: Either up (forward) or down (rollback)
  • Extension: Always .sql

Examples:

20250109120000_create_users_table.up.sql
20250109120000_create_users_table.down.sql
20250109120100_add_user_email_index.up.sql
20250109120100_add_user_email_index.down.sql
20250109120200_add_posts_table.up.sql
20250109120200_add_posts_table.down.sql

Migration File Structure

Forward Migration (up):

-- 20250109120000_create_users_table.up.sql
-- Create users table with proper indexes

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(255) NOT NULL,
    username VARCHAR(100) NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uniq_email (email),
    INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Rollback Migration (down):

-- 20250109120000_create_users_table.down.sql
-- Drop users table

DROP TABLE IF EXISTS users;

Multi-Statement Migrations

For complex migrations with multiple statements, ensure multiStatements=true is in your DSN:

-- 20250109120100_add_user_profiles.up.sql

CREATE TABLE user_profiles (
    user_id BIGINT PRIMARY KEY,
    bio TEXT,
    avatar_url VARCHAR(500),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE user_settings (
    user_id BIGINT PRIMARY KEY,
    theme VARCHAR(20) DEFAULT 'light',
    notifications_enabled BOOLEAN DEFAULT true,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Create indexes
CREATE INDEX idx_profiles_avatar ON user_profiles(avatar_url);
CREATE INDEX idx_settings_theme ON user_settings(theme);

Configuration

Environment Variables

gomigratex supports the following environment variables:

Variable Description Default
DB_DSN MySQL connection string -
MIGRATIONS_DIR Path to migrations directory ./migrations
MIGRATIONS_TABLE Tracking table name schema_migrations
LOCK_TIMEOUT_SEC Advisory lock timeout 30
APPLIED_BY Identifier for who applied migration Current user

DSN Format

The MySQL DSN (Data Source Name) must include specific parameters:

username:password@tcp(host:port)/database?parseTime=true&multiStatements=true

Required parameters:

  • parseTime=true - Enable automatic time.Time parsing
  • multiStatements=true - Allow multiple SQL statements per migration

Example:

export DB_DSN="myuser:mypassword@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"

With connection pooling:

user:password@tcp(host:port)/db?parseTime=true&multiStatements=true&maxAllowedPacket=67108864&maxOpenConns=25&maxIdleConns=5&connMaxLifetime=300s

Configuration File

Create migratex.yaml for complex configurations:

# Database connection
dsn: "user:password@tcp(localhost:3306)/mydb?parseTime=true&multiStatements=true"

# Migration settings
dir: "./db/migrations"
table: "schema_migrations"
lock_timeout_sec: 60

# Execution settings
applied_by: "ci-deployment"
verbose: true
json: true
dry_run: false

Usage:

migratex up --config migratex.yaml

Advanced Topics

Database Schema

gomigratex creates a tracking table to manage migration state:

CREATE TABLE schema_migrations (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    version VARCHAR(64) NOT NULL,
    name VARCHAR(255) NOT NULL,
    checksum CHAR(64) NOT NULL,
    applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    applied_by VARCHAR(255) NOT NULL,
    duration_ms BIGINT NOT NULL,
    status ENUM('success','failed') NOT NULL,
    execution_order BIGINT NOT NULL,
    UNIQUE KEY uniq_version_name (version, name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Fields:

  • version - Migration timestamp
  • name - Migration description
  • checksum - SHA-256 hash of migration file
  • applied_at - When migration was executed
  • applied_by - Who/what applied the migration
  • duration_ms - Execution time in milliseconds
  • status - Success or failure status
  • execution_order - Order of execution

Checksum Validation

gomigratex uses SHA-256 checksums to detect changes to applied migrations:

  1. When a migration is applied, its checksum is stored
  2. On subsequent runs, file checksums are compared with stored values
  3. Mismatches indicate drift and trigger errors

Handling drift:

# After intentionally editing an applied migration
migratex repair --dsn "$DB_DSN" --dir ./migrations

Warning: Only use repair when you understand the implications. Changing applied migrations can lead to inconsistent database states across environments.

Advisory Locking

gomigratex uses MySQL's GET_LOCK() function to prevent concurrent migrations:

SELECT GET_LOCK('gomigratex_lock', 30)

Benefits:

  • Prevents race conditions in multi-instance deployments
  • Ensures only one migration runs at a time
  • Automatically releases locks on connection close

Timeout handling:

# Increase lock timeout for slow migrations
migratex up --dsn "$DB_DSN" --dir ./migrations --lock-timeout 120

Baseline Migrations

For existing databases, use baseline to mark migrations as applied without executing them:

# Mark all migrations up to and including version 20250109120500 as applied
migratex force 20250109120500 --dsn "$DB_DSN" --dir ./migrations

Use cases:

  • Importing existing production database
  • Starting migrations on legacy systems
  • Synchronizing new environments with production

Dry Run Mode

Preview what migrations would be applied without executing them:

migratex up --dsn "$DB_DSN" --dir ./migrations --dry-run

Output:

DRY RUN MODE - No changes will be applied

Pending migrations:
  1. 20250109120000_create_users_table.up.sql
  2. 20250109120100_add_user_profiles.up.sql
  3. 20250109120200_add_posts_table.up.sql

Total: 3 migrations would be applied

Best Practices

1. Always Write Reversible Migrations

Every up migration should have a corresponding down migration:

-- up.sql
CREATE TABLE posts (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    content TEXT
);

-- down.sql
DROP TABLE IF EXISTS posts;

2. Use Transactions for Data Integrity

Wrap complex migrations in transactions:

-- up.sql
START TRANSACTION;

CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    total DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE INDEX idx_orders_user_id ON orders(user_id);

INSERT INTO orders (user_id, total)
SELECT id, 0.00 FROM users;

COMMIT;

3. Test Migrations Thoroughly

Always test both forward and rollback migrations:

# Test forward migration
migratex up --dsn "$DB_DSN" --dir ./migrations

# Verify application works correctly

# Test rollback
migratex down 1 --dsn "$DB_DSN" --dir ./migrations

# Test forward again
migratex up --dsn "$DB_DSN" --dir ./migrations

4. Use Descriptive Migration Names

# Good ✅
migratex create add_user_email_verification
migratex create create_product_inventory_table
migratex create add_index_to_orders_created_at

# Bad ❌
migratex create migration1
migratex create update_db
migratex create fix

5. Handle Data Migrations Carefully

When modifying columns with existing data:

-- up.sql
-- Step 1: Add new column with default
ALTER TABLE users
ADD COLUMN full_name VARCHAR(255) DEFAULT '';

-- Step 2: Populate data
UPDATE users
SET full_name = CONCAT(first_name, ' ', last_name)
WHERE full_name = '';

-- Step 3: Make NOT NULL if needed
ALTER TABLE users
MODIFY COLUMN full_name VARCHAR(255) NOT NULL;

-- Step 4: Drop old columns
ALTER TABLE users
DROP COLUMN first_name,
DROP COLUMN last_name;

6. Keep Migrations Small and Focused

# Instead of one large migration
migratex create massive_database_overhaul

# Create several focused migrations
migratex create add_users_table
migratex create add_posts_table
migratex create add_comments_table
migratex create add_foreign_keys
migratex create add_indexes

7. Document Complex Migrations

-- 20250109120000_optimize_user_queries.up.sql
-- This migration improves user query performance by:
-- 1. Adding composite index on (email, created_at)
-- 2. Converting user_sessions table to InnoDB
-- 3. Partitioning by created_at month
--
-- Expected downtime: ~5 seconds on 1M row table
-- Performance improvement: 3x faster user lookups
-- Rollback safety: Safe, indexes can be dropped without data loss

CREATE INDEX idx_user_email_created ON users(email, created_at);

ALTER TABLE user_sessions ENGINE=InnoDB;

8. Use Consistent Charset and Collation

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

9. Handle Migration Failures Gracefully

Design migrations to be rerunnable or easily fixable:

-- Use IF NOT EXISTS where possible
CREATE TABLE IF NOT EXISTS users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT
);

-- Use DROP IF EXISTS for cleanup
DROP TABLE IF EXISTS temp_migration_data;

10. Version Control Everything

# Add migrations to git
git add migrations/
git commit -m "Add user authentication migrations"

# Never manually edit applied migrations
# Instead, create a new migration to fix issues

Troubleshooting

Common Issues and Solutions

1. migratex Command Not Found After Installation

Error:

$ migratex -v
zsh: command not found: migratex

Cause: The Go binary was installed but the Go bin directory is not in your PATH environment variable.

Diagnosis:

# Check if Go is properly installed
go version

# Check Go environment
go env GOPATH
go env GOBIN

# Check if binary was installed
ls -la $(go env GOPATH)/bin/ | grep migratex

# Check if Go bin is in PATH
echo $PATH | tr ':' '\n' | grep go

Solutions:

Option A: Add Go bin to PATH (Recommended) Add this line to your shell profile (~/.zshrc for zsh or ~/.bash_profile for bash):

export PATH=$PATH:$(go env GOPATH)/bin

Then reload your shell:

source ~/.zshrc  # for zsh
# or
source ~/.bash_profile  # for bash

Option B: Install with explicit GOBIN

export GOBIN=/usr/local/bin
go install github.com/mirajehossain/gomigratex/cmd/migratex@latest

Option C: Use full path temporarily

$(go env GOPATH)/bin/migratex -v

Verification:

which migratex
migratex -v
# Should output: gomigratex v1.0.x

2. multiStatements=true Required

Error:

Error: You have an error in your SQL syntax

Solution: Add multiStatements=true to your DSN:

export DB_DSN="user:pass@tcp(localhost:3306)/db?parseTime=true&multiStatements=true"

3. Checksum Drift Detected

Error:

Error: checksum drift detected for migration 20250109120000:create_users_table
Expected: a1b2c3d4...
Got: e5f6g7h8...

Solution: If the file was intentionally modified:

migratex repair --dsn "$DB_DSN" --dir ./migrations

If not intentional, restore the original file from version control.

4. Advisory Lock Timeout

Error:

Error: failed to acquire advisory lock after 30 seconds

Causes:

  • Another migration process is running
  • Previous migration didn't release lock (crashed)
  • Network issues

Solutions:

# Check for other migration processes
ps aux | grep migratex

# Increase timeout
migratex up --dsn "$DB_DSN" --dir ./migrations --lock-timeout 120

# Manually release lock in MySQL
mysql> SELECT RELEASE_LOCK('gomigratex_lock');

5. Migration File Not Found

Error:

Error: migration file not found: 20250109120000_create_users_table.down.sql

Solution: Ensure both .up.sql and .down.sql files exist for each migration:

ls -la migrations/

6. Connection Issues

Error:

Error: failed to connect to database: dial tcp: lookup localhost: no such host

Solutions:

# Verify DSN format
echo $DB_DSN

# Test connection manually
mysql -h localhost -u user -p database

# Check network connectivity
ping localhost

# Try IP address instead of hostname
export DB_DSN="user:pass@tcp(127.0.0.1:3306)/db?parseTime=true&multiStatements=true"

7. Permission Denied

Error:

Error: CREATE TABLE command denied to user 'app'@'localhost'

Solution: Grant necessary permissions:

GRANT CREATE, ALTER, DROP, INDEX ON database.* TO 'app'@'localhost';
FLUSH PRIVILEGES;

8. Incorrect Migration Order

Error:

Error: foreign key constraint fails

Solution: Ensure migrations are numbered in the correct order:

# List migrations to check order
ls -la migrations/ | sort

# If needed, rename migrations to fix ordering
mv 20250109120200_add_fk.up.sql 20250109120150_add_fk.up.sql

Debug Mode

Enable verbose logging for detailed information:

migratex up --dsn "$DB_DSN" --dir ./migrations --verbose

Output:

INFO: Acquiring advisory lock...
INFO: Lock acquired successfully
INFO: Scanning directory: ./migrations
INFO: Found 3 migration files
INFO: Planning migrations...
INFO: Executing: 20250109120000_create_users_table.up.sql
INFO: Migration completed in 145ms
INFO: Executing: 20250109120100_add_user_profiles.up.sql
INFO: Migration completed in 89ms
INFO: All migrations applied successfully
INFO: Releasing advisory lock

Getting Help

If you encounter issues not covered here:

  1. Check existing issues: GitHub Issues
  2. Search discussions: GitHub Discussions
  3. Create new issue: Provide:
    • Go version (go version)
    • gomigratex version (migratex version)
    • MySQL version
    • Complete error message
    • Migration files (if relevant)
    • Steps to reproduce

Contributing

We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, we appreciate your help.

How to Contribute

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Write tests for new functionality
  5. Ensure tests pass (make test)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Development Setup

# Clone the repository
git clone https://github.com/mirajehossain/gomigratex.git
cd gomigratex

# Install dependencies
go mod download

# Run tests
make test

# Run specific package tests
go test ./internal/migrator -v

# Build the CLI
go build -o migratex ./cmd/migratex

# Run integration tests (requires MySQL)
docker-compose up -d
DB_DSN="root:testpass@tcp(127.0.0.1:3306)/testdb?parseTime=true&multiStatements=true" go test -v ./...
docker-compose down

Project Structure

gomigratex/
├── cmd/
│   └── migratex/           # CLI entry point
├── internal/
│   ├── checksum/           # SHA-256 checksum utilities
│   ├── config/             # Configuration management
│   ├── db/                 # Database connection & operations
│   ├── fsutil/             # File system utilities
│   ├── lock/               # Advisory locking mechanism
│   ├── logger/             # Logging utilities
│   └── migrator/           # Core migration logic
│       ├── migrator.go     # Main migration runner
│       ├── planner.go      # Migration planning
│       ├── storage.go      # Database storage interface
│       └── model.go        # Data models
├── examples/               # Usage examples
├── migrations/             # Sample migrations
├── CONTRIBUTING.md         # Contribution guidelines
├── README.md              # This file
├── LICENSE                # MIT License
├── Makefile               # Build and test commands
└── go.mod                 # Go module definition

Code Standards

  • Follow Effective Go guidelines
  • Use gofmt for code formatting
  • Write tests for new functionality (aim for >80% coverage)
  • Add godoc comments for exported functions and types
  • Keep functions small and focused
  • Use meaningful variable and function names

Testing

# Run all tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

# Run specific test
go test -run TestMigrationPlanner ./internal/migrator

# Run with race detector
go test -race ./...

Pull Request Guidelines

  • Keep PRs focused: One feature or bug fix per PR
  • Write clear descriptions: Explain what and why, not just how
  • Link related issues: Use "Fixes #123" or "Relates to #456"
  • Update documentation: If you change behavior, update README
  • Add tests: New features need tests
  • Pass CI checks: Ensure all tests and lints pass

Reporting Bugs

When reporting bugs, please include:

  1. Clear title: Summarize the issue
  2. Description: What happened vs. what you expected
  3. Steps to reproduce: Detailed steps
  4. Environment: OS, Go version, MySQL version, gomigratex version
  5. Code samples: Minimal reproducible example
  6. Error messages: Complete error output

Feature Requests

We love new ideas! When requesting features:

  1. Check existing issues first
  2. Describe the problem you're trying to solve
  3. Suggest a solution if you have one in mind
  4. Provide use cases to help us understand the value

For more details, see CONTRIBUTING.md.

Versioning

gomigratex follows Semantic Versioning:

  • Major version (v1.x.x → v2.x.x): Breaking changes
  • Minor version (v1.0.x → v1.1.x): New features, backward compatible
  • Patch version (v1.0.0 → v1.0.1): Bug fixes, backward compatible

Checking Version

# Multiple ways to check version
migratex -v
migratex --version
migratex version

Output:

gomigratex v1.0.2

Version Compatibility

Version Status Go Version MySQL Version
v1.x.x ✅ Stable 1.22+ 5.7+, 8.0+
v0.x.x ⚠️ Beta 1.22+ 5.7+, 8.0+

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with ❤️ for the Go community

FAQ

Q: Can I use gomigratex with databases other than MySQL? A: Currently, gomigratex is optimized specifically for MySQL. Support for PostgreSQL and other databases may be added in future versions.

Q: Is it safe to run migrations in production? A: Yes, gomigratex is production-ready with features like advisory locking, checksum validation, and dry-run mode. However, always test migrations in staging first and have backups.

Q: Can multiple instances run migrations simultaneously? A: No, the advisory lock mechanism ensures only one instance can run migrations at a time, preventing race conditions.

Q: How do I handle long-running migrations? A: Use the --lock-timeout flag to increase the timeout, and consider breaking large migrations into smaller chunks. Monitor your database during execution.

Q: Can I edit an already-applied migration? A: It's not recommended. If you must, use migratex repair to update checksums. Better practice is to create a new migration to modify previous changes.

Q: How do I migrate an existing production database? A: Use the force command to baseline your existing schema, then apply new migrations from that point forward.

Q: Does gomigratex support transaction rollback on failure? A: Individual migration files can use transactions. If a migration fails, it won't be marked as applied, allowing you to fix and retry.

Q: Can I use gomigratex in CI/CD pipelines? A: Absolutely! Use the --json flag for structured output and the --dry-run flag in validation stages.


Star ⭐ this repository if you find it useful!

Follow @mirajehossain for updates.

Questions? Open an issue or discussion.

About

A lightweight, production-ready MySQL migration tool for Go applications with support for both CLI usage and library integration.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published