pressly/goose
A database migration tool. Supports SQL migrations and Go functions.
Mixed signals — read the receipts
weakest axisnon-standard license (Other)
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5d ago
- ✓21+ active contributors
- ✓Other licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 58% of recent commits
- ⚠Non-standard license (Other) — review terms
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
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 "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/pressly/goose)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/pressly/goose on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: pressly/goose
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:
- 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. - 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.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/pressly/goose 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
WAIT — Mixed signals — read the receipts
- Last commit 5d ago
- 21+ active contributors
- Other licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 58% of recent commits
- ⚠ Non-standard license (Other) — review terms
<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 pressly/goose
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/pressly/goose.
What it runs against: a local clone of pressly/goose — 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 pressly/goose | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | 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 ≤ 35 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of pressly/goose. If you don't
# have one yet, run these first:
#
# git clone https://github.com/pressly/goose.git
# cd goose
#
# 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 pressly/goose and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "pressly/goose(\\.git)?\\b" \\
&& ok "origin remote is pressly/goose" \\
|| miss "origin remote is not pressly/goose (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other 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 "goose.go" \\
&& ok "goose.go" \\
|| miss "missing critical file: goose.go"
test -f "cmd/goose/main.go" \\
&& ok "cmd/goose/main.go" \\
|| miss "missing critical file: cmd/goose/main.go"
test -f "database/dialects.go" \\
&& ok "database/dialects.go" \\
|| miss "missing critical file: database/dialects.go"
test -f "internal/sqlparser/parser.go" \\
&& ok "internal/sqlparser/parser.go" \\
|| miss "missing critical file: internal/sqlparser/parser.go"
test -f "internal/migrationstats/migrationstats.go" \\
&& ok "internal/migrationstats/migrationstats.go" \\
|| miss "missing critical file: internal/migrationstats/migrationstats.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 35 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~5d)"
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/pressly/goose"
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).
⚡TL;DR
Goose is a standalone CLI and Go library for managing database schema migrations across 10+ SQL databases (PostgreSQL, MySQL, SQLite, MSSQL, YDB, ClickHouse, Vertica, etc.). It supports both SQL files and Go functions as migration definitions, with features like embedded migrations, out-of-order execution, environment variable substitution, and transactional safety. Monolithic single-package structure: cmd/goose/ contains CLI entrypoints with driver-specific files (driver_postgres.go, driver_mysql.go, etc.), root-level functions (create.go, down.go, fix.go, db.go) handle core logic, and database/ subdirectory abstracts SQL dialects via dialect/querier.go and store.go. Examples/ directory provides runnable SQL and Go migration templates.
👥Who it's for
Backend engineers and DevOps teams managing production databases who need a lightweight, database-agnostic migration tool that runs as a CLI or embeds into Go applications. Particularly useful for teams using multiple database types or requiring fine-grained control over migration execution.
🌱Maturity & risk
Production-ready. The codebase shows ~450KB of Go code, CI/CD pipelines (ci.yaml, integration.yaml, lint.yaml, release.yaml), comprehensive test files (create_test.go, fix_test.go, store_test.go), and support for 9+ database drivers. The project actively maintains multiple driver implementations and follows semantic versioning (v3.x in go.mod), indicating mature API stability.
Low-to-moderate risk. Dependencies are well-managed (ClickHouse, pgx, mssql-go are official drivers), but the binary bloats significantly without build tags (reason for no_postgres, no_mysql, etc. tags). Single-maintainer concern typical of OSS tools; verify last commit recency via GitHub Actions. Database-specific quirks (transaction handling differs per driver) mean thorough testing against your target DB is critical.
Active areas of work
Active maintenance on driver expansion (recent additions: driver_turso.go, driver_ydb.go, driver_clickhouse.go suggest 2024 activity) and dialect handling (database/dialect/ modules). CI shows integration tests against real databases. No specific PR or milestone info visible, but build tags and release.yaml indicate ongoing releases.
🚀Get running
Clone: git clone https://github.com/pressly/goose.git && cd goose. Install: go install ./cmd/goose@latest or brew install goose. Quick test: goose sqlite3 ./test.db create init sql && goose sqlite3 ./test.db up. See examples/sql-migrations/ and examples/go-migrations/ for migration templates.
Daily commands:
Dev: make build (Makefile present). CLI: goose postgres 'user=postgres dbname=test' status or set GOOSE_DRIVER, GOOSE_DBSTRING, GOOSE_MIGRATION_DIR env vars and run goose status. Run tests: go test ./.... See Makefile for full lint/test targets.
🗺️Map of the codebase
goose.go— Core public API exposing all migration operations (Up, Down, Status, etc.) that both CLI and library consumers depend on.cmd/goose/main.go— CLI entry point parsing commands and driver selection; every contributor adding CLI features must understand this flow.database/dialects.go— Registry and abstraction layer for all supported database dialects; critical for adding new database support.internal/sqlparser/parser.go— SQL migration parser handling directives and transaction semantics; core to understanding how migrations are executed.internal/migrationstats/migrationstats.go— Migration metadata tracking and versioning; essential for understanding state management across all databases.database/store.go— Abstraction over database schema versioning table; all dialect implementations depend on this interface.internal/dialects/postgres.go— Reference dialect implementation showing the pattern all new database drivers must follow.
🛠️How to make changes
Add Support for a New Database
- Create a new dialect file in
internal/dialects/(e.g.,internal/dialects/newdb.go) implementing theDialectinterface with query builders for inserting/querying version history. (internal/dialects/newdb.go) - Register the dialect in
database/dialects.goby adding a case in theDialect()function and importing your new dialect package. (database/dialects.go) - Create a driver file in
cmd/goose/driver_newdb.gothat initializes a database connection with DSN parsing (following the pattern ofcmd/goose/driver_postgres.go). (cmd/goose/driver_newdb.go) - Update
cmd/goose/main.goto add a case that calls your driver initialization when the dialect is selected. (cmd/goose/main.go) - Add integration tests in
.github/workflows/integration.yamlto test against your new database in CI. (.github/workflows/integration.yaml)
Add a New SQL Migration Directive
- Update the parser in
internal/sqlparser/parser.goto recognize and extract your new directive (e.g.,-- +goose NewDirective). (internal/sqlparser/parser.go) - Add the directive constant and handling logic to
internal/migrationstats/migration_sql.goso migrations can store and expose the new directive. (internal/migrationstats/migration_sql.go) - Implement directive-specific execution logic in
up.goordown.go(or create a new file) that respects the directive when executing migrations. (up.go) - Write test cases in
internal/sqlparser/parser_test.goto verify the directive is parsed correctly. (internal/sqlparser/parser_test.go)
Add a New CLI Command
- Implement the command logic in a new file (e.g.,
newcommand.go) or add to an existing command file, exporting a function that the CLI can call. (goose.go) - Update
cmd/goose/main.goto parse your new command-line flag and dispatch to your command function. (cmd/goose/main.go) - Add integration tests in
goose_cli_test.goor create a new test file to verify the command works end-to-end. (goose_cli_test.go)
🔧Why these technologies
- SQL database abstraction via Dialect interface — Enables single codebase to support 10+ databases (Postgres, MySQL, SQLite, etc.) by plugging in dialect implementations without duplicating orchestration logic.
- Embedded migrations (Go 1.16+ embed package) — Allows shipping migrations inside compiled binaries for single-binary deployments; eliminates file system dependencies at runtime.
- Go function migrations (runtime reflection) — Lets users write migrations in Go instead of SQL for complex data transformations with type safety and testing.
- Parsed SQL directives (custom comment syntax) — Provides database-agnostic way to customize per-migration behavior (disable transaction, skip execution, etc.) without extending SQL syntax.
- Sequential versioning with out-of-order support — Allows teams to generate migrations in parallel (e.g., feature branches) and apply them in order; fixes sequencing issues via
goose fix.
⚖️Trade-offs already made
-
Single versioning table per database instead of per-schema migrations
- Why: Simpler, common pattern across all databases; prevents the need for complex multi-schema tracking.
- Consequence: Cannot run different migration sequences in parallel on the same database; requires coordination.
-
Stateless CLI—no persistent config file for database URL
- Why: Keeps goose portable and environment-friendly; avoids storing secrets in repos.
- Consequence: Database DSN must be provided each invocation (via env var or flag); requires wrapper scripts for convenience.
-
No built-in rollback guarantees (down migrations are user-written)
- Why: Acknowledges that true rollback is database-specific and potentially dangerous; puts safety in user's hands.
- Consequence: Down migrations can fail or be incomplete; users must test them thoroughly and may need manual intervention.
-
Blocking, sequential migration execution (no parallelism within a run)
- Why: Guarantees deterministic ordering and avoids race conditions on the versioning table across machines.
- Consequence: Large migrations slow down deployment; users must optimize
🪤Traps & gotchas
- Transactions default to ON for SQL migrations but are skipped if SQL contains
-- +goose NO TRANSACTIONpragma—verify your migration syntax. 2. Build tags are required to reduce binary size;go installwithout tags pulls all drivers (~20MB+). 3. Migration filenames must follow patternYYYYMMDDHHMMSS_description.sqlor.go; malformed names are silently skipped. 4. Environment variable substitution in SQL uses${VAR_NAME}syntax (mfridman/interpolate), not standard$1or?placeholders. 5. Out-of-order migrations require explicit-- +goose NO TRANSACTIONif they depend on prior state; no automatic dependency resolution. 6. Connection string formats vary wildly per DB (postgres: libpq string, MySQL: DSN, SQLite: filepath, MSSQL: sqlserver:// URL)—copy exact format from README examples.
🏗️Architecture
💡Concepts to learn
- Database Dialect Abstraction — Goose supports 9+ SQL databases with subtly different syntax (MSSQL IDENTITY vs PostgreSQL SERIAL, SQLite no transactions); the dialect/querier pattern isolates these differences so core migration logic stays DB-agnostic
- Embedded Migrations (Go 1.16+ embed package) — Goose leverages Go's embed directive to bake SQL migrations directly into compiled binaries, eliminating runtime file dependencies—critical for containerized deployments
- Out-of-Order Migration Execution — Unlike linear migration tools, goose can apply older migrations that were missed; requires explicit NO TRANSACTION pragma to avoid state corruption, adding complexity to schema design
- Connection String Polymorphism — Each database uses a radically different connection format (postgres libpq vs MySQL DSN vs SQLite file path); goose's driver pattern abstracts parsing, but users must know target DB syntax
- Transaction Safety in Migrations — Goose wraps each SQL migration in a transaction by default (auto-rollback on failure), but DDL behavior differs per DB (PostgreSQL supports DDL in TX, SQLite has limitations); NO TRANSACTION pragma overrides this
- Schema Versioning via _migrations Table — Goose tracks applied migrations in a DB-specific metadata table (schema_migrations or goose_db_version); understanding this table's role is essential for debugging stuck migrations or manual recovery
- Environment Variable Interpolation in SQL — Goose uses mfridman/interpolate to substitute ${VAR_NAME} in SQL files at runtime, enabling dynamic credentials/paths without hardcoding; differs from standard SQL parameterization
🔗Related repos
golang-migrate/migrate— Direct competitor; Go migration tool supporting same databases but with different CLI UX and Go-function syntaxrubymigrations/rails— Inspiration; Rails Active Record migrations pioneered timestamp-based versioning and up/down function paradigm adopted by goosesqlc-dev/sqlc— Complementary tool; generates type-safe Go code from SQL, often used alongside goose for migration + query layerpressly/warble— Sibling project from same maintainer; provides alternative CLI wrapper patterns for embedded database toolinglib/pq— PostgreSQL driver for Go; goose depends on pgx/v5 (newer) but lib/pq was historically relevant for early goose versions
🪄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 integration tests for Turso/LibSQL driver (cmd/goose/driver_turso.go)
The Turso driver exists but there's no dedicated integration test workflow. Looking at .github/workflows/integration.yaml, tests likely cover major databases (Postgres, MySQL, SQLite) but Turso/LibSQL is a relatively new addition. Adding comprehensive integration tests would catch regressions early and demonstrate driver stability to users.
- [ ] Create .github/workflows/integration-turso.yaml workflow file
- [ ] Add Turso/LibSQL test container setup (similar to existing database containers in integration.yaml)
- [ ] Write integration tests in a new file like internal/dialects/turso_test.go covering: connection, migration up/down, transaction handling
- [ ] Test with the tursodatabase/libsql-client-go dependency already in go.mod
- [ ] Add Turso-specific migration examples to examples/go-migrations/ if missing
Refactor database dialect drivers into internal/dialects package (consolidate driver_*.go files)
Currently cmd/goose/ contains 8+ driver_*.go files (postgres, mysql, sqlite3, mssql, clickhouse, vertica, ydb, turso) that are CLI-specific wrappers. These are duplicative of internal/dialects/ implementations. Consolidating the logic would reduce maintenance burden and make it clear which drivers are fully supported.
- [ ] Audit all driver_*.go files in cmd/goose/ to identify CLI-specific vs generic driver logic
- [ ] Move generic driver initialization logic into internal/dialects/{dialect}.go files as exported functions
- [ ] Update cmd/goose/main.go to call internal/dialects functions instead of local driver setup code
- [ ] Remove or significantly reduce cmd/goose/driver_*.go files, keeping only CLI-specific argument parsing
- [ ] Add unit tests to internal/controller/store.go to verify driver registration works for all supported dialects
Add comprehensive testing for SQL migration templating/interpolation (environment variable substitution)
The README mentions 'Environment variable substitution in SQL migrations' as a feature, and mfridman/interpolate is in go.mod, but there are no visible tests in the file structure for this functionality. This feature is error-prone and deserves dedicated test coverage for edge cases.
- [ ] Create internal/interpolate_test.go with tests for: basic variable substitution, missing variables, nested variables, special characters in values
- [ ] Add example SQL migrations in examples/sql-migrations/ demonstrating variable substitution (e.g., 00004_interpolate_example.sql using ${TABLE_NAME} syntax)
- [ ] Add integration test in goose_cli_test.go or new file testing end-to-end migration with environment variables
- [ ] Document the interpolation syntax and limitations in README.md with concrete examples
- [ ] Test that interpolation works consistently across all supported database dialects
🌿Good first issues
- Add integration tests for driver_turso.go and driver_ydb.go in integration.yaml workflow; currently only postgres, mysql, sqlite3 appear in CI. Helps ensure new drivers don't regress.
- Expand database/dialect/querier_extended.go with dialect-specific optimizations (e.g., use RETURNING clause in PostgreSQL for GetMigrations instead of SELECT + COUNT). Tests should verify behavior matches querier.go for all 9 drivers.
- Document the NO TRANSACTION pragma and migration naming rules in a new MIGRATION_FORMAT.md file with examples; currently scattered across README and code comments. Critical for users avoiding silent failures.
⭐Top contributors
Click to expand
Top contributors
- @mfridman — 58 commits
- @dependabot[bot] — 21 commits
- @k2589 — 2 commits
- @vfoucault — 2 commits
- @tooilxui — 1 commits
📝Recent commits
Click to expand
Recent commits
e3235f7— release: v3.27.1 (mfridman)883e2f7— build(deps): bump Go and dependency versions (#1067) (mfridman)2e2fe5c— build(deps): bump the gomod group with 3 updates (#1048) (dependabot[bot])21176ca— build(deps): bump modernc.org/sqlite from 1.46.1 to 1.47.0 in the gomod group (#1045) (dependabot[bot])e7bd535— build(deps): bump google.golang.org/grpc from 1.79.1 to 1.79.3 (#1042) (dependabot[bot])f9c7cb4— build(deps): bump google.golang.org/grpc from 1.79.1 to 1.79.3 in /internal/testing (#1043) (dependabot[bot])b6220db— build(deps): bump the gomod group across 1 directory with 3 updates (#1041) (dependabot[bot])65e320f— docs: fix README escaping marker in ENVSUB example (#1037) (tooilxui)18f6ef7— build(deps): bump goreleaser/goreleaser-action from 6 to 7 (#1036) (dependabot[bot])de28e04— docs: update v3.27.0 release notes with Go 1.25 minimum and dep upgrades (mfridman)
🔒Security observations
- High · SQL Injection Risk in Migration Execution —
cmd/goose/main.go, internal/sqlparser/parse.go, database/store.go. As a database migration tool that executes SQL migrations, goose is inherently exposed to SQL injection risks if migration files are sourced from untrusted locations or if user input is incorporated into migration queries without proper sanitization. The tool's core functionality involves parsing and executing raw SQL files (e.g., examples/sql-migrations/*.sql), which could be exploited if access controls on migration files are not properly enforced. Fix: Implement strict validation of migration file sources, enforce file integrity checks (checksums/signatures), validate migration file ownership and permissions, and ensure migrations are only loaded from trusted directories. Document best practices for securing migration file storage. - High · Potential Remote Code Execution via Go Migrations —
examples/go-migrations/00002_rename_root.go, examples/go-migrations/00003_add_user_no_tx.go, internal/migrationstats/migration_go.go. Goose supports Go migrations as plain functions (examples/go-migrations/*.go). If these migration files can be modified by untrusted actors or dynamically compiled/loaded, this could enable remote code execution. The tool dynamically loads and executes Go code as part of migrations. Fix: Restrict write permissions on migration directories to authorized users only. Implement code review workflows for migration changes. Consider signing migrations with cryptographic signatures. Limit the capabilities available to migration code through sandboxing or reduced privilege execution. - Medium · Insufficient Input Validation in Environment Variable Substitution —
README.md (feature description), goose.go. The README mentions 'Environment variable substitution in SQL migrations' as a feature. This could lead to injection vulnerabilities if environment variables are not properly validated before being substituted into SQL queries. Attackers with control over environment variables could inject malicious SQL. Fix: Implement strict validation and escaping for environment variables before substitution. Use parameterized queries/prepared statements instead of string substitution. Document the risks and security best practices for environment variable usage in migrations. - Medium · Database Connection String Exposure Risk —
cmd/goose/main.go, cmd/goose/driver_*.go files, .github/workflows/. The tool requires database connection strings which may contain sensitive credentials. The presence of .github/workflows and multiple database driver files suggests database credentials may be logged or exposed in CI/CD pipelines or debug output. Fix: Ensure connection strings are never logged or printed in debug output. Use secure secret management for CI/CD pipelines. Document secure practices for handling database credentials. Implement redaction of sensitive connection string components in logs and error messages. - Medium · Multiple Database Driver Dependencies —
go.mod (dependencies section). The tool depends on numerous third-party database drivers (clickhouse-go, pgx, go-mssqldb, mysql driver, etc.). Each represents a potential attack surface. Several dependencies are from organizations with varying security track records, and some may have unpatched vulnerabilities. Fix: Regularly update all database driver dependencies. Implement automated dependency scanning using tools like 'go list -u -m all' and vulnerability scanners like Nancy or Snyk. Pin versions to tested releases and test updates in staging before production deployment. - Medium · Embedded Migration File Integrity —
README.md (embedded migrations feature), examples/. The tool supports embedded migrations using Go's embed package. Malicious or modified embedded migrations could be included in compiled binaries, and there's no apparent integrity verification mechanism mentioned in the structure. Fix: Verify integrity of embedded migrations at compile time. Implement checksums or signatures for migration files. Document the security implications of embedding migrations. Consider separating compiled binaries from migration files when possible. - Low · Outdated Go Version Target —
go.mod. The go.mod specifies 'go 1.25.7' which appears to be a future/unrealistic version number for Go (as of analysis time). This may indicate outdated or incorrectly maintained dependency specifications, which could mask actual version inconsistencies. Fix: Verify the correct Go version target and ensure it matches the minimum supported version. Align with Go's actual release schedule. Regularly update Go version to receive security patches. - Low · Missing Security Configuration in CI/CD —
undefined. Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.