Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/bundb: Bun CLI for database schema management #1062

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cmd/bundb/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
TODO:

- Add a mechanism to detect potentially duplicate migration files. That is,
once we've collected migrations in a bytes.Buffer, check if 'migrations/' package
has another migration files that:
1. is identical in content
2. belongs to the migration that has not been applied yet

If we find such migration, prompt the user for confirmation, unless -force flag is set.
Ideally, we should be able to ignore "transactional" for this purpose,
i.e. same_thing.up.tx.sql should overwrite same_thing.up.sql.

- Store configured options to env variables? E.g. after 'bundb init --create-directory=db-migrations/'
set BUNDB_MIGRATIONS=db-migrations, so that subsequent commands can be run without additional parameters.
Although... this way we are moving towards a .bundb.config or something.
*/
package main

import (
"log"
"os"

"github.com/uptrace/bun/extra/buncli"
)

func main() {
log.SetPrefix("bundb: ")
if err := buncli.NewStandalone("bundb").Run(os.Args); err != nil {
log.Fatal(err)
}
}
2 changes: 2 additions & 0 deletions dialect/pgdialect/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/sys v0.27.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions dialect/pgdialect/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
Expand Down
4 changes: 3 additions & 1 deletion driver/pgdriver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kr/text v0.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/sys v0.27.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 1 addition & 1 deletion driver/pgdriver/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
4 changes: 2 additions & 2 deletions example/migrate/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ replace github.com/uptrace/bun/driver/sqliteshim => ../../driver/sqliteshim

require (
github.com/uptrace/bun v1.2.6
github.com/uptrace/bun/dialect/sqlitedialect v1.2.6
github.com/uptrace/bun/driver/sqliteshim v1.2.6
github.com/uptrace/bun/dialect/sqlitedialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/driver/sqliteshim v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/extra/bundebug v1.2.6
github.com/urfave/cli/v2 v2.27.5
)
Expand Down
54 changes: 54 additions & 0 deletions extra/buncli/auto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package buncli

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

// CmdAuto creates the auto command hierarchy.
func CmdAuto(c AutoConfig) *cli.Command {
return &cli.Command{
Name: "auto",
Usage: "Manage database schema with AutoMigrator",
Subcommands: cli.Commands{
&cli.Command{
Name: "create",
Usage: "Generate SQL migration files",
Flags: []cli.Flag{
flagTx,
},
Action: func(ctx *cli.Context) error {
return runAutoCreate(ctx, c)
},
},
&cli.Command{
Name: "migrate",
Usage: "Generate SQL migrations and apply them right away",
Action: func(ctx *cli.Context) error {
return runAutoMigrate(ctx, c)
},
},
},
}
}

// 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 AutoConfig) error {
var err error
if flagTx.Get(ctx) {
_, err = c.Auto().CreateTxSQLMigrations(ctx.Context)
} else {
_, err = c.Auto().CreateSQLMigrations(ctx.Context)
}
return err
}
105 changes: 105 additions & 0 deletions extra/buncli/buncli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
TODO:
- 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

import (
"context"

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

// 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",
Suggest: true,
}

// New creates a new CLI application for managing bun migrations.
func New(c *Config) *App {
if c.RootName != "" {
bunApp.Name = c.RootName
}
bunApp.Commands = cli.Commands{
CmdMigrate(c),
CmdRollback(c),
CmdCreate(c),
CmdAuto(c),
CmdUnlock(c),
}
return &App{
App: bunApp,
}
}

// NewStandalone create a new CLI application to be distributed as a standalone binary.
// 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.
func NewStandalone(name string) *App {
bunApp.Name = name
bunApp.Commands = cli.Commands{
CmdInit(),
}

// NOTE: use `-tags experimental` to enable/disable this feature?
addCommandGroup(bunApp, "EXPERIMENTAL", pluginCommands()...)
return &App{
App: bunApp,
}
}

type Config struct {
RootName string
DB *bun.DB
AutoMigrator *migrate.AutoMigrator
Migrations *migrate.Migrations
MigratorOptions []migrate.MigratorOption
MigrateOptions []migrate.MigrationOption
GoMigrationOptions []migrate.GoMigrationOption
}

// Run calls cli.App.Run and returns its error.
func Run(args []string, c *Config) error {
return New(c).Run(args)
}

// RunContext calls cli.App.RunContext and returns its error.
func RunContext(ctx context.Context, args []string, c *Config) error {
return New(c).RunContext(ctx, args)
}

// App is a wrapper around cli.App that extends it with bun-specific features.
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...)
}
61 changes: 61 additions & 0 deletions extra/buncli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module github.com/uptrace/bun/extra/buncli

go 1.22.0

require (
github.com/uptrace/bun v1.2.7-0.20241125022320-89e9d5169f8a
github.com/uptrace/bun/dialect/mssqldialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/dialect/mysqldialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/dialect/oracledialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/dialect/pgdialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/dialect/sqlitedialect v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/driver/pgdriver v1.2.7-0.20241126124946-928d0779110e
github.com/uptrace/bun/driver/sqliteshim v1.2.7-0.20241126124946-928d0779110e
github.com/urfave/cli/v2 v2.27.5
golang.org/x/mod v0.22.0
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/sys v0.27.0 // indirect
mellium.im/sasl v0.3.2 // indirect
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852 // indirect
modernc.org/libc v1.61.2 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.34.1 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)

replace github.com/uptrace/bun => ../..

// replace github.com/uptrace/bun/dialect/mssqldialect => ../../dialect/mssqldialect

// replace github.com/uptrace/bun/dialect/mysqldialect => ../../dialect/mysqldialect

// replace github.com/uptrace/bun/dialect/oracledialect => ../../dialect/oracledialect

// replace github.com/uptrace/bun/dialect/pgdialect => ../../dialect/pgdialect

// replace github.com/uptrace/bun/dialect/sqlitedialect => ../../dialect/sqlitedialect

// replace github.com/uptrace/bun/driver/pgdriver => ../../driver/pgdriver

// replace github.com/uptrace/bun/driver/sqliteshim => ../../driver/sqliteshim
Loading
Loading