RepoPilotOpen in app β†’

amacneil/dbmate

πŸš€ A lightweight, framework-agnostic database migration tool.

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained β€” safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI β€” clean foundation to fork and modify.

Learn fromHealthy

Documented and popular β€” useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture β€” runnable as-is.

  • βœ“Last commit 1w ago
  • βœ“17 active contributors
  • βœ“Distributed ownership (top contributor 46% of recent commits)
Show all 6 evidence items β†’
  • βœ“MIT licensed
  • βœ“CI configured
  • βœ“Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README β€” live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/amacneil/dbmate)](https://repopilot.app/r/amacneil/dbmate)

Paste at the top of your README.md β€” renders inline like a shields.io badge.

β–ΈPreview social card (1200Γ—630)

This card auto-renders when someone shares https://repopilot.app/r/amacneil/dbmate on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: amacneil/dbmate

Generated by RepoPilot Β· 2026-05-09 Β· Source

πŸ€–Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale β€” STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI Β· unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/amacneil/dbmate shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything β€” but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO β€” Healthy across the board

  • Last commit 1w ago
  • 17 active contributors
  • Distributed ownership (top contributor 46% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

βœ…Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live amacneil/dbmate repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale β€” regenerate it at repopilot.app/r/amacneil/dbmate.

What it runs against: a local clone of amacneil/dbmate β€” the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in amacneil/dbmate | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≀ 38 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> β€” paste this script from inside your clone of <code>amacneil/dbmate</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of amacneil/dbmate. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/amacneil/dbmate.git
#   cd dbmate
#
# Then paste this script. Every check is read-only β€” no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of amacneil/dbmate and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "amacneil/dbmate(\\.git)?\\b" \\
  && ok "origin remote is amacneil/dbmate" \\
  || miss "origin remote is not amacneil/dbmate (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift β€” was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "main.go" \\
  && ok "main.go" \\
  || miss "missing critical file: main.go"
test -f "pkg/dbmate/db.go" \\
  && ok "pkg/dbmate/db.go" \\
  || miss "missing critical file: pkg/dbmate/db.go"
test -f "pkg/dbmate/driver.go" \\
  && ok "pkg/dbmate/driver.go" \\
  || miss "missing critical file: pkg/dbmate/driver.go"
test -f "pkg/dbmate/migration.go" \\
  && ok "pkg/dbmate/migration.go" \\
  || miss "missing critical file: pkg/dbmate/migration.go"
test -f "pkg/driver/postgres/postgres.go" \\
  && ok "pkg/driver/postgres/postgres.go" \\
  || miss "missing critical file: pkg/driver/postgres/postgres.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 38 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~8d)"
else
  miss "last commit was $days_since_last days ago β€” artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) β€” safe to trust"
else
  echo "artifact has $fail stale claim(s) β€” regenerate at https://repopilot.app/r/amacneil/dbmate"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

⚑TL;DR

Dbmate is a lightweight, framework-agnostic database migration tool written in Go that manages schema changes across MySQL, PostgreSQL, SQLite, ClickHouse, BigQuery, and Spanner. It uses plain SQL migration files (timestamped to avoid conflicts), runs migrations atomically within transactions, and provides a single self-contained binary for teams to keep database schemas in sync across development, testing, and production environments. Monolithic Go binary with modular driver architecture: main.go and main_cgo.go are entry points, pkg/dbmate/ contains the core migration engine (db.go, migration.go, driver.go), pkg/driver/ contains database-specific implementations (bigquery/, mysql/, postgres/, sqlite/, clickhouse/, spanner/), and pkg/dbtest/ provides testing utilities. CLI uses urfave/cli/v2, environment loading via joho/godotenv, and test fixtures under fixtures/.

πŸ‘₯Who it's for

Backend developers and DevOps engineers using any language or framework (Go, Node.js, Python, Ruby, PHP, Rust, C++, etc.) who need a database schema migration tool that works consistently across their team and doesn't depend on a specific ORM or framework.

🌱Maturity & risk

Production-ready and actively maintained. The project has a structured CI/CD pipeline (.github/workflows/ci.yml), comprehensive test coverage (main_test.go, pkg/dbmate/db_test.go), uses Go 1.26.2 with semantic versioning (v2 in go.mod), and maintains multiple database driver implementations. The presence of RELEASING.md and post-release automation indicates professional release management.

Low risk overall due to single-binary distribution model, but maintainer capacity appears thin (single amacneil ownership visible). The dependency tree is moderate (~30 direct deps including cloud.google.com/go/bigquery, ClickHouse, and SQL drivers), and the project explicitly supports legacy databases (SQLite, MySQL 5.7+). Breaking changes between v1β†’v2 are documented, but no visible breaking change policy in files.

Active areas of work

Active maintenance with recent Go toolchain syncing (sync-go-toolchain.yml workflow), dependabot updates enabled, and support for emerging database systems (BigQuery, Spanner, Supavisor). The devcontainer setup suggests local development standardization. No specific open issue backlog visible in provided data, but release automation indicates regular deployments.

πŸš€Get running

git clone https://github.com/amacneil/dbmate.git
cd dbmate
make
./dbmate --help

Or via npm: npm install --save-dev dbmate && npx dbmate --help

Daily commands: Development: make builds the binary; make test runs tests; docker-compose.yml provides local databases (MySQL, PostgreSQL, SQLite, ClickHouse). Production: distribute the single dbmate binary and set DATABASE_URL environment variable or use dbmate -d <url> <command>.

πŸ—ΊοΈMap of the codebase

  • main.go β€” Entry point for the CLI application; orchestrates command parsing and database operations.
  • pkg/dbmate/db.go β€” Core database abstraction layer; implements migration logic, database connections, and schema management.
  • pkg/dbmate/driver.go β€” Driver interface definition; all database-specific implementations must conform to this contract.
  • pkg/dbmate/migration.go β€” Migration file parsing and versioning logic; critical for understanding how migrations are discovered and executed.
  • pkg/driver/postgres/postgres.go β€” PostgreSQL driver implementation; reference implementation for implementing new database drivers.
  • go.mod β€” Dependency manifest; shows all supported databases and CLI framework (urfave/cli).
  • pkg/dbutil/dbutil.go β€” Database utility functions; shared logic for connection strings, environment variables, and file handling.

πŸ› οΈHow to make changes

Add Support for a New Database

  1. Create a new driver package directory (pkg/driver/newdb/)
  2. Implement the Driver interface from driver.go in your new driver file (pkg/driver/newdb/newdb.go)
  3. Register the driver in the driver registry (likely in db.go's New() or init() function) (pkg/dbmate/db.go)
  4. Add connection string parsing logic to dbutil.go if needed (pkg/dbutil/dbutil.go)
  5. Create integration tests matching existing driver test patterns (pkg/driver/newdb/newdb_test.go)
  6. Add test fixtures (migrations) if not covered by common testdata (fixtures/newdb/migrations/)

Add a New CLI Command

  1. Add command struct and flags to main.go (main.go)
  2. Implement command logic by adding a method to the DB struct in db.go (pkg/dbmate/db.go)
  3. Register the command in the urfave/cli app definition in main.go (main.go)
  4. Add tests to main_test.go covering the command flow (main_test.go)
  5. Update README.md with command documentation and examples (README.md)

Customize Migration Behavior for a Database

  1. Locate the driver's implementation file (e.g., postgres.go) (pkg/driver/postgres/postgres.go)
  2. Override the migration methods (CreateMigrationsTable, RunMigration, VerifyChecksumQuery) in your driver struct (pkg/driver/postgres/postgres.go)
  3. Add driver-specific SQL or logic to handle schema versioning tables (pkg/driver/postgres/postgres.go)
  4. Write driver-specific tests to validate the new behavior (pkg/driver/postgres/postgres_test.go)

Load Environment Variables for Database Connections

  1. Place .env file in your project root or use fixtures/loadEnvFiles as reference (fixtures/loadEnvFiles/.env)
  2. Call dbutil functions to parse and load environment variables (uses godotenv package) (pkg/dbutil/dbutil.go)
  3. Pass loaded connection string to db.New() or driver constructors (pkg/dbmate/db.go)

πŸ”§Why these technologies

  • Go β€” Produces a single statically-linked binary with no external dependencies; supports all major OSes and architectures; fast compilation and execution for CLI tools.
  • urfave/cli v2 β€” Lightweight CLI framework for Go; handles flag parsing, subcommands, and help text generation without heavy dependencies.
  • database/sql β€” Go standard library for database abstraction; all drivers must implement sql.Driver interface for consistency and portability.
  • godotenv β€” Enables loading DATABASE_URL and other config from .env files without requiring a separate config file format.
  • TypeScript + Node.js bindings β€” Distributes pre-compiled Go binaries through npm; enables integration in JavaScript/Node.js projects without Go installation.

βš–οΈTrade-offs already made

  • Single binary distribution rather than language-specific packages

    • Why: Avoids rewriting migration logic in multiple languages; achieves consistency and reduces maintenance burden.
    • Consequence: Requires Go installation to build from source, but npm package includes pre-built binaries for common platforms.
  • Driver interface with database-specific implementations rather than a generic migration executor

    • Why: Allows leveraging database-native features (e.g., transactions, checksums, schema creation) and handles idiosyncrasies of each database.
    • Consequence: Adding a new database requires implementing ~10–15 methods in a new driver; encourages community contributions for less common databases.
  • File-based migrations (SQL files) rather than code-based (ORM-style)

    • Why: Database-agnostic; migrations are portable between frameworks and languages; SQL is explicit and version-controllable.
    • Consequence: Requires manual SQL writing; no type safety for schema changes; developers must manage migration ordering by timestamp.
  • Schema version tracking via database table rather than separate metadata store

    • Why: Keeps all schema state in one place; works with every database; no external dependencies.
    • Consequence: Migrations must have unique timestamps; concurrent migrations can cause conflicts; rollback must be explicit.

🚫Non-goals (don't propose these)

  • Does not handle authentication or permission management; database credentials come from CONNECTION_URL or environment.
  • Does not provide ORM functionality or query builders; migrations are raw SQL only.
  • Does not support real-time or live schema synchronization; migrations are snapshot-based.
  • Does not include a UI or web dashboard; CLI-only interface.
  • Does not validate migration SQL syntax before execution; relies on database server for validation.
  • Does not support rolling forward/backward with automatic conflict resolution; conflicts require manual intervention.
  • Not designed for multi-tenant databases with independent schemas; treats database as single monolithic unit.

πŸͺ€Traps & gotchas

  1. DATABASE_URL environment variable must be set or passed explicitly (-d flag); using -d with embedded credentials in shell history is a security risk. 2) SQLite requires file:// URLs with absolute paths, not relative paths. 3) BigQuery requires GOOGLE_APPLICATION_CREDENTIALS pointing to a service account JSON; local authentication won't work. 4) Migrations run inside transactions atomically, so DDL in some databases (e.g., certain MySQL DDL operations) may not rollback properly. 5) The schema.sql dump feature (db.DumpSchema) is database-specific and not all drivers implement idempotent dumps identically.

πŸ—οΈArchitecture

πŸ’‘Concepts to learn

  • Timestamp-based migration versioning β€” Dbmate avoids merge conflicts by using Unix timestamps (e.g., 20151129054053) instead of sequential integers, critical for multi-developer workflows where migrations may be created in parallel branches
  • Atomic transactions in DDL β€” Dbmate runs each migration inside a SQL transaction to ensure all-or-nothing semantics; understanding which DDL operations are transactional is essential for avoiding partial migrations
  • Driver pattern / pluggable backends β€” The driver.go interface allows dbmate to support any SQL database by implementing a small set of methods; adding a new database means only writing a new driver, not forking the entire codebase
  • Schema migrations tracking table β€” Dbmate records executed migrations in a schema_migrations table to prevent re-running migrations and detect missing migrations; understanding this table's structure is critical for debugging and recovery
  • DSN (Data Source Name) / connection strings β€” Dbmate accepts DATABASE_URL as a DSN string; each driver has different URL formats (postgres://, mysql://, file://, etc.), and malformed URLs are a common source of errors
  • Idempotent schema dumps β€” The dump-schema command generates schema.sql that should produce identical output on repeated runs; this enables clean git diffs and schema version control
  • CGO and platform-specific builds β€” main_cgo.go suggests SQLite (mattn/go-sqlite3) requires CGO compilation, which complicates cross-platform builds; understanding when CGO is needed explains build gotchas
  • golang-migrate/migrate β€” Go-native database migration tool with similar goals but more complex version/checksum model; often compared in dbmate's alternatives section
  • flyway/flyway β€” JVM-based migration tool with versioning and callback features; major competing solution for polyglot teams
  • liquibase/liquibase β€” XML/YAML-based database change management that supports more advanced features like rollback triggers and change sets; another heavyweight alternative
  • joho/godotenv β€” Direct dependency that dbmate uses for .env file loading; understanding this library helps with environment variable configuration
  • urfave/cli β€” Direct dependency for CLI command/flag parsing (github.com/urfave/cli/v2); used throughout main.go for argument routing

πŸͺ„PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive integration tests for BigQuery driver with fixture migrations

The repo has BigQuery fixtures (fixtures/bigquery/migrations/) and a BigQuery driver (pkg/driver/bigquery/bigquery.go), but bigquery_test.go lacks integration tests that verify end-to-end migration workflows. Current tests are likely unit-only. Adding fixture-based integration tests would catch regressions in schema validation, transaction handling, and BigQuery-specific SQL dialect issues.

  • [ ] Review pkg/driver/bigquery/bigquery_test.go to identify gaps vs fixtures/bigquery/migrations/*.sql
  • [ ] Add integration test cases in bigquery_test.go that use fixtures/bigquery migrations as test data
  • [ ] Verify tests handle BigQuery-specific behaviors (dataset creation, partitioning, clustering) using testhelpers similar to pkg/dbtest/dbtest.go
  • [ ] Ensure CI workflow (.github/workflows/ci.yml) runs BigQuery tests with proper GCP credentials setup

Add unit tests for ClickHouse cluster parameter parsing and validation

The ClickHouse driver has cluster_parameters.go and cluster_parameters_test.go, but there's also testdata/cluster_config/ with actual cluster XML configs. The test coverage for edge cases in cluster parameter parsing (malformed XML, missing required fields, invalid node names) appears limited. Adding comprehensive validation tests would prevent silent failures in production multi-node ClickHouse setups.

  • [ ] Review pkg/driver/clickhouse/cluster_parameters_test.go for coverage gaps
  • [ ] Add test cases for invalid/malformed cluster_config XML files in testdata/
  • [ ] Add edge case tests: empty clusters, nodes without required ports, invalid replica names
  • [ ] Verify error messages are descriptive for debugging (test error output in cluster_parameters_test.go)

Add environment file (.env) loading tests covering fixtures/loadEnvFiles/

The repo has fixtures/loadEnvFiles/ with test data (first.txt, second.txt, invalid.txt, .env, .gitignore) but these fixtures don't appear to be used in pkg/dbutil/dbutil_test.go or main_test.go. This test suite should verify the --config flag's .env file loading behavior, including multi-file loading order, invalid file handling, and variable precedenceβ€”critical for CI/CD reliability.

  • [ ] Review main.go and main_test.go to identify where env file loading is tested (if at all)
  • [ ] Add test cases in main_test.go that use fixtures/loadEnvFiles/ to verify: .env parsing, first.txt/second.txt merge order, invalid.txt error handling
  • [ ] Verify env variable precedence (CLI flags > env files > defaults) with specific test cases
  • [ ] Document env file behavior in README.md if not already present

🌿Good first issues

  • Add integration tests for the new Spanner driver (pkg/driver/spanner/) to match the coverage pattern in bigquery_test.go β€” look at the BigQuery tests as a template and add similar table creation/migration/dump assertions for Spanner's specific dialect
  • Expand pkg/dbutil/dbutil_test.go β€” currently minimal coverage; add tests for URL parsing edge cases (malformed DSNs, missing credentials, unusual port numbers) and add test coverage for the SliceFlagValue type used by CLI
  • Document the driver interface contract in pkg/dbmate/driver.go β€” add detailed godoc comments explaining the semantics of each interface method, including transaction guarantees, idempotency requirements, and expected error types for each driver implementation

⭐Top contributors

Click to expand

πŸ“Recent commits

Click to expand
  • 2d97ccf β€” Simplify release process by removing dependency upgrade steps (#792) (sofuture)
  • 5e8c82a β€” v2.33.0 (#790) (sofuture)
  • ff474ed β€” Bump golang from 1.26.1 to 1.26.2 (#782) (dependabot[bot])
  • 4177ffe β€” Bump alpine from 3.23.3 to 3.23.4 (#789) (dependabot[bot])
  • 2036c59 β€” Update Go dependencies. (#779) (dossy)
  • 365589d β€” Bump handlebars from 4.7.8 to 4.7.9 in /typescript (#778) (dependabot[bot])
  • ca73b83 β€” Bump mislav/bump-homebrew-formula-action from 3 to 4 (#775) (dependabot[bot])
  • c89e2fd β€” Release v2.32.0 (#777) (sofuture)
  • e274828 β€” Bump picomatch from 4.0.3 to 4.0.4 in /typescript (#776) (dependabot[bot])
  • a5ab198 β€” Bump google.golang.org/grpc from 1.78.0 to 1.79.3 (#771) (dependabot[bot])

πŸ”’Security observations

Failed to generate security analysis.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals β€” see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals Β· amacneil/dbmate β€” RepoPilot