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) }