# migrate `go.auvem.com/corestack/migrate` sets the house configuration for [Goose's Provider API](https://pressly.github.io/goose/documentation/provider/). It returns `*goose.Provider` directly: Goose owns execution, version tracking, status, and errors. There is no lifecycle dependency or background work. Embed migrations in the application's binary and run them before serving traffic: ```go package app import ( "context" "database/sql" "embed" "log/slog" "github.com/pressly/goose/v3" "go.auvem.com/corestack/migrate" ) //go:embed migrations/*.sql var migrations embed.FS func migrateDatabase(ctx context.Context, db *sql.DB, logger *slog.Logger) error { provider, err := migrate.New(db, goose.DialectPostgres, migrations, "migrations", logger) if err != nil { return err } _, err = provider.Up(ctx) return err } ``` The caller opens and closes `db`, selects a matching database driver and dialect, supplies a nonnil logger, and controls the operation context/deadline. Construction is offline; SQL is parsed lazily by Goose. `Provider.Close` also closes `db`, so do not defer it when sharing the pool with the application. Use `"."` for SQL files at the embedded root. Files in the selected directory are discovered directly, without recursively searching subdirectories. Use unique positive versions and Goose's migration creator naming convention: zero-padded sequential names such as `00007_users.sql` and `00010_events.sql`, or fixed-width timestamps such as `20261027114500_create_users.sql`. Filenames must sort lexically in numeric version order, including when mixing naming schemes; choose padding wide enough for the complete history. Goose v3.29.0 preserves lexical source order for SQL-only providers, so arbitrary unpadded names such as `7_users.sql` and `10_events.sql` are unsupported by this house convention: they can produce incorrect latest versions and source/status ordering. This package does not rename or normalize migration files. Never derive a version from a file count or add one to the current version. Keep applied migrations immutable and ship the complete history. Goose rejects newly introduced migrations below the current version by default. ```sql -- +goose Up CREATE TABLE users (id BIGINT PRIMARY KEY); -- +goose Down DROP TABLE users; ``` Goose runs each migration in a transaction by default. Its annotations support statement blocks and explicit `NO TRANSACTION` where required; such migrations and nontransactional database DDL can leave partial effects on failure. Down migrations execute application-authored SQL and may delete data. | Operation | Provider method | | --- | --- | | Apply all pending migrations | `Up(ctx)` | | Apply pending versions through a target, inclusive | `UpTo(ctx, version)` | | Roll back versions above a target; zero removes all | `DownTo(ctx, version)` | | Inspect each migration's state | `Status(ctx)` | | Current database version | `GetDBVersion(ctx)` | | Current and latest available versions | `GetVersions(ctx)` | | Detect pending migrations | `HasPending(ctx)` | Targets are numeric version bounds, so gaps and timestamps work without special handling. Up on a fully migrated database succeeds with no results. Operations may create Goose's `goose_db_version` table even when only inspecting state. Execution errors retain Goose's details; use `errors.As` with `*goose.PartialError` to inspect applied and failed migrations. Transactional failure leaves the database at the last committed migration version. Operation logs use the injected slog logger; callers handle and log returned errors. For Go functions, pass migrations created with `goose.NewGoMigration(version, up, down)` as the trailing arguments to `migrate.New`. Use `goose.GoFunc.RunTx` for transactional work or `RunDB` for work requiring its own connections. Each provider should receive its own migration instances. Versions must be unique across SQL and Go migrations. Global registration is disabled; no `init` or `goose.AddMigration` calls are needed. Postgres providers automatically use Goose's default session advisory locker. Concurrent startup calls from separate providers/processes serialize migration execution and recheck applied versions under the lock. The default lock ID serializes Goose migrations throughout the same database, including different schemas; lock acquisition retries for about five minutes and unlock retries for about one minute, subject to the operation context. Use a direct connection or a session-pooling proxy: transaction pooling cannot preserve session locks. `RunDB` Go migrations need spare pool connections while the locked session is held; avoid a one-connection pool for them. Other dialects have no cross-provider/process lock configured by this package. Serialize migration runs externally, for example with a single deployment job. SQLite tests validate the SQL convention, not concurrent daemon startup safety. Run the integration suite against an existing Postgres database with schema creation privileges: ```sh MIGRATE_POSTGRES_DSN='postgres://user:password@localhost/testdb?sslmode=disable' go test -race ./migrate ``` Tests create and clean only random schemas they own. Without a DSN, Postgres tests skip locally; in CI a missing DSN fails, and the CI job supplies a Postgres service. SQLite tests always run in process using temporary databases.