A lightweight, production-ready MySQL migration tool for Go applications with support for both CLI usage and library integration.
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.
- 🚀 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
embedpackage - 🔒 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
- Installation
- Quick Start
- CLI Usage
- Library Usage
- Migration Files
- Configuration
- Advanced Topics
- Best Practices
- Troubleshooting
- Contributing
- License
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/migratexmacOS (Intel):
wget https://github.com/mirajehossain/gomigratex/releases/latest/download/migratex-darwin
chmod +x migratex-darwin
sudo mv migratex-darwin /usr/local/bin/migratexmacOS (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/migratexWindows: Download migratex-windows.exe and add to your PATH.
Verify installation:
migratex -v
# Output: gomigratex v1.0.0Install the latest version directly from GitHub:
go install github.com/mirajehossain/gomigratex/cmd/migratex@latestThis will automatically use the latest release version.
Add to your Go project:
go get github.com/mirajehossain/gomigratex@latestUse a specific version:
go get github.com/mirajehossain/gomigratex@v1.0.0Create your first migration using the CLI:
migratex create add_users_table --dir ./migrationsThis generates two files:
20250109120000_add_users_table.up.sql- Forward migration20250109120000_add_users_table.down.sql- Rollback migration
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;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 ./migrationsOutput:
Applied 1 migration(s) successfully
✓ 20250109120000_add_users_table.up.sql
| 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 |
| 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 |
Apply all pending migrations:
migratex up --dsn "$DB_DSN" --dir ./migrationsApply with verbose output:
migratex up --dsn "$DB_DSN" --dir ./migrations --verbosePreview migrations without applying (dry run):
migratex up --dsn "$DB_DSN" --dir ./migrations --dry-runGet JSON output for automation:
migratex status --dsn "$DB_DSN" --dir ./migrations --jsonCreate a new migration:
migratex create add_user_indexes --dir ./migrationsRollback the last migration:
migratex down 1 --dsn "$DB_DSN" --dir ./migrationsRollback all migrations:
migratex down all --dsn "$DB_DSN" --dir ./migrationsBaseline an existing database:
# Mark all migrations up to version 20250109120000 as applied
migratex force 20250109120000 --dsn "$DB_DSN" --dir ./migrationsRepair checksums after manual edits:
migratex repair --dsn "$DB_DSN" --dir ./migrationsCreate 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: falseUse the configuration file:
migratex up --config migratex.yamlIntegrate 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")
}
}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))
}
}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 must follow this strict naming pattern:
{timestamp}_{description}.{direction}.sql
Components:
timestamp: 14-digit timestamp in formatYYYYMMDDHHmmssdescription: Lowercase descriptive name with underscoresdirection: Eitherup(forward) ordown(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
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;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);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 |
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 parsingmultiStatements=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
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: falseUsage:
migratex up --config migratex.yamlgomigratex 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 timestampname- Migration descriptionchecksum- SHA-256 hash of migration fileapplied_at- When migration was executedapplied_by- Who/what applied the migrationduration_ms- Execution time in millisecondsstatus- Success or failure statusexecution_order- Order of execution
gomigratex uses SHA-256 checksums to detect changes to applied migrations:
- When a migration is applied, its checksum is stored
- On subsequent runs, file checksums are compared with stored values
- Mismatches indicate drift and trigger errors
Handling drift:
# After intentionally editing an applied migration
migratex repair --dsn "$DB_DSN" --dir ./migrationsWarning: Only use repair when you understand the implications. Changing applied migrations can lead to inconsistent database states across environments.
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 120For 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 ./migrationsUse cases:
- Importing existing production database
- Starting migrations on legacy systems
- Synchronizing new environments with production
Preview what migrations would be applied without executing them:
migratex up --dsn "$DB_DSN" --dir ./migrations --dry-runOutput:
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
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;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;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# 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 fixWhen 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;# 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-- 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;CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;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;# 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 issuesError:
$ migratex -v
zsh: command not found: migratexCause: 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 goSolutions:
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)/binThen reload your shell:
source ~/.zshrc # for zsh
# or
source ~/.bash_profile # for bashOption B: Install with explicit GOBIN
export GOBIN=/usr/local/bin
go install github.com/mirajehossain/gomigratex/cmd/migratex@latestOption C: Use full path temporarily
$(go env GOPATH)/bin/migratex -vVerification:
which migratex
migratex -v
# Should output: gomigratex v1.0.xError:
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"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 ./migrationsIf not intentional, restore the original file from version control.
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');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/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"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;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.sqlEnable verbose logging for detailed information:
migratex up --dsn "$DB_DSN" --dir ./migrations --verboseOutput:
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
If you encounter issues not covered here:
- Check existing issues: GitHub Issues
- Search discussions: GitHub Discussions
- Create new issue: Provide:
- Go version (
go version) - gomigratex version (
migratex version) - MySQL version
- Complete error message
- Migration files (if relevant)
- Steps to reproduce
- Go version (
We welcome contributions from the community! Whether it's bug reports, feature requests, documentation improvements, or code contributions, we appreciate your help.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Write tests for new functionality
- Ensure tests pass (
make test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# 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 downgomigratex/
├── 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
- Follow Effective Go guidelines
- Use
gofmtfor 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
# 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 ./...- 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
When reporting bugs, please include:
- Clear title: Summarize the issue
- Description: What happened vs. what you expected
- Steps to reproduce: Detailed steps
- Environment: OS, Go version, MySQL version, gomigratex version
- Code samples: Minimal reproducible example
- Error messages: Complete error output
We love new ideas! When requesting features:
- Check existing issues first
- Describe the problem you're trying to solve
- Suggest a solution if you have one in mind
- Provide use cases to help us understand the value
For more details, see CONTRIBUTING.md.
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
# Multiple ways to check version
migratex -v
migratex --version
migratex versionOutput:
gomigratex v1.0.2
| Version | Status | Go Version | MySQL Version |
|---|---|---|---|
| v1.x.x | ✅ Stable | 1.22+ | 5.7+, 8.0+ |
| v0.x.x | 1.22+ | 5.7+, 8.0+ |
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with ❤️ for the Go community
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.