From e9fd1e5cac32706e039ff614dd81848600aa3ecc Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Tue, 26 Nov 2024 17:33:20 +0100 Subject: [PATCH] feat: add Migrator commands - migrate - rollback - create - unlock --- cmd/bundb/main.go | 1 + extra/buncli/auto.go | 21 ++----- extra/buncli/buncli.go | 25 ++++---- extra/buncli/migrator.go | 122 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 extra/buncli/migrator.go diff --git a/cmd/bundb/main.go b/cmd/bundb/main.go index 1e5365b0..0e424d42 100644 --- a/cmd/bundb/main.go +++ b/cmd/bundb/main.go @@ -35,5 +35,6 @@ func main() { log.SetPrefix("bundb: ") // TODO: use buncli.New(buncli.FromPlugin()) to read config from plugin if err := buncli.Run(os.Args, nil); err != nil { + log.Fatal(err) } } diff --git a/extra/buncli/auto.go b/extra/buncli/auto.go index 0d984907..4fd4eb17 100644 --- a/extra/buncli/auto.go +++ b/extra/buncli/auto.go @@ -4,6 +4,7 @@ import ( "github.com/urfave/cli/v2" ) +// CmdAuto creates the auto command hierarchy. func CmdAuto(c *Config) *cli.Command { return &cli.Command{ Name: "auto", @@ -16,36 +17,26 @@ func CmdAuto(c *Config) *cli.Command { flagTx, }, Action: func(ctx *cli.Context) error { - return autoCreate(ctx, c) + return runAutoCreate(ctx, c) }, }, &cli.Command{ Name: "migrate", Usage: "Generate SQL migrations and apply them right away", Action: func(ctx *cli.Context) error { - return autoMigrate(ctx, c) + return runAutoMigrate(ctx, c) }, }, }, } } -var ( - // flagTx adds --transactional flag. - flagTx = &cli.BoolFlag{ - Name: "tx", - Aliases: []string{"transactional"}, - Usage: "write migrations to .tx.(up|down).sql file, they will be marked as transactional", - Value: false, - } -) - -func autoMigrate(ctx *cli.Context, c *Config) error { - _, err := c.AutoMigrator.Migrate(ctx.Context) +func runAutoMigrate(ctx *cli.Context, c *Config) error { + _, err := c.AutoMigrator.Migrate(ctx.Context, c.MigrateOptions...) return err } -func autoCreate(ctx *cli.Context, c *Config) error { +func runAutoCreate(ctx *cli.Context, c *Config) error { var err error if flagTx.Get(ctx) { _, err = c.AutoMigrator.CreateTxSQLMigrations(ctx.Context) diff --git a/extra/buncli/buncli.go b/extra/buncli/buncli.go index 58983666..04d7ccf2 100644 --- a/extra/buncli/buncli.go +++ b/extra/buncli/buncli.go @@ -1,14 +1,10 @@ /* TODO: - - Commands: - - init - Create migration+locks tables [--cmd to add cmd/ folder structure] - - migrate - Apply database migrations - - rollback - Rollback the last migration group - - create - Create template SQL migration filex [--go | --sql | --transactional] - - unlock - Unlock locks table - - provide NewCommand() *cli.Command intead of the cli.App, so that buncli could be embeded in the existing CLIs - - configure logging and verbosity - - (experimental, low prio) add FromPlugin() to read config from plugin and use from cmd/bundb. + - Commands: + - init - Create migration+locks tables [--no-cmd to omit cmd/ folder] + - provide NewCommand() *cli.Command intead of the cli.App, so that buncli could be embeded in the existing CLIs + - configure logging and verbosity + - (experimental, low prio) add FromPlugin() to read config from plugin and use from cmd/bundb. */ package buncli @@ -29,7 +25,11 @@ var bunApp = &cli.App{ // New creates a new CLI application for managing bun migrations. func New(c *Config) *App { bunApp.Commands = cli.Commands{ + CmdMigrate(c), + CmdRollback(c), + CmdCreate(c), CmdAuto(c), + CmdUnlock(c), } return &App{ App: bunApp, @@ -37,8 +37,11 @@ func New(c *Config) *App { } type Config struct { - DB *bun.DB - AutoMigrator *migrate.AutoMigrator + DB *bun.DB + AutoMigrator *migrate.AutoMigrator + Migrations *migrate.Migrations + MigrateOptions []migrate.MigrationOption + GoMigrationOptions []migrate.GoMigrationOption } // Run calls cli.App.Run and returns its error. diff --git a/extra/buncli/migrator.go b/extra/buncli/migrator.go new file mode 100644 index 00000000..a50dd484 --- /dev/null +++ b/extra/buncli/migrator.go @@ -0,0 +1,122 @@ +package buncli + +import ( + "github.com/uptrace/bun/migrate" + "github.com/urfave/cli/v2" +) + +// CmdMigrate creates a migrate command. +func CmdMigrate(c *Config) *cli.Command { + return &cli.Command{ + Name: "migrate", + Usage: "Apply database migrations", + Action: func(ctx *cli.Context) error { + return runMigrate(ctx, c) + }, + } +} + +func runMigrate(ctx *cli.Context, c *Config) error { + m := migrate.NewMigrator(c.DB, c.Migrations) + _, err := m.Migrate(ctx.Context, c.MigrateOptions...) + return err +} + +// CmdRollback creates a rollback command. +func CmdRollback(c *Config) *cli.Command { + return &cli.Command{ + Name: "rollback", + Usage: "Rollback the last migration group", + Action: func(ctx *cli.Context) error { + return runRollback(ctx, c) + }, + } +} + +func runRollback(ctx *cli.Context, c *Config) error { + m := migrate.NewMigrator(c.DB, c.Migrations) + _, err := m.Rollback(ctx.Context, c.MigrateOptions...) + return err +} + +func CmdCreate(c *Config) *cli.Command { + return &cli.Command{ + Name: "create", + Usage: "Create a new migration file template", + Args: true, // TODO: add usage example and description + Flags: []cli.Flag{ + flagTx, + flagGo, + flagSQL, + }, + Action: func(ctx *cli.Context) error { + return runCreate(ctx, c) + }, + } +} + +var ( + // createGo controls the type of the template (Go/SQL) the Migrator should create. + createGo bool + + // flagTx adds --transactional flag. + flagTx = &cli.BoolFlag{ + Name: "tx", + Aliases: []string{"transactional"}, + Usage: "write migrations to .tx.(up|down).sql file, they will be marked as transactional", + Value: false, + } + + // flagGo adds --go flag. Prefer checking 'createGo' as the flagGo and flagSQL are mutually exclusive and it is how they synchronize. + flagGo = &cli.BoolFlag{ + Name: "go", + Usage: "create .go migration file", + Value: false, + Destination: &createGo, + } + + // flagSQL adds --sql flag. Prefer checking 'createGo' as the flagGo and flagSQL are mutually exclusive and it is how they synchronize. + flagSQL = &cli.BoolFlag{ + Name: "sql", + Usage: "create .sql migrations", + Value: true, + Action: func(ctx *cli.Context, b bool) error { + createGo = !b + return nil + }, + } +) + +func runCreate(ctx *cli.Context, c *Config) error { + var err error + m := migrate.NewMigrator(c.DB, c.Migrations) + name := ctx.Args().First() + + if createGo { + _, err = m.CreateGoMigration(ctx.Context, name, c.GoMigrationOptions...) + return err + } + + if flagTx.Get(ctx) { + _, err = m.CreateTxSQLMigrations(ctx.Context, name) + } else { + _, err = m.CreateSQLMigrations(ctx.Context, name) + } + return err +} + +// CmdUnlock creates an unlock command. +func CmdUnlock(c *Config) *cli.Command { + return &cli.Command{ + Name: "unlock", + Usage: "Unlock migration locks table", + Action: func(ctx *cli.Context) error { + return runUnlock(ctx, c) + }, + } +} + +func runUnlock(ctx *cli.Context, c *Config) error { + m := migrate.NewMigrator(c.DB, c.Migrations) + return m.Unlock(ctx.Context) +}