mirror of
https://github.com/foomo/posh-providers.git
synced 2025-10-16 12:35:41 +00:00
feat(golang-migrate/migrate): add provider
This commit is contained in:
parent
cb17a4c194
commit
1dbfe817a1
5
go.mod
5
go.mod
@ -11,6 +11,7 @@ require (
|
||||
github.com/cloudrecipes/packagejson v1.0.0
|
||||
github.com/digitalocean/godo v1.131.0
|
||||
github.com/foomo/posh v0.8.2
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||
github.com/google/go-github/v47 v47.1.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
@ -40,7 +41,9 @@ require (
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
@ -73,7 +76,7 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
|
||||
15
go.sum
15
go.sum
@ -53,6 +53,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
@ -70,10 +72,15 @@ github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
@ -145,8 +152,8 @@ github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaK
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
@ -216,8 +223,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
|
||||
52
golang-migrate/migrate/README.md
Normal file
52
golang-migrate/migrate/README.md
Normal file
@ -0,0 +1,52 @@
|
||||
# POSH Golang Migrate provider
|
||||
|
||||
## Usage
|
||||
|
||||
### Plugin
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
// ...
|
||||
// import database drivers
|
||||
_ "github.com/golang-migrate/migrate/v4/database/pgx/v5"
|
||||
// import source drivers
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
// ...
|
||||
)
|
||||
|
||||
type Plugin struct {
|
||||
l log.Logger
|
||||
}
|
||||
|
||||
func New(l log.Logger) (plugin.Plugin, error) {
|
||||
var err error
|
||||
inst := &Plugin{
|
||||
l: l,
|
||||
commands: command.Commands{},
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
inst.commands.Add(migrate.NewCommand(l))
|
||||
|
||||
// ...
|
||||
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
```yaml
|
||||
## Migrate
|
||||
migrate:
|
||||
# https://github.com/golang-migrate/migrate/blob/master/README.md#migration-sources
|
||||
databases:
|
||||
local: pgx5://postgres:postgres@localhost:5432/admin?sslmode=disable
|
||||
# https://github.com/golang-migrate/migrate/blob/master/README.md#migration-sources
|
||||
sources:
|
||||
default: file://migrations
|
||||
```
|
||||
214
golang-migrate/migrate/command.go
Normal file
214
golang-migrate/migrate/command.go
Normal file
@ -0,0 +1,214 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/foomo/posh/pkg/command/tree"
|
||||
"github.com/foomo/posh/pkg/log"
|
||||
"github.com/foomo/posh/pkg/prompt/goprompt"
|
||||
"github.com/foomo/posh/pkg/readline"
|
||||
"github.com/foomo/posh/pkg/util/suggests"
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type (
|
||||
Command struct {
|
||||
l log.Logger
|
||||
name string
|
||||
config Config
|
||||
configKey string
|
||||
commandTree tree.Root
|
||||
}
|
||||
CommandOption func(*Command)
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Options
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func CommandWithName(v string) CommandOption {
|
||||
return func(o *Command) {
|
||||
o.name = v
|
||||
}
|
||||
}
|
||||
|
||||
func CommandWithConfigKey(v string) CommandOption {
|
||||
return func(o *Command) {
|
||||
o.configKey = v
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Constructor
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func NewCommand(l log.Logger, opts ...CommandOption) (*Command, error) {
|
||||
inst := &Command{
|
||||
l: l.Named("migrate"),
|
||||
name: "migrate",
|
||||
configKey: "migrate",
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt(inst)
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.UnmarshalKey(inst.configKey, &inst.config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inst.commandTree = tree.New(&tree.Node{
|
||||
Name: inst.name,
|
||||
Description: "Manage database migrations",
|
||||
Nodes: tree.Nodes{
|
||||
{
|
||||
Name: "database",
|
||||
Values: func(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
|
||||
return suggests.List(inst.config.Databases())
|
||||
},
|
||||
Nodes: tree.Nodes{
|
||||
{
|
||||
Name: "source",
|
||||
Values: func(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
|
||||
return suggests.List(inst.config.Sources())
|
||||
},
|
||||
Nodes: tree.Nodes{
|
||||
{
|
||||
Name: "up",
|
||||
Description: "Migrate the DB to the most recent version available",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "up-by-one",
|
||||
Description: "Migrate the DB up by 1",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "down",
|
||||
Description: "Roll back the version by 1",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "down-by-one",
|
||||
Description: "Migrate the DB down by 1",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "force",
|
||||
Description: "Sets a migration version",
|
||||
Args: tree.Args{
|
||||
{
|
||||
Name: "version",
|
||||
Description: "Version to migrate",
|
||||
},
|
||||
},
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Description: "Print the current version of the database",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
{
|
||||
Name: "drop",
|
||||
Description: "Deletes everything in the database",
|
||||
Execute: inst.execute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (c *Command) Name() string {
|
||||
return c.commandTree.Node().Name
|
||||
}
|
||||
|
||||
func (c *Command) Description() string {
|
||||
return c.commandTree.Node().Description
|
||||
}
|
||||
|
||||
func (c *Command) Complete(ctx context.Context, r *readline.Readline) []goprompt.Suggest {
|
||||
return c.commandTree.Complete(ctx, r)
|
||||
}
|
||||
|
||||
func (c *Command) Execute(ctx context.Context, r *readline.Readline) error {
|
||||
return c.commandTree.Execute(ctx, r)
|
||||
}
|
||||
|
||||
func (c *Command) Help(ctx context.Context, r *readline.Readline) string {
|
||||
return c.commandTree.Help(ctx, r)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Private methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (c *Command) execute(ctx context.Context, r *readline.Readline) error {
|
||||
database := c.config.Database(r.Args().At(0))
|
||||
source := c.config.Source(r.Args().At(1))
|
||||
|
||||
m, err := migrate.New(source, database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Log = &logger{l: c.l}
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.l.Info("triggering graceful migration shutdown")
|
||||
m.GracefulStop <- true
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
if dErr, sErr := m.Close(); dErr != nil {
|
||||
c.l.Warn(dErr)
|
||||
} else if sErr != nil {
|
||||
c.l.Warn(sErr)
|
||||
}
|
||||
}()
|
||||
|
||||
switch r.Args().At(2) {
|
||||
case "up":
|
||||
return m.Up()
|
||||
case "up-by-one":
|
||||
return m.Steps(1)
|
||||
case "down":
|
||||
return m.Down()
|
||||
case "down-by-one":
|
||||
return m.Steps(-1)
|
||||
case "force":
|
||||
i, err := strconv.ParseInt(r.Args().At(3), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.Force(int(i))
|
||||
case "drop":
|
||||
return m.Drop()
|
||||
case "version":
|
||||
version, dirty, err := m.Version()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.l.Infof("Version: %d, Dirty: %t", version, dirty)
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("unkown command: %s", r.Args().At(2))
|
||||
}
|
||||
}
|
||||
32
golang-migrate/migrate/config.go
Normal file
32
golang-migrate/migrate/config.go
Normal file
@ -0,0 +1,32 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SourcesMap map[string]string `json:"sources" yaml:"sources" mapstructure:"sources"`
|
||||
DatabasesMap map[string]string `json:"databases" yaml:"databases" mapstructure:"databases"`
|
||||
}
|
||||
|
||||
func (c Config) Sources() []string {
|
||||
return lo.Keys(c.SourcesMap)
|
||||
}
|
||||
|
||||
func (c Config) Source(name string) string {
|
||||
if value, ok := c.SourcesMap[name]; ok {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c Config) Databases() []string {
|
||||
return lo.Keys(c.DatabasesMap)
|
||||
}
|
||||
|
||||
func (c Config) Database(name string) string {
|
||||
if value, ok := c.DatabasesMap[name]; ok {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
17
golang-migrate/migrate/logger.go
Normal file
17
golang-migrate/migrate/logger.go
Normal file
@ -0,0 +1,17 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"github.com/foomo/posh/pkg/log"
|
||||
)
|
||||
|
||||
type logger struct {
|
||||
l log.Logger
|
||||
}
|
||||
|
||||
func (l *logger) Printf(format string, v ...interface{}) {
|
||||
l.l.Infof(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Verbose() bool {
|
||||
return l.l.IsLevel(log.LevelDebug)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user