Files
migrate/cli/cli.go
T
end c65e357e15 fix: unify goose Provider path and fail on pending migrations
Apply BasePath via fs.Sub, route CLI through Provider API, return
ErrPendingMigrations when auto-migrate is disabled, use DownTo for blank.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 18:04:08 -07:00

173 lines
5.2 KiB
Go

package migratecli
import (
"context"
"fmt"
"gitea.auvem.com/go-toolkit/appcli"
"gitea.auvem.com/go-toolkit/migrate"
"github.com/pressly/goose/v3"
"github.com/urfave/cli/v3"
)
// MigrateCmd returns the main migrate command.
func MigrateCmd(sqlo migrate.SQLOFunc, directDeps appcli.DepFn, childDeps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "migrate",
Usage: "Migrate the database",
Commands: AllSubcommands(sqlo, childDeps),
}, directDeps)
}
// AllSubcommands returns all subcommands of the migrate command.
func AllSubcommands(sqlo migrate.SQLOFunc, deps appcli.DepFn) []*cli.Command {
return []*cli.Command{
MigrateStatusCmd(sqlo, deps),
MigrateCreateCmd(sqlo, deps),
MigrateUpCmd(sqlo, deps),
MigrateUpToCmd(sqlo, deps),
MigrateDownCmd(sqlo, deps),
MigrateDownToCmd(sqlo, deps),
MigrateRedoCmd(sqlo, deps),
}
}
func withProvider(ctx context.Context, fn func(context.Context, *goose.Provider) error) error {
p, err := migrate.Provider()
if err != nil {
return err
}
return fn(ctx, p)
}
// MigrateStatusCmd returns a command to get database migration status.
func MigrateStatusCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "status",
Usage: "Get database migration status",
Action: func(ctx context.Context, cmd *cli.Command) error {
return migrate.PrintMigrationStatus(ctx)
},
}, deps)
}
// MigrateCreateCmd returns a command to create a new migration file.
func MigrateCreateCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "create",
Usage: "Create a new migration",
Arguments: []cli.Argument{
&cli.StringArg{Name: "name", UsageText: "Name of the migration"},
&cli.StringArg{Name: "type", UsageText: "Type of the migration (e.g., sql, go)"},
},
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "sequential",
Aliases: []string{"s"},
Usage: "Create migration in sequential mode",
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
if cmd.Bool("sequential") {
goose.SetSequential(true)
}
cfg := migrate.MigrationsConfig()
dir := cfg.BasePath
if dir == "." || dir == "" {
dir = "migrations"
}
if err := goose.Create(sqlo(), dir, cmd.StringArg("name"), cmd.StringArg("type")); err != nil {
return fmt.Errorf("couldn't create migration: %w", err)
}
return nil
},
}, deps)
}
// MigrateUpCmd returns a command to apply all available database migrations.
func MigrateUpCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "up",
Usage: "Apply all available database migrations",
Action: func(ctx context.Context, cmd *cli.Command) error {
return withProvider(ctx, func(ctx context.Context, p *goose.Provider) error {
_, err := p.Up(ctx)
return err
})
},
}, deps)
}
// MigrateUpToCmd returns a command to apply migrations up to a specific version.
func MigrateUpToCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "up-to",
Usage: "Apply all available database migrations up to a specific version",
Arguments: []cli.Argument{
&cli.Int64Arg{
Name: "version",
UsageText: "Target version to migrate up to",
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
version := cmd.Int64("version")
return withProvider(ctx, func(ctx context.Context, p *goose.Provider) error {
_, err := p.UpTo(ctx, version)
return err
})
},
}, deps)
}
// MigrateDownCmd returns a command to rollback the most recent database migration.
func MigrateDownCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "down",
Usage: "Rollback the most recent database migration",
Action: func(ctx context.Context, cmd *cli.Command) error {
return withProvider(ctx, func(ctx context.Context, p *goose.Provider) error {
_, err := p.Down(ctx)
return err
})
},
}, deps)
}
// MigrateDownToCmd returns a command to rollback migrations down to a specific version.
func MigrateDownToCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "down-to",
Usage: "Rollback all database migrations down to a specific version",
Arguments: []cli.Argument{
&cli.Int64Arg{
Name: "version",
UsageText: "Target version to rollback to",
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
version := cmd.Int64("version")
return withProvider(ctx, func(ctx context.Context, p *goose.Provider) error {
_, err := p.DownTo(ctx, version)
return err
})
},
}, deps)
}
// MigrateRedoCmd returns a command to rollback and reapply the most recent migration.
func MigrateRedoCmd(sqlo migrate.SQLOFunc, deps appcli.DepFn) *cli.Command {
return appcli.NewCommand(&cli.Command{
Name: "redo",
Usage: "Rollback the most recent database migration and reapply it",
Action: func(ctx context.Context, cmd *cli.Command) error {
return withProvider(ctx, func(ctx context.Context, p *goose.Provider) error {
if _, err := p.Down(ctx); err != nil {
return err
}
_, err := p.UpByOne(ctx)
return err
})
},
}, deps)
}