README
¶
bedrock-migrate
A flexible database migration tool for PostgreSQL and MongoDB, written in Go. Works both as a standalone CLI and as an embeddable library.
Features
- Multi-Database Support: PostgreSQL and MongoDB
- Dual Usage: Standalone CLI or embeddable library
- Transaction Safety: PostgreSQL migrations run in transactions
- Migration Tracking: Prevents re-applying migrations
- Flexible Commands: Apply/revert migrations by count or version
- Dry Run Mode: Preview changes before applying
- Colored Output: Easy-to-read status messages
- Verbose Logging: Detailed execution information
Installation
As a Standalone CLI
go install github.com/Jack4Code/bedrock-migrate/cmd/migrate@latest
Or build from source:
git clone https://github.com/Jack4Code/bedrock-migrate.git
cd bedrock-migrate
go build -o migrate ./cmd/migrate
sudo mv migrate /usr/local/bin/
As a Library
go get github.com/Jack4Code/bedrock-migrate
Configuration
PostgreSQL
Configure using environment variables:
Option 1: Connection URL
export DATABASE_URL="postgresql://user:password@localhost:5432/dbname?sslmode=disable"
Option 2: Individual Variables
export PG_HOST=localhost
export PG_PORT=5432
export PG_USER=postgres
export PG_PASSWORD=yourpassword
export PG_DATABASE=mydb
MongoDB
Configure using environment variables:
Option 1: Connection URL
export MONGO_URL="mongodb://user:password@localhost:27017"
export MONGO_DATABASE=mydb
Option 2: Individual Variables
export MONGO_HOST=localhost
export MONGO_PORT=27017
export MONGO_USER=admin
export MONGO_PASSWORD=yourpassword
export MONGO_DATABASE=mydb
Usage
CLI Commands
Create a New Migration
# PostgreSQL
migrate create add_users_table --type postgres
# MongoDB
migrate create add_users_collection --type mongo
This creates two files:
migrations/postgres/20240101120000_add_users_table.up.sqlmigrations/postgres/20240101120000_add_users_table.down.sql
Apply Migrations
# Apply all pending migrations
migrate up --type postgres
# Apply migrations up to a specific version
migrate up --to 20240101120000 --type postgres
# Apply next N migrations
migrate up -n 2 --type postgres
# Dry run (show what would be applied)
migrate up --dry-run --type postgres
# Verbose output
migrate up -v --type postgres
Revert Migrations
# Revert last migration
migrate down --type postgres
# Revert migrations down to a specific version
migrate down --to 20240101120000 --type postgres
# Revert last N migrations
migrate down -n 2 --type postgres
# Dry run (show what would be reverted)
migrate down --dry-run --type postgres
Check Migration Status
migrate status --type postgres
Example output:
Migration Status:
=================
Version Name Status
------------------------------------------------------------
20240101120000 create_users_table APPLIED
20240102150000 add_products_table PENDING
------------------------------------------------------------
Total: 2 | Applied: 1 | Pending: 1
Show Current Version
migrate version --type postgres
Using as a Library
You can embed bedrock-migrate commands into your own application:
package main
import (
"github.com/Jack4Code/bedrock-migrate/pkg/commands"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "myapp",
Short: "My application",
}
// Add the migrate command to your app
rootCmd.AddCommand(commands.MigrateCommand())
// Add other commands...
// rootCmd.AddCommand(otherCommand())
rootCmd.Execute()
}
Now you can run:
myapp migrate up --type postgres
myapp migrate status --type postgres
Programmatic Usage
You can also use the migration library programmatically:
package main
import (
"log"
"github.com/Jack4Code/bedrock-migrate/pkg/migration"
)
func main() {
// Create a PostgreSQL driver
driver, err := migration.NewPostgresDriver()
if err != nil {
log.Fatal(err)
}
defer driver.Close()
// Create a migration manager
config := &migration.Config{
MigrationsDir: "./migrations",
DBType: "postgres",
Verbose: true,
DryRun: false,
}
manager := migration.NewManager(driver, config)
// Apply all pending migrations
if err := manager.MigrateUp(0, 0); err != nil {
log.Fatal(err)
}
// Get current version
version, err := manager.GetCurrentVersion()
if err != nil {
log.Fatal(err)
}
log.Printf("Current version: %d\n", version)
}
Migration Files
Directory Structure
migrations/
├── postgres/
│ ├── 20240101120000_create_users_table.up.sql
│ ├── 20240101120000_create_users_table.down.sql
│ ├── 20240102150000_add_products_table.up.sql
│ └── 20240102150000_add_products_table.down.sql
└── mongo/
├── 20240101120000_create_users_collection.up.js
├── 20240101120000_create_users_collection.down.js
├── 20240102150000_add_products_collection.up.js
└── 20240102150000_add_products_collection.down.js
PostgreSQL Migration Example
20240101120000_create_users_table.up.sql
-- Create users table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create index on email
CREATE INDEX idx_users_email ON users(email);
20240101120000_create_users_table.down.sql
-- Drop users table
DROP TABLE IF EXISTS users;
MongoDB Migration Example
20240101120000_create_users_collection.up.js
// Create users collection with validation
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["email", "username"],
properties: {
email: {
bsonType: "string",
description: "must be a string and is required"
},
username: {
bsonType: "string",
description: "must be a string and is required"
}
}
}
}
});
// Create indexes
db.users.createIndex({ email: 1 }, { unique: true });
20240101120000_create_users_collection.down.js
// Drop users collection
db.users.drop();
Migration Tracking
PostgreSQL
Creates a schema_migrations table:
CREATE TABLE schema_migrations (
version BIGINT PRIMARY KEY,
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
MongoDB
Creates a schema_migrations collection with documents:
{
version: 20240101120000,
applied_at: ISODate("2024-01-01T12:00:00Z")
}
Command-Line Flags
Global Flags
--dir <path>: Migrations directory (default:./migrations)--type <db>: Database type (postgresormongo) - required-v, --verbose: Enable verbose output--dry-run: Show what would happen without executing
Command-Specific Flags
up command:
--to <version>: Apply migrations up to this version-n <count>: Apply next N migrations
down command:
--to <version>: Revert migrations down to this version-n <count>: Revert last N migrations
Best Practices
- Always write down migrations: Every up migration should have a corresponding down migration
- Test migrations: Use
--dry-runto preview changes before applying - Use transactions: PostgreSQL migrations automatically run in transactions
- Version control: Commit migration files to your repository
- Backup your data: Always backup before running migrations in production
- Sequential migrations: Don't modify existing migration files after they've been applied
Error Handling
- Migrations run in transactions (PostgreSQL) - failed migrations will rollback
- Clear error messages indicate which migration failed and why
- Migration tracking ensures failed migrations aren't marked as applied
- Use
--verboseflag for detailed error information
Development
Building
go build -o migrate ./cmd/migrate
Running Tests
go test ./...
Project Structure
bedrock-migrate/
├── cmd/
│ └── migrate/
│ └── main.go # CLI entry point
├── pkg/
│ ├── commands/ # Cobra commands (exportable)
│ │ ├── root.go
│ │ ├── create.go
│ │ ├── up.go
│ │ ├── down.go
│ │ ├── status.go
│ │ └── version.go
│ └── migration/ # Core migration logic
│ ├── migration.go # Interfaces and manager
│ ├── postgres.go # PostgreSQL driver
│ └── mongo.go # MongoDB driver
└── migrations/ # Example migrations
├── postgres/
└── mongo/
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details
Support
For issues and questions:
- Open an issue on GitHub
- Check existing documentation
Changelog
v1.0.0
- Initial release
- PostgreSQL support
- MongoDB support
- CLI and library usage
- Migration tracking
- Dry-run mode
- Colored output