Skip to content

Commit

Permalink
feat(bundb): provide experimental commands under bundb
Browse files Browse the repository at this point in the history
Experimental commans are the good old [auto, migrate, rollback, etc.]
but adapted to import configuration from a pre-built plugin.

Plugins are known to be unstable and only sometimes work correctly,
so this feature is more of a proof of concept and we should probably
add the option to disable it entirely with build tags (for standalone bundb).

TODO:: bun init --plugin does not initialize a Go module in the
migrations/ directory. Currently one would need to manually run go mod init
and go mod tidy after bootstraping the project.
  • Loading branch information
bevzzz committed Nov 27, 2024
1 parent 792c96f commit 09ccc34
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 30 deletions.
19 changes: 13 additions & 6 deletions extra/buncli/auto.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package buncli

import (
"github.com/uptrace/bun/migrate"
"github.com/urfave/cli/v2"
)

// CmdAuto creates the auto command hierarchy.
func CmdAuto(c *Config) *cli.Command {
func CmdAuto(c AutoConfig) *cli.Command {
return &cli.Command{
Name: "auto",
Usage: "Manage database schema with AutoMigrator",
Expand All @@ -31,17 +32,23 @@ func CmdAuto(c *Config) *cli.Command {
}
}

func runAutoMigrate(ctx *cli.Context, c *Config) error {
_, err := c.AutoMigrator.Migrate(ctx.Context, c.MigrateOptions...)
// AutoConfig provides configuration for commands related to auto migration.
type AutoConfig interface {
OptionsConfig
Auto() *migrate.AutoMigrator
}

func runAutoMigrate(ctx *cli.Context, c AutoConfig) error {
_, err := c.Auto().Migrate(ctx.Context, c.GetMigrateOptions()...)
return err
}

func runAutoCreate(ctx *cli.Context, c *Config) error {
func runAutoCreate(ctx *cli.Context, c AutoConfig) error {
var err error
if flagTx.Get(ctx) {
_, err = c.AutoMigrator.CreateTxSQLMigrations(ctx.Context)
_, err = c.Auto().CreateTxSQLMigrations(ctx.Context)
} else {
_, err = c.AutoMigrator.CreateSQLMigrations(ctx.Context)
_, err = c.Auto().CreateSQLMigrations(ctx.Context)
}
return err
}
31 changes: 28 additions & 3 deletions extra/buncli/buncli.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import (

// bunApp is the root-level bundb app that all other commands attach to.
var bunApp = &cli.App{
Name: "bundb",
Usage: "Database migration tool for uptrace/bun",
Name: "bundb",
Usage: "Database migration tool for uptrace/bun",
Suggest: true,
}

// New creates a new CLI application for managing bun migrations.
Expand All @@ -40,7 +41,7 @@ func New(c *Config) *App {
}

// NewStandalone create a new CLI application to be distributed as a standalone binary.
// It's intended to be used in the cmb/bund and does not require any prior setup from the user:
// It's intended to be used in the cmb/bundb and does not require any prior setup from the user:
// the app only includes the Init command and reads all its configuration from command line.
//
// Prefer using New(*Config) in your custom entrypoint.
Expand All @@ -49,6 +50,9 @@ func NewStandalone(name string) *App {
bunApp.Commands = cli.Commands{
CmdInit(),
}

// NOTE: use `-tags experimental` to enable/disable this feature?
addCommandGroup(bunApp, "EXPERIMENTAL", pluginCommands()...)
return &App{
App: bunApp,
}
Expand Down Expand Up @@ -78,3 +82,24 @@ func RunContext(ctx context.Context, args []string, c *Config) error {
type App struct {
*cli.App
}

var _ OptionsConfig = (*Config)(nil)
var _ MigratorConfig = (*Config)(nil)
var _ AutoConfig = (*Config)(nil)

func (c *Config) NewMigrator() *migrate.Migrator {
return migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
}

func (c *Config) Auto() *migrate.AutoMigrator { return c.AutoMigrator }
func (c *Config) GetMigratorOptions() []migrate.MigratorOption { return c.MigratorOptions }
func (c *Config) GetMigrateOptions() []migrate.MigrationOption { return c.MigrateOptions }
func (c *Config) GetGoMigrationOptions() []migrate.GoMigrationOption { return c.GoMigrationOptions }

// addCommandGroup groups commands into one category.
func addCommandGroup(app *cli.App, group string, commands ...*cli.Command) {
for _, cmd := range commands {
cmd.Category = group
}
app.Commands = append(app.Commands, commands...)
}
24 changes: 18 additions & 6 deletions extra/buncli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func CmdInit() *cli.Command {
}
}

type OptionsConfig interface {
GetMigratorOptions() []migrate.MigratorOption
GetMigrateOptions() []migrate.MigrationOption
GetGoMigrationOptions() []migrate.GoMigrationOption
}

const (
maingo = "main.go"
defaultBin = "bun"
Expand Down Expand Up @@ -102,8 +108,8 @@ var (
}
)

func runInit(ctx *cli.Context, c *Config) error {
m := migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
func runInit(ctx *cli.Context, c MigratorConfig) error {
m := c.NewMigrator()
if err := m.Init(ctx.Context); err != nil {
return err
}
Expand Down Expand Up @@ -209,17 +215,19 @@ func (n *normalMode) Bootstrap() error {
if err != nil {
return err
}

migratorOpts := strings.Join(n.MigratorOptions, ", ")
if err := writef(binDir, maingo, entrypointTemplate, modPath, n.Binary, migratorOpts); err != nil {
return err
}

// Create migrations/main.go template
migrationsDir := path.Join(binDir, n.Migrations)
if err := writef(migrationsDir, maingo, migrationsTemplate); err != nil {
return err
}


return nil
}

Expand Down Expand Up @@ -274,11 +282,15 @@ func init() {
`

func (p *pluginMode) Bootstrap() error {
binDir := path.Join(p.Loc, p.Migrations)
migrationsDir := path.Join(p.Loc, p.Migrations)
migratorOpts := strings.Join(p.MigratorOptions, ", ")
if err := writef(binDir, maingo, pluginTemplate, migratorOpts); err != nil {
if err := writef(migrationsDir, maingo, pluginTemplate, migratorOpts); err != nil {
return err
}

// TODO: run go mod init <modPath> in migrations/ directory so that it'd be a standalone package
// TODO: run go mod init in migrations/ directory

return nil
}

Expand Down
36 changes: 21 additions & 15 deletions extra/buncli/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// CmdMigrate creates migrate command.
func CmdMigrate(c *Config) *cli.Command {
func CmdMigrate(c MigratorConfig) *cli.Command {
return &cli.Command{
Name: "migrate",
Usage: "Apply database migrations",
Expand All @@ -16,14 +16,20 @@ func CmdMigrate(c *Config) *cli.Command {
}
}

func runMigrate(ctx *cli.Context, c *Config) error {
m := migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
_, err := m.Migrate(ctx.Context, c.MigrateOptions...)
// MigratorConfig provides configuration related to conventional migrations.
type MigratorConfig interface {
OptionsConfig
NewMigrator() *migrate.Migrator
}

func runMigrate(ctx *cli.Context, c MigratorConfig) error {
m := c.NewMigrator()
_, err := m.Migrate(ctx.Context, c.GetMigrateOptions()...)
return err
}

// CmdRollback creates rollback command.
func CmdRollback(c *Config) *cli.Command {
func CmdRollback(c MigratorConfig) *cli.Command {
return &cli.Command{
Name: "rollback",
Usage: "Rollback the last migration group",
Expand All @@ -33,14 +39,14 @@ func CmdRollback(c *Config) *cli.Command {
}
}

func runRollback(ctx *cli.Context, c *Config) error {
m := migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
_, err := m.Rollback(ctx.Context, c.MigrateOptions...)
func runRollback(ctx *cli.Context, c MigratorConfig) error {
m := c.NewMigrator()
_, err := m.Rollback(ctx.Context, c.GetMigrateOptions()...)
return err
}

// CmdCreate creates create command.
func CmdCreate(c *Config) *cli.Command {
func CmdCreate(c MigratorConfig) *cli.Command {
return &cli.Command{
Name: "create",
Usage: "Create a new migration file template",
Expand Down Expand Up @@ -88,13 +94,13 @@ var (
}
)

func runCreate(ctx *cli.Context, c *Config) error {
func runCreate(ctx *cli.Context, c MigratorConfig) error {
var err error
m := migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
m := c.NewMigrator()
name := ctx.Args().First()

if createGo {
_, err = m.CreateGoMigration(ctx.Context, name, c.GoMigrationOptions...)
_, err = m.CreateGoMigration(ctx.Context, name, c.GetGoMigrationOptions()...)
return err
}

Expand All @@ -107,7 +113,7 @@ func runCreate(ctx *cli.Context, c *Config) error {
}

// CmdUnlock creates an unlock command.
func CmdUnlock(c *Config) *cli.Command {
func CmdUnlock(c MigratorConfig) *cli.Command {
return &cli.Command{
Name: "unlock",
Usage: "Unlock migration locks table",
Expand All @@ -117,7 +123,7 @@ func CmdUnlock(c *Config) *cli.Command {
}
}

func runUnlock(ctx *cli.Context, c *Config) error {
m := migrate.NewMigrator(c.DB, c.Migrations, c.MigratorOptions...)
func runUnlock(ctx *cli.Context, c MigratorConfig) error {
m := c.NewMigrator()
return m.Unlock(ctx.Context)
}
Loading

0 comments on commit 09ccc34

Please sign in to comment.