postgresqlgomigrationgooseent

Using Goose migration tool for running Golang migration when using Ent framework


I'm trying to create a POC for running Golang code migrations using goose. The issue is that I'm using Ent framework, which seems in compatible for a straight forward use with goose.

According to goose docs and examples, it is possible to simply create a new Golang migration file using the command: goose create fetch_user_data go. The automatic boilerplate output is:

package migrations

import (
    "database/sql"

    "github.com/pressly/goose/v3"
)

func init() {
    goose.AddMigration(Up, Down)
}

func Up(tx *sql.Tx) error {
    _, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
    if err != nil {
        return err
    }
    return nil
}

func Down(tx *sql.Tx) error {
    _, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
    if err != nil {
        return err
    }
    return nil
}

This means that once a Golang migration is running, the "Up" function gets a pointer to a transaction (*sql.Tx), allowing to run only a pure SQL query.

Now, I'm trying to modify the "Up" and "Down" function, to receive a pointer to the DB itself (*sql.DB), in order to create a new Ent client:

driver := entSQL.NewDriver("postgres", db.Conn(ctx))
client := ent.NewClient(ent.Driver(driver))
defer client.Close()

I've tried all kinds of ways as above, in order to create the Ent client but it seems that Ent cannot be initialized using a transaction pointer, and a higher level access pointer is needed instead.

From goose docs, it seems that this can be achieved while using a custom Provider, in which it gets *sql.DB based on given env vars. By connecting goose library to that custom provider, it should be possible to modify the "Up" and "Down" function signatures as described.

To use a custom provider, it seems that I need to create my own goose binary and register my migration files.

I haven't managed to find a good example which demonstrates how to do that. I'm looking for some guidance in order to understand how to do it. Eventually, I want to be able to write the same goose create command, as written above, and get an automatic boilerplate output which uses *sql.DB instead of *sql.Tx. If possible to get an Ent client directly, it is even better.

I tried various types for Ent client creation, based on *sql.Tx, but it didn't work. I keep getting compilation errors, as goose library by default is not familiar with entgo.io/ent/dialect/sql, only with database/sql, which is incompatible with Ent.

Please advise, thanks.


Solution

  • You could change the goose.AddMigration to the goose.AddMigrationNoTxContext in the migration's init function and change the Up and Down signatures:

    func init() {
        goose.AddMigrationNoTxContext(Up, Down)
    }
    
    func Up(ctx context.Context, db *sql.DB) error {
        // ...
        return nil
    }
    
    func Down(ctx context.Context, db *sql.DB) error {
        // ...
        return nil
    }