package dbmigrate import ( "errors" "fmt" "log/slog" "os" "strings" "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/postgres" "github.com/golang-migrate/migrate/v4/source/iofs" "github.com/ultisuite/ulti-backend/migrations" ) // Up applies pending SQL migrations. No-op when ULTID_AUTO_MIGRATE=false. func Up(databaseURL string) error { if !autoMigrateEnabled() { slog.Info("database auto-migrate disabled") return nil } if strings.TrimSpace(databaseURL) == "" { return fmt.Errorf("empty database URL") } source, err := iofs.New(migrations.Files, ".") if err != nil { return fmt.Errorf("migration source: %w", err) } m, err := migrate.NewWithSourceInstance("iofs", source, databaseURL) if err != nil { return fmt.Errorf("migrate init: %w", err) } defer func() { srcErr, dbErr := m.Close() if srcErr != nil { slog.Warn("migrate close source", "error", srcErr) } if dbErr != nil { slog.Warn("migrate close database", "error", dbErr) } }() if err := m.Up(); err != nil { if errors.Is(err, migrate.ErrNoChange) { slog.Info("database schema up to date") return nil } return fmt.Errorf("migrate up: %w", err) } slog.Info("database migrations applied") return nil } func autoMigrateEnabled() bool { v := strings.ToLower(strings.TrimSpace(os.Getenv("ULTID_AUTO_MIGRATE"))) if v == "false" || v == "0" || v == "no" { return false } return true }