{"id":43053582,"url":"https://github.com/alexisvisco/amigo","last_synced_at":"2026-01-31T11:03:17.154Z","repository":{"id":236263522,"uuid":"786988084","full_name":"alexisvisco/amigo","owner":"alexisvisco","description":"A database migration tool in Go language, supporting go and sql. 0 dependencies.","archived":false,"fork":false,"pushed_at":"2026-01-16T21:45:01.000Z","size":5929,"stargazers_count":32,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-20T13:54:41.211Z","etag":null,"topics":["go-migration","golang","golang-library","migrations","postgresql","sql"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexisvisco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-04-15T17:15:54.000Z","updated_at":"2026-01-16T21:33:00.000Z","dependencies_parsed_at":"2024-04-26T10:44:41.432Z","dependency_job_id":"33ada85d-2af1-40fb-b331-1232ac6600e4","html_url":"https://github.com/alexisvisco/amigo","commit_stats":null,"previous_names":["alexisvisco/mig"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/alexisvisco/amigo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexisvisco%2Famigo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexisvisco%2Famigo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexisvisco%2Famigo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexisvisco%2Famigo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexisvisco","download_url":"https://codeload.github.com/alexisvisco/amigo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexisvisco%2Famigo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28939508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T10:18:23.202Z","status":"ssl_error","status_checked_at":"2026-01-31T10:18:22.693Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go-migration","golang","golang-library","migrations","postgresql","sql"],"created_at":"2026-01-31T11:03:16.610Z","updated_at":"2026-01-31T11:03:17.145Z","avatar_url":"https://github.com/alexisvisco.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Amigo - Go SQL Migration Tool\n\nA simple, powerful SQL migration tool for Go with support for both SQL and Go migrations.\n\nAmigo provides a clean API for managing database migrations with built-in CLI support and programmatic access. Write migrations in SQL or Go, control transactions, and get real-time feedback during execution.\n\n## Features\n\n- **SQL and Go migrations** - Write migrations in SQL files or Go code\n- **Embedded migrations** - SQL files are embedded in binary via `embed.FS` for portability\n- **Transaction control** - Fine-grained control over transaction behavior\n- **Multiple database support** - PostgreSQL, SQLite, ClickHouse drivers included\n- **CLI tool** - Built-in CLI for managing migrations\n- **Programmatic API** - Use migrations directly in your Go code\n- **Standard library only** - No external dependencies \n\n## Installation\n\n```bash\ngo get github.com/alexisvisco/amigo\n```\n\n## Quick Start\n\n### 1. Setup your project structure\n\nCreate the following structure:\n\n```\nyourapp/\n├── cmd/\n│   └── migrate/\n│       └── main.go\n├── migrations/\n│   └── migrations.go\n└── go.mod\n```\n\n### 2. Create your migration CLI\n\nCreate `cmd/migrate/main.go`:\n\n```go\npackage main\n\nimport (\n    \"database/sql\"\n    \"log\"\n    \"os\"\n    \n    \"github.com/alexisvisco/amigo\"\n    \"yourapp/migrations\"\n    _ \"modernc.org/sqlite\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"sqlite\", \"app.db\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n    \n    config := amigo.DefaultConfiguration\n    config.DB = db\n    config.Driver = amigo.NewSQLiteDriver(\"\")\n    \n    // Load migrations\n    migrationList := migrations.Migrations(config)\n    \n    cli := amigo.NewCLI(amigo.CLIConfig{\n        Config:               config,\n        Migrations:           migrationList,\n        Directory:            \"migrations\",\n        DefaultTransactional: true,\n        DefaultFileFormat:    \"sql\",\n    })\n    \n    os.Exit(cli.Run(os.Args[1:]))\n}\n```\n\nCreate `migrations/migrations.go`:\n\n```go\npackage migrations\n\nimport (\n    \"embed\"\n    \n    \"github.com/alexisvisco/amigo\"\n)\n\n//go:embed *.sql\nvar sqlFiles embed.FS\n\nfunc Migrations(cfg amigo.Configuration) []amigo.Migration {\n    return []amigo.Migration{}\n}\n```\n\n**Note**: SQL migrations are embedded using `embed.FS`, making your migration binary portable with no external SQL files needed.\n\n### 3. Generate your first migration\n\n```bash\ngo run cmd/migrate/main.go generate create_users_table\n```\n\nThis creates `migrations/20240101120000_create_users_table.sql`:\n\n```sql\n-- migrate:up tx=true\nCREATE TABLE users (\n    id INTEGER PRIMARY KEY,\n    name TEXT NOT NULL,\n    email TEXT UNIQUE NOT NULL\n);\n\n-- migrate:down tx=true\nDROP TABLE users;\n```\n\nAnd automatically updates `migrations/migrations.go`:\n\n```go\npackage migrations\n\nimport (\n    \"embed\"\n    \n    \"github.com/alexisvisco/amigo\"\n)\n\n//go:embed *.sql\nvar sqlFiles embed.FS\n\nfunc Migrations(cfg amigo.Configuration) []amigo.Migration {\n    return []amigo.Migration{\n        amigo.SQLFileToMigration(sqlFiles, \"20240101120000_create_users_table.sql\", cfg),\n    }\n}\n```\n\nThe `//go:embed *.sql` directive embeds all SQL files into the binary, and `SQLFileToMigration` takes the embedded filesystem as its first argument.\n\n### 4. Run migrations\n\n```bash\n# Apply all pending migrations\ngo run cmd/migrate/main.go up\n\n# View status\ngo run cmd/migrate/main.go status\n```\n\n### 5. (Optional) Build the migration binary\n\n```bash\ngo build -o bin/migrate cmd/migrate/main.go\n\n# Use it\n./bin/migrate up\n./bin/migrate status\n```\n\n## CLI Commands\n\n### `generate` - Create a new migration\n\n```bash\n# Generate SQL migration\ngo run cmd/migrate/main.go generate create_users_table\n\n# Generate Go migration\ngo run cmd/migrate/main.go generate --format=go add_email_validation\n```\n\n### `up` - Apply pending migrations\n\n```bash\n# Apply all pending migrations\ngo run cmd/migrate/main.go up\n\n# Apply next 2 migrations\ngo run cmd/migrate/main.go up --steps=2\n\n# Skip confirmation\ngo run cmd/migrate/main.go up --yes\n```\n\n### `down` - Revert applied migrations\n\n```bash\n# Revert last migration\ngo run cmd/migrate/main.go down\n\n# Revert last 3 migrations\ngo run cmd/migrate/main.go down --steps=3\n\n# Revert all migrations\ngo run cmd/migrate/main.go down --steps=-1\n\n# Skip confirmation\ngo run cmd/migrate/main.go down --yes\n```\n\n### `status` - Show migration status\n\n```bash\ngo run cmd/migrate/main.go status\n```\n\nOutput:\n```\nMigration Status: 2 applied, 1 pending\n\nStatus   Date            Name          Applied At\npending  20240103100000  add_comments\napplied  20240102150000  add_posts     2024-01-02 15:30:45\napplied  20240101120000  create_users  2024-01-01 12:05:23\n```\n\n### `show-config` - Display configuration\n\n```bash\ngo run cmd/migrate/main.go show-config\n```\n\n## Using Migrations Programmatically (Without CLI)\n\nYou can run migrations directly in your Go code without using the CLI:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"database/sql\"\n    \"fmt\"\n    \"log\"\n    \n    \"github.com/alexisvisco/amigo\"\n    \"yourapp/migrations\"\n    _ \"modernc.org/sqlite\"\n)\n\nfunc main() {\n    // Open database\n    db, err := sql.Open(\"sqlite\", \"app.db\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n    \n    // Configure amigo\n    config := amigo.DefaultConfiguration\n    config.DB = db\n    config.Driver = amigo.NewSQLiteDriver(\"schema_migrations\")\n    \n    // Load migrations\n    migrationList := migrations.Migrations(config)\n    \n    // Create runner\n    runner := amigo.NewRunner(config)\n    ctx := context.Background()\n    \n    // Run all pending migrations\n    err = runner.Up(ctx, migrationList)\n    if err != nil {\n        log.Fatalf(\"Failed to run migrations: %v\", err)\n    }\n    \n    fmt.Println(\"Migrations applied successfully!\")\n}\n```\n\n### With Progress Feedback\n\nUse iterators for real-time progress:\n\n```go\n// Run migrations with progress feedback\nfor result := range runner.UpIterator(ctx, migrationList) {\n    if result.Error != nil {\n        log.Fatalf(\"Migration failed: %v\", result.Error)\n    }\n    fmt.Printf(\"✓ %s (%.2fs)\\n\", result.Migration.Name(), result.Duration.Seconds())\n}\n```\n\n### Revert Migrations\n\n```go\n// Revert last migration\nerr = runner.Down(ctx, migrationList, amigo.RunnerDownOptionSteps(1))\n\n// Revert with progress\nfor result := range runner.DownIterator(ctx, migrationList, amigo.RunnerDownOptionSteps(1)) {\n    if result.Error != nil {\n        log.Fatalf(\"Revert failed: %v\", result.Error)\n    }\n    fmt.Printf(\"✓ Reverted %s (%.2fs)\\n\", result.Migration.Name(), result.Duration.Seconds())\n}\n```\n\n### Check Migration Status\n\n```go\nstatuses, err := runner.GetMigrationsStatuses(ctx, migrationList)\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, status := range statuses {\n    if status.Applied {\n        fmt.Printf(\"✓ %s (applied at %s)\\n\", \n            status.Migration.Name, \n            status.Migration.AppliedAt.Format(\"2006-01-02 15:04:05\"))\n    } else {\n        fmt.Printf(\"○ %s (pending)\\n\", status.Migration.Name)\n    }\n}\n```\n\n## Writing Migrations\n\n### SQL Migrations\n\nSQL migrations use annotations to separate up and down migrations:\n\n```sql\n-- migrate:up tx=true\nCREATE TABLE posts (\n    id INTEGER PRIMARY KEY,\n    title TEXT NOT NULL,\n    body TEXT\n);\n\nCREATE INDEX idx_posts_title ON posts(title);\n\n-- migrate:down tx=true\nDROP TABLE posts;\n```\n\n#### Transaction Control\n\nControl transaction behavior per migration:\n\n```sql\n-- migrate:up tx=false\nCREATE INDEX CONCURRENTLY idx_users_email ON users(email);\n\n-- migrate:down tx=false\nDROP INDEX CONCURRENTLY idx_users_email;\n```\n\n### Go Migrations\n\nGo migrations give you full programmatic control:\n\n```go\npackage migrations\n\nimport (\n    \"context\"\n    \"database/sql\"\n    \n    \"github.com/alexisvisco/amigo\"\n)\n\ntype Migration20240101120000CreateUsers struct{}\n\nfunc (m Migration20240101120000CreateUsers) Name() string {\n    return \"create_users\"\n}\n\nfunc (m Migration20240101120000CreateUsers) Date() int64 {\n    return 20240101120000\n}\n\nfunc (m Migration20240101120000CreateUsers) Up(ctx context.Context, db *sql.DB) error {\n    return amigo.Tx(ctx, db, func(tx *sql.Tx) error {\n        _, err := tx.Exec(`\n            CREATE TABLE users (\n                id INTEGER PRIMARY KEY,\n                name TEXT NOT NULL\n            )\n        `)\n        return err\n    })\n}\n\nfunc (m Migration20240101120000CreateUsers) Down(ctx context.Context, db *sql.DB) error {\n    return amigo.Tx(ctx, db, func(tx *sql.Tx) error {\n        _, err := tx.Exec(`DROP TABLE users`)\n        return err\n    })\n}\n```\n\n#### Chaining Multiple Statements\n\nUse `ChainExecTx` to chain multiple SQL statements without repetitive error handling:\n\n```go\nfunc (m Migration20240101120000CreateUsers) Up(ctx context.Context, db *sql.DB) error {\n    return amigo.Tx(ctx, db, func(tx *sql.Tx) error {\n        return amigo.NewChainExecTx(ctx, tx).\n            Exec(`CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)`).\n            Exec(`CREATE INDEX idx_users_name ON users(name)`).\n            Exec(`INSERT INTO settings (key, value) VALUES ('version', '1')`).\n            Err()\n    })\n}\n```\n\nOr use `ChainExec` for non-transactional operations:\n\n```go\nfunc (m Migration20240101120000CreateUsers) Up(ctx context.Context, db *sql.DB) error {\n    return amigo.NewChainExec(ctx, db).\n        Exec(`CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`).\n        Exec(`CREATE INDEX CONCURRENTLY idx_users_created_at ON users(created_at)`).\n        Err()\n}\n```\n\n#### Without Transactions\n\n```go\nfunc (m Migration20240101120000CreateUsers) Up(ctx context.Context, db *sql.DB) error {\n    _, err := db.ExecContext(ctx, `CREATE INDEX CONCURRENTLY idx_users_email ON users(email)`)\n    return err\n}\n```\n\n## Configuration\n\n### Migration Configuration\n\n```go\nconfig := amigo.Configuration{\n    DB:                    db,\n    Driver:                driver,\n    SQLFileUpAnnotation:   \"-- migrate:up\",\n    SQLFileDownAnnotation: \"-- migrate:down\",\n}\n```\n\n### CLI Configuration\n\n```go\ncliConfig := amigo.CLIConfig{\n    Config:               config,\n    Migrations:           migrationList,\n    Output:               os.Stdout,\n    ErrorOut:             os.Stderr,\n    Directory:            \"db/migrations\",\n    DefaultTransactional: true,\n    DefaultFileFormat:    \"sql\",\n}\n\ncli := amigo.NewCLI(cliConfig)\n```\n\n## Database Drivers\n\n### PostgreSQL\n\n```go\nimport (\n    \"github.com/alexisvisco/amigo\"\n    _ \"github.com/lib/pq\"\n)\n\ndriver := amigo.NewPostgresDriver(\"schema_migrations\")\n```\n\n### SQLite\n\n```go\nimport (\n    \"github.com/alexisvisco/amigo\"\n    _ \"modernc.org/sqlite\"\n)\n\ndriver := amigo.NewSQLiteDriver(\"schema_migrations\")\n```\n\n### ClickHouse\n\n```go\nimport (\n    \"github.com/alexisvisco/amigo\"\n    _ \"github.com/ClickHouse/clickhouse-go/v2\"\n)\n\n// For standalone ClickHouse\ndriver := amigo.NewClickHouseDriver(\"schema_migrations\", \"\")\n\n// For clustered ClickHouse\ndriver := amigo.NewClickHouseDriver(\"schema_migrations\", \"{cluster}\")\n```\n\n**Note**: When using a cluster, the driver creates a `ReplicatedReplacingMergeTree` table and uses soft deletes for migration rollbacks. For standalone setups (empty cluster string), it uses `MergeTree` and hard deletes.\n\n## Multi-Database Setup\n\nIf you have multiple databases (e.g., PostgreSQL for main data and ClickHouse for analytics), create separate migration CLIs:\n\n### PostgreSQL Migration CLI (`cmd/migrate-postgres/main.go`)\n\n```go\npackage main\n\nimport (\n    \"database/sql\"\n    \"log\"\n    \"os\"\n    \n    \"github.com/alexisvisco/amigo\"\n    \"yourapp/migrations/postgres\"\n    _ \"github.com/lib/pq\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"postgres\", \"postgres://user:pass@localhost/mydb?sslmode=disable\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n    \n    config := amigo.DefaultConfiguration\n    config.DB = db\n    config.Driver = amigo.NewPostgresDriver(\"schema_migrations\")\n    \n    migrationList := postgres.Migrations(config)\n    \n    cli := amigo.NewCLI(amigo.CLIConfig{\n        Config:               config,\n        Migrations:           migrationList,\n        Directory:            \"migrations/postgres\",\n        DefaultTransactional: true,\n        DefaultFileFormat:    \"sql\",\n    })\n    \n    os.Exit(cli.Run(os.Args[1:]))\n}\n```\n\n### ClickHouse Migration CLI (`cmd/migrate-clickhouse/main.go`)\n\n```go\npackage main\n\nimport (\n    \"database/sql\"\n    \"log\"\n    \"os\"\n    \n    \"github.com/alexisvisco/amigo\"\n    \"yourapp/migrations/clickhouse\"\n    _ \"github.com/ClickHouse/clickhouse-go/v2\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"clickhouse\", \"clickhouse://localhost:9000/default\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n    \n    config := amigo.DefaultConfiguration\n    config.DB = db\n    config.Driver = amigo.NewClickHouseDriver(\"schema_migrations\", \"\")\n    \n    migrationList := clickhouse.Migrations(config)\n    \n    cli := amigo.NewCLI(amigo.CLIConfig{\n        Config:               config,\n        Migrations:           migrationList,\n        Directory:            \"migrations/clickhouse\",\n        DefaultTransactional: true,\n        DefaultFileFormat:    \"sql\",\n    })\n    \n    os.Exit(cli.Run(os.Args[1:]))\n}\n```\n\n### Directory Structure\n\n```\nyourapp/\n├── cmd/\n│   ├── migrate-postgres/\n│   │   └── main.go\n│   └── migrate-clickhouse/\n│       └── main.go\n├── migrations/\n│   ├── postgres/\n│   │   ├── migrations.go\n│   │   ├── 20240101120000_create_users.sql\n│   │   └── 20240102150000_create_orders.sql\n│   └── clickhouse/\n│       ├── migrations.go\n│       ├── 20240101120000_create_events.sql\n│       └── 20240102150000_create_analytics.sql\n└── go.mod\n```\n\n### Usage\n\n```bash\n# PostgreSQL migrations\ngo run cmd/migrate-postgres/main.go generate create_users\ngo run cmd/migrate-postgres/main.go up\ngo run cmd/migrate-postgres/main.go status\n\n# ClickHouse migrations\ngo run cmd/migrate-clickhouse/main.go generate create_events\ngo run cmd/migrate-clickhouse/main.go up\ngo run cmd/migrate-clickhouse/main.go status\n```\n\n### Building Separate Binaries\n\n```bash\n# Build both migration tools\ngo build -o bin/migrate-postgres cmd/migrate-postgres/main.go\ngo build -o bin/migrate-clickhouse cmd/migrate-clickhouse/main.go\n\n# Use them\n./bin/migrate-postgres up\n./bin/migrate-clickhouse up\n```\n\n## Migration File Format\n\nMigration files follow the format: `{timestamp}_{name}.{ext}`\n\n- Timestamp: `YYYYMMDDHHMMSS`\n- Name: Snake case description\n- Extension: `.sql` or `.go`\n\nExample: `20240101120000_create_users_table.sql`\n\n## Transaction Helper\n\nUse the `Tx` helper for transactional Go migrations:\n\n```go\nerr := amigo.Tx(ctx, db, func(tx *sql.Tx) error {\n    _, err := tx.Exec(\"INSERT INTO users (name) VALUES (?)\", \"Alice\")\n    if err != nil {\n        return err\n    }\n    \n    _, err = tx.Exec(\"INSERT INTO posts (title) VALUES (?)\", \"First Post\")\n    return err\n})\n```\n\n## License\n\nMIT\n\n## Contributing\n\nContributions welcome! Please open an issue or PR.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexisvisco%2Famigo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexisvisco%2Famigo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexisvisco%2Famigo/lists"}