package db import ( "context" "database/sql" "embed" "fmt" "github.com/jackc/pgx/v5/pgxpool" _ "github.com/jackc/pgx/v5/stdlib" // pgx/v5 driver for database/sql "github.com/pressly/goose/v3" ) // RunMigrations applies all pending goose migrations from the embedded FS. // It uses the pgx/v5/stdlib bridge so that goose's database/sql interface // can speak to the same Postgres as the rest of the application. // // Call RunMigrations once at startup, before constructing the router. // goose.Up is idempotent — already-applied migrations are skipped. func RunMigrations(ctx context.Context, pool *pgxpool.Pool, migrationsFS embed.FS) error { // Extract the DSN from the pool's connection config. connConfig := pool.Config().ConnConfig dsn := connConfig.ConnString() // Open a database/sql connection for goose. The pgx/v5 stdlib driver is // registered by the blank import above. sqlDB, err := sql.Open("pgx/v5", dsn) if err != nil { return fmt.Errorf("migrate: open sql.DB: %w", err) } defer sqlDB.Close() // Point goose at the embedded FS so no on-disk files are needed. goose.SetBaseFS(migrationsFS) if err := goose.SetDialect("postgres"); err != nil { return fmt.Errorf("migrate: set dialect: %w", err) } if err := goose.Up(sqlDB, "migrations"); err != nil { return fmt.Errorf("migrate: goose up: %w", err) } return nil }