diff --git a/.golangci.yml b/.golangci.yml index fe701bb..f105d81 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -57,7 +57,7 @@ linters: #- gci # Gci controls golang package import order and makes it always deterministic. [fast: true, auto-fix: false] #- gochecknoglobals # check that no global variables exist [fast: true, auto-fix: false] #- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] - - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] + #- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] - gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false] - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] diff --git a/arbitary/open/command.go b/arbitary/open/command.go index 8080d49..2e5f361 100644 --- a/arbitary/open/command.go +++ b/arbitary/open/command.go @@ -149,14 +149,30 @@ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { return err } - if route.Username != nil && route.Password != nil { - if username, err := c.op.Get(ctx, *route.Username); err != nil { - return err - } else if password, err := c.op.Get(ctx, *route.Password); err != nil { - return err - } else { - u.User = url.UserPassword(username, password) + if route.BasicAuth != nil { + var ( + username string + password string + ) + { + secret := *route.BasicAuth + secret.Field = "username" + if value, err := c.op.Get(ctx, secret); err != nil { + return err + } else { + username = value + } } + { + secret := *route.BasicAuth + secret.Field = "password" + if value, err := c.op.Get(ctx, secret); err != nil { + return err + } else { + password = value + } + } + u.User = url.UserPassword(username, password) } return browser.OpenURL(u) diff --git a/arbitary/open/config.go b/arbitary/open/config.go index 1bb4860..b27246c 100644 --- a/arbitary/open/config.go +++ b/arbitary/open/config.go @@ -14,7 +14,6 @@ type ( ConfigRoute struct { Path string `yaml:"path"` Description string `yaml:"description"` - Username *onepassword.Secret `yaml:"username"` - Password *onepassword.Secret `yaml:"password"` + BasicAuth *onepassword.Secret `yaml:"basicAuth"` } ) diff --git a/derailed/k9s/command.go b/derailed/k9s/command.go index 3d001e0..c7daa9c 100644 --- a/derailed/k9s/command.go +++ b/derailed/k9s/command.go @@ -75,6 +75,15 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl, squadron *squadron.Squad Suggest: inst.completeSquadrons, }, }, + Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + if r.Args().HasIndex(0) { + fs.Internal().String("profile", "", "Profile to use.") + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } + return nil + }, Execute: inst.execute, }) return inst @@ -109,10 +118,17 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ------------------------------------------------------------------------------------------------ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { + ifs := r.FlagSets().Internal() cluster, fleet, squad := r.Args().At(0), r.Args().At(1), r.Args().At(2) + + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + return shell.New(ctx, c.l, "k9s", "-n", c.namespaceFn(cluster, fleet, squad), "--logoless"). Args(r.AdditionalArgs()...). - Env(c.kubectl.Cluster(cluster).Env()). + Env(c.kubectl.Cluster(cluster).Env(profile)). Run() } diff --git a/dreadl0ck/zeus/command.go b/dreadl0ck/zeus/command.go index 81f9a65..c997e70 100644 --- a/dreadl0ck/zeus/command.go +++ b/dreadl0ck/zeus/command.go @@ -112,9 +112,9 @@ func (c *Command) completePaths(ctx context.Context) []goprompt.Suggest { //nolint:forcetypeassert func (c *Command) paths(ctx context.Context) []string { return c.cache.Get("paths", func() any { - if value, err := files.Find(ctx, ".", "zeus"); err != nil { + if value, err := files.Find(ctx, ".", "zeus", files.FindWithIgnore(`^\.`, "node_modules"), files.FindWithIsDir(true)); err != nil { c.l.Debug("failed to walk files", err.Error()) - return nil + return []string{} } else { return value } diff --git a/etcd-io/etcd/command.go b/etcd-io/etcd/command.go index eb80391..b22b21c 100644 --- a/etcd-io/etcd/command.go +++ b/etcd-io/etcd/command.go @@ -9,7 +9,9 @@ import ( "strings" prompt2 "github.com/c-bata/go-prompt" + "github.com/foomo/posh-providers/kubernets/kubectl" "github.com/foomo/posh/pkg/command/tree" + "github.com/foomo/posh/pkg/env" "github.com/foomo/posh/pkg/log" "github.com/foomo/posh/pkg/prompt/goprompt" "github.com/foomo/posh/pkg/readline" @@ -21,6 +23,7 @@ import ( type Command struct { l log.Logger etcd *ETCD + kubectl *kubectl.Kubectl commandTree tree.Root } @@ -28,21 +31,33 @@ type Command struct { // ~ Constructor // ------------------------------------------------------------------------------------------------ -func NewCommand(l log.Logger, etcd *ETCD, opts ...Option) *Command { +func NewCommand(l log.Logger, etcd *ETCD, kubectl *kubectl.Kubectl, opts ...Option) *Command { inst := &Command{ - l: l.Named("etcd"), - etcd: etcd, + l: l.Named("etcd"), + etcd: etcd, + kubectl: kubectl, } - pathArg := &tree.Arg{ - Name: "path", - Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []prompt2.Suggest { - if value, ok := inst.etcd.cfg.Cluster(r.Args().At(0)); ok { - return suggests.List(value.Paths) - } - return nil + args := tree.Args{ + { + Name: "path", + Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []prompt2.Suggest { + if value, ok := inst.etcd.cfg.Cluster(r.Args().At(0)); ok { + return suggests.List(value.Paths) + } + return nil + }, }, } + flags := func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + if r.Args().HasIndex(0) { + fs.Internal().String("profile", "", "Profile to use.") + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } + return nil + } inst.commandTree = tree.New(&tree.Node{ Name: "etcd", @@ -62,12 +77,14 @@ func NewCommand(l log.Logger, etcd *ETCD, opts ...Option) *Command { Nodes: tree.Nodes{ { Name: "get", - Args: tree.Args{pathArg}, + Args: args, + Flags: flags, Execute: inst.get, }, { Name: "edit", - Args: tree.Args{pathArg}, + Args: args, + Flags: flags, Execute: inst.edit, }, }, @@ -108,9 +125,16 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { func (c *Command) get(ctx context.Context, r *readline.Readline) error { etcdPath := r.Args().At(2) + ifs := r.FlagSets().Internal() + + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + if cluster, ok := c.etcd.cfg.Cluster(r.Args().At(0)); !ok { return errors.New("invalid cluster") - } else if out, err := c.etcd.GetPath(ctx, cluster, etcdPath); err != nil { + } else if out, err := c.etcd.GetPath(ctx, cluster, profile, etcdPath); err != nil { return errors.Wrap(err, out) } else { prints.Code(c.l, etcdPath, out+"\n", "yaml") @@ -129,10 +153,16 @@ func (c *Command) edit(ctx context.Context, r *readline.Readline) error { } etcdPath := r.Args().At(2) - filename := path.Join(os.Getenv("PROJECT_ROOT"), c.etcd.cfg.ConfigPath, etcdPath) + ifs := r.FlagSets().Internal() + filename := env.Path(c.etcd.cfg.ConfigPath, etcdPath) + + profile, err := ifs.GetString("profile") + if err != nil { + return err + } { // retrieve data - if value, err := c.etcd.GetPath(ctx, cluster, etcdPath); err != nil { + if value, err := c.etcd.GetPath(ctx, cluster, profile, etcdPath); err != nil { return err } else { prev = []byte(strings.ReplaceAll(value, "\r\r\n", "\n")) @@ -173,7 +203,7 @@ func (c *Command) edit(ctx context.Context, r *readline.Readline) error { } c.l.Info("updating config") - if out, err := c.etcd.SetPath(ctx, cluster, etcdPath, string(next)); err != nil { + if out, err := c.etcd.SetPath(ctx, cluster, profile, etcdPath, string(next)); err != nil { return errors.Wrap(err, out) } return nil diff --git a/etcd-io/etcd/etcd.go b/etcd-io/etcd/etcd.go index c6f5c4f..7066673 100644 --- a/etcd-io/etcd/etcd.go +++ b/etcd-io/etcd/etcd.go @@ -65,13 +65,13 @@ func New(l log.Logger, kubectl *kubectl.Kubectl, opts ...Option) (*ETCD, error) // ~ Public methods // ------------------------------------------------------------------------------------------------ -func (c *ETCD) GetPath(ctx context.Context, cluster Cluster, path string) (string, error) { +func (c *ETCD) GetPath(ctx context.Context, cluster Cluster, profile, path string) (string, error) { if out, err := shell.New(ctx, c.l, "kubectl", "exec", "-it", cluster.PodName, "--namespace", cluster.Namespace, "--", "/bin/sh", "-c", "'etcdctl get "+path+" --print-value-only'", ). - Env(c.kubectl.Cluster(cluster.Name).Env()). + Env(c.kubectl.Cluster(cluster.Name).Env(profile)). Output(); err != nil { return "", err } else { @@ -79,13 +79,13 @@ func (c *ETCD) GetPath(ctx context.Context, cluster Cluster, path string) (strin } } -func (c *ETCD) SetPath(ctx context.Context, cluster Cluster, path, value string) (string, error) { +func (c *ETCD) SetPath(ctx context.Context, cluster Cluster, profile, path, value string) (string, error) { if out, err := shell.New(ctx, c.l, "kubectl", "exec", "-it", cluster.PodName, "--namespace", cluster.Namespace, "--", "/bin/sh", "-c", fmt.Sprintf("'echo \"%s\" | etcdctl put "+path+"'", strings.ReplaceAll(value, "\n", "\\n")), ). - Env(c.kubectl.Cluster(cluster.Name).Env()). + Env(c.kubectl.Cluster(cluster.Name).Env(profile)). Output(); err != nil { return "", err } else { diff --git a/foomo/gocontentful/command.go b/foomo/gocontentful/command.go index d7d1e3f..b9d0fbf 100644 --- a/foomo/gocontentful/command.go +++ b/foomo/gocontentful/command.go @@ -159,7 +159,7 @@ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { //nolint:forcetypeassert func (c *Command) paths(ctx context.Context) []string { return c.cache.Get("paths", func() any { - if value, err := files.Find(ctx, ".", "gocontentful.yml"); err != nil { + if value, err := files.Find(ctx, ".", "gocontentful.yaml", files.FindWithIgnore(`^\.`, "node_modules")); err != nil { c.l.Debug("failed to walk files", err.Error()) return []string{} } else { diff --git a/foomo/gotsrpc/command.go b/foomo/gotsrpc/command.go index 6814d59..07abd14 100644 --- a/foomo/gotsrpc/command.go +++ b/foomo/gotsrpc/command.go @@ -40,8 +40,9 @@ func NewCommand(l log.Logger, cache cache.Cache) *Command { }, Args: tree.Args{ { - Name: "path", - Suggest: inst.completePaths, + Name: "path", + Optional: true, + Suggest: inst.completePaths, }, }, Execute: inst.execute, diff --git a/foomo/squadron/command.go b/foomo/squadron/command.go index ef6e70d..09a7d43 100644 --- a/foomo/squadron/command.go +++ b/foomo/squadron/command.go @@ -101,6 +101,15 @@ func NewCommand(l log.Logger, squadron *Squadron, kubectl *kubectl.Kubectl, op * fs.Internal().Bool("slack", false, "send slack notification") } } + profileFlag := func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + if r.Args().HasIndex(0) { + fs.Internal().String("profile", "", "Profile to use.") + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } + return nil + } commonFlags := func(fs *readline.FlagSets) { fs.Internal().Bool("no-override", false, "ignore override files") fs.Default().Bool("verbose", inst.l.IsLevel(log.LevelDebug), "set verbose level") @@ -153,6 +162,9 @@ func NewCommand(l log.Logger, squadron *Squadron, kubectl *kubectl.Kubectl, op * Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { slackFlag(fs) commonFlags(fs) + if err := profileFlag(ctx, r, fs); err != nil { + return err + } fs.Default().Bool("diff", false, "show diff") fs.Default().Bool("push", false, "push image") fs.Default().Bool("build", false, "build image") @@ -178,8 +190,11 @@ func NewCommand(l log.Logger, squadron *Squadron, kubectl *kubectl.Kubectl, op * Description: "Uninstalls the squadron chart", Args: tree.Args{unitsArg}, Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - commonFlags(fs) slackFlag(fs) + commonFlags(fs) + if err := profileFlag(ctx, r, fs); err != nil { + return err + } return nil }, Execute: inst.execute, @@ -216,6 +231,9 @@ func NewCommand(l log.Logger, squadron *Squadron, kubectl *kubectl.Kubectl, op * Args: tree.Args{unitsArg}, Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { commonFlags(fs) + if err := profileFlag(ctx, r, fs); err != nil { + return err + } return nil }, Execute: inst.execute, @@ -236,9 +254,12 @@ func NewCommand(l log.Logger, squadron *Squadron, kubectl *kubectl.Kubectl, op * Description: "Roll back the squadron chart", Args: tree.Args{unitsArg}, Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + slackFlag(fs) commonFlags(fs) fs.Default().String("revision", "", "revision number to rollback to") - slackFlag(fs) + if err := profileFlag(ctx, r, fs); err != nil { + return err + } return nil }, Execute: inst.execute, @@ -316,9 +337,14 @@ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { tag, _ := ifs.GetString("tag") noOverride := log.MustGet(ifs.GetBool("no-override"))(c.l) + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + { // handle 1password if c.op != nil { - if ok, _ := c.op.Session(); !ok { + if ok, _ := c.op.IsAuthenticated(); !ok { c.l.Info("missing 1password session, please login") if err := c.op.SignIn(ctx); err != nil { return err @@ -343,7 +369,7 @@ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { if tag != "" { env = append(env, fmt.Sprintf("TAG=%q", tag)) } - env = append(env, c.kubectl.Cluster(cluster).Env()) + env = append(env, c.kubectl.Cluster(cluster).Env(profile)) } { // handle squadrons diff --git a/go.mod b/go.mod index 3bd217a..52d840a 100644 --- a/go.mod +++ b/go.mod @@ -8,30 +8,35 @@ require ( github.com/1Password/connect-sdk-go v1.5.0 github.com/c-bata/go-prompt v0.2.6 github.com/cloudrecipes/packagejson v1.0.0 - github.com/foomo/posh v0.4.0 + github.com/foomo/posh v0.4.1 + github.com/google/go-github/v47 v47.1.0 github.com/joho/godotenv v1.5.1 github.com/pkg/errors v0.9.1 - github.com/pterm/pterm v0.12.57 + github.com/pterm/pterm v0.12.62 github.com/samber/lo v1.38.1 - github.com/slack-go/slack v0.12.1 + github.com/slack-go/slack v0.12.2 github.com/spf13/viper v1.15.0 - golang.org/x/exp v0.0.0-20230126173853-a67bb567ff2e - golang.org/x/sync v0.1.0 + golang.org/x/exp v0.0.0-20230307190834-24139beb5833 + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 + golang.org/x/sync v0.2.0 gopkg.in/yaml.v3 v3.0.1 ) require ( atomicgo.dev/cursor v0.1.1 // indirect atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.0.2 // indirect github.com/alecthomas/chroma v0.10.0 // indirect github.com/charlievieth/fastwalk v1.0.1 // indirect github.com/containerd/console v1.0.3 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gookit/color v1.5.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/gookit/color v1.5.3 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/lithammer/fuzzysearch v1.1.5 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -51,9 +56,13 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 8d3c2c9..e8a49fc 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4= atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.0.2 h1:2e/4KY6t3wokja01Cyty6qgkQM8MotJzjtqCH70oX2Q= +atomicgo.dev/schedule v0.0.2/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -86,8 +88,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/foomo/posh v0.4.0 h1:vL+EJ+nQKaswAnrqvMn9ptx+s/BY6xOddHY/aJPIxug= -github.com/foomo/posh v0.4.0/go.mod h1:+ir4dZiQ3ReOQP3cfpK8IAYd4xymMOEpHaOxbWJ32mc= +github.com/foomo/posh v0.4.1 h1:2gUEzrwlNS/KqVOq5DcQi7oHovrsDQOJ6wJSjchxDV0= +github.com/foomo/posh v0.4.1/go.mod h1:Q631XvEisDS5UDJe4UO10R8ENBMO9c9+qmSPM0CV2WI= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 h1:kXNtle4AoQnngdm+gwt4ku6Llbzw3EFHgZYpL618JaI= github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5/go.mod h1:+syUfnvYJUO5A+6QMQYXAyzkxHMNlj9dH2LIeQfBSjc= @@ -124,6 +126,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -135,8 +140,13 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8= +github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -159,8 +169,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= +github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= 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/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -188,8 +198,8 @@ 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/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= -github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -230,8 +240,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.57 h1:HTjDUmILmh6hIsEidRdpxQAiqcoHCdvRCxIR3KZ0/XE= -github.com/pterm/pterm v0.12.57/go.mod h1:7rswprkyxYOse1IMh79w42jvReNHxro4z9oHfqjIdzM= +github.com/pterm/pterm v0.12.62 h1:Xjj5Wl6UR4Il9xOiDUOZRwReRTdO75if/JdWsn9I59s= +github.com/pterm/pterm v0.12.62/go.mod h1:+c3ujjE7N5qmNx6eKAa7YVSC6m/gCorJJKhzwYTbL90= 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= @@ -241,8 +251,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/slack-go/slack v0.12.1 h1:X97b9g2hnITDtNsNe5GkGx6O2/Sz/uC20ejRZN6QxOw= -github.com/slack-go/slack v0.12.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= +github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= +github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -266,7 +276,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= @@ -280,6 +290,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -294,7 +305,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -308,8 +322,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230126173853-a67bb567ff2e h1:nEzRHNOazEST44vMvEwxGxnYGrzXEmxJmnti5mKSWTk= -golang.org/x/exp v0.0.0-20230126173853-a67bb567ff2e/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -334,6 +348,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -365,6 +381,10 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -374,6 +394,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -384,8 +406,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -432,15 +456,19 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -448,8 +476,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -502,6 +531,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -535,6 +566,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -598,6 +630,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/goharbor/harbor/command.go b/goharbor/harbor/command.go new file mode 100644 index 0000000..4266003 --- /dev/null +++ b/goharbor/harbor/command.go @@ -0,0 +1,147 @@ +package harbor + +import ( + "context" + "os" + + "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/shell" + "github.com/foomo/posh/pkg/util" + "github.com/foomo/posh/pkg/util/browser" + "github.com/google/go-github/v47/github" + "github.com/pterm/pterm" + "github.com/spf13/viper" + "golang.org/x/oauth2" +) + +type ( + Command struct { + l log.Logger + cfg Config + name string + configKey string + commandTree tree.Root + } + CommandOption func(*Command) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func CommandWithName(v string) CommandOption { + return func(o *Command) { + o.name = v + } +} + +func WithConfigKey(v string) CommandOption { + return func(o *Command) { + o.configKey = v + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewCommand(l log.Logger, opts ...CommandOption) *Command { + inst := &Command{ + l: l.Named("harbor"), + name: "harbor", + configKey: "open", + } + for _, opt := range opts { + if opt != nil { + opt(inst) + } + } + if err := viper.UnmarshalKey(inst.configKey, &inst.cfg); err != nil { + return nil + } + + inst.commandTree = tree.New(&tree.Node{ + Name: inst.name, + Description: "Run harbor", + Execute: inst.auth, + Nodes: tree.Nodes{ + { + Name: "auth", + Args: nil, + Description: "Sign in to Harbor", + Execute: inst.auth, + }, + { + Name: "docker", + Args: nil, + Description: "Configure docker.", + Execute: inst.docker, + }, + }, + }) + + return inst +} + +// ------------------------------------------------------------------------------------------------ +// ~ 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) auth(ctx context.Context, r *readline.Readline) error { + return browser.OpenRawURL(c.cfg.AuthURL) +} + +func (c *Command) docker(ctx context.Context, r *readline.Readline) error { + client := github.NewClient( + oauth2.NewClient( + ctx, + oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}, + ), + ), + ) + + var username string + user, _, err := client.Users.Get(ctx, "") + if err == nil && user != nil && user.Login != nil { + username = *user.Login + } else if username, err = util.Prompt("github username"); err != nil { + return err + } + + pterm.Info.Println("registry: " + c.cfg.URL) + pterm.Info.Println("username: " + username) + pterm.Info.Println("please enter your CLI secret as password...") + + return shell.New(ctx, c.l, "docker", "login", c.cfg.URL, "-u", username). + Args(r.AdditionalArgs()...). + Args(r.AdditionalFlags()...). + Run() +} diff --git a/goharbor/harbor/config.go b/goharbor/harbor/config.go new file mode 100644 index 0000000..5d02331 --- /dev/null +++ b/goharbor/harbor/config.go @@ -0,0 +1,6 @@ +package harbor + +type Config struct { + URL string `json:"url" yaml:"url"` + AuthURL string `json:"authUrl" yaml:"authUrl"` +} diff --git a/google/gcloud/checker.go b/google/gcloud/checker.go index ede0a34..de4e841 100644 --- a/google/gcloud/checker.go +++ b/google/gcloud/checker.go @@ -2,18 +2,19 @@ package gcloud import ( "context" + "fmt" "github.com/foomo/posh/pkg/log" "github.com/foomo/posh/pkg/prompt/check" ) -func AccountChecker(p *GCloud) check.Checker { +func AuthChecker(p *GCloud) check.Checker { return func(ctx context.Context, l log.Logger) check.Info { - name := "GCloud: Account" + name := "GCloud" if account, err := p.ActiveAccount(ctx, l); err != nil { return check.NewFailureInfo(name, "Error: "+err.Error()) } else { - return check.NewSuccessInfo(name, account) + return check.NewSuccessInfo(name, fmt.Sprintf("Authenticated (%s)", account)) } } } diff --git a/google/gcloud/command.go b/google/gcloud/command.go index 29ce6d9..debbea0 100644 --- a/google/gcloud/command.go +++ b/google/gcloud/command.go @@ -108,6 +108,10 @@ func NewCommand(l log.Logger, gcloud *GCloud, kubectl *kubectl.Kubectl, opts ... }, }, }, + Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + fs.Internal().String("profile", "", "Store credentials in given profile.") + return fs.Internal().SetValues("profile", "gcloud") + }, Execute: inst.containerClustersGetCredentials, }, }, @@ -206,11 +210,14 @@ func (c *Command) authConfigureDocker(ctx context.Context, r *readline.Readline) func (c *Command) containerClustersGetCredentials(ctx context.Context, r *readline.Readline) error { var args []string + ifs := r.FlagSets().Internal() clusterName := r.Args().At(1) + cluster, err := c.gcloud.cfg.Cluster(clusterName) if err != nil { return errors.Errorf("failed to retrieve cluster for: %q", clusterName) } + kubectlCluster := c.kubectl.Cluster(c.clusterNameFn(clusterName, cluster)) if kubectlCluster == nil { return errors.Errorf("failed to retrieve kubectl cluster for: %q", cluster.Name) @@ -224,12 +231,17 @@ func (c *Command) containerClustersGetCredentials(ctx context.Context, r *readli } } + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + return shell.New(ctx, c.l, "gcloud", "container", "clusters", "get-credentials", cluster.Name, "--project", cluster.Project, "--region", cluster.Region, ). Args(args...). Args(r.AdditionalArgs()...). - Env(kubectlCluster.Env()). + Env(kubectlCluster.Env(profile)). Run() } diff --git a/gravitational/teleport/checker.go b/gravitational/teleport/checker.go new file mode 100644 index 0000000..2b6007d --- /dev/null +++ b/gravitational/teleport/checker.go @@ -0,0 +1,19 @@ +package teleport + +import ( + "context" + + "github.com/foomo/posh/pkg/log" + "github.com/foomo/posh/pkg/prompt/check" +) + +func AuthChecker(p *Teleport) check.Checker { + return func(ctx context.Context, l log.Logger) check.Info { + name := "Teleport" + if p.IsAuthenticated(ctx) { + return check.NewSuccessInfo(name, "Authenticated") + } else { + return check.NewFailureInfo(name, "Run `teleport auth` to sign into teleport") + } + } +} diff --git a/gravitational/teleport/command.go b/gravitational/teleport/command.go index 0da630e..e169186 100644 --- a/gravitational/teleport/command.go +++ b/gravitational/teleport/command.go @@ -14,34 +14,54 @@ import ( "github.com/foomo/posh/pkg/util/suggests" ) -type Command struct { - l log.Logger - name string - cache cache.Cache - kubectl *kubectl.Kubectl - teleport *Teleport - commandTree tree.Root +type ( + Command struct { + l log.Logger + name string + cache cache.Cache + kubectl *kubectl.Kubectl + teleport *Teleport + commandTree tree.Root + } + CommandOption func(*Command) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func CommandWithName(v string) CommandOption { + return func(o *Command) { + o.name = v + } } // ------------------------------------------------------------------------------------------------ // ~ Constructor // ------------------------------------------------------------------------------------------------ -func NewCommand(l log.Logger, cache cache.Cache, teleport *Teleport, kubectl *kubectl.Kubectl, opts ...Option) *Command { +func NewCommand(l log.Logger, cache cache.Cache, teleport *Teleport, kubectl *kubectl.Kubectl, opts ...CommandOption) *Command { inst := &Command{ l: l.Named("teleport"), + name: "teleport", cache: cache, kubectl: kubectl, teleport: teleport, } + for _, opt := range opts { + if opt != nil { + opt(inst) + } + } inst.commandTree = tree.New(&tree.Node{ Name: "teleport", Description: "Manage access points through teleport", + Execute: inst.auth, Nodes: tree.Nodes{ { - Name: "login", - Execute: inst.login, + Name: "auth", + Execute: inst.auth, }, { Name: "kubeconfig", @@ -55,6 +75,10 @@ func NewCommand(l log.Logger, cache cache.Cache, teleport *Teleport, kubectl *ku }, }, }, + Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + fs.Internal().String("profile", "", "Profile to use.") + return fs.Internal().SetValues("profile", "teleport") + }, Execute: inst.kubeconfig, }, { @@ -74,64 +98,6 @@ func NewCommand(l log.Logger, cache cache.Cache, teleport *Teleport, kubectl *ku }, }) - /* - // Execute ... - func (c *Teleport) Execute(args, passArgs, pipeArgs []string) error { - var env []string - cmd, args := args[0], args[1:] - - env = append(env, "HOME="+path.Join(os.Getenv("PROJECT_ROOT"), "devops", "config")) - - switch cmd { - case "login": - if err := shell.New("tsh", "login"). - WithArgs( - "--auth=github", - fmt.Sprintf("--proxy=%s:443", c.config().Hostname), - ). - WithPassArgs(passArgs...). - WithPipeArgs(pipeArgs...). - WithEnv(env). - Run(); err != nil { - return err - } - case "database": - database, _ := args[0], args[1:] - databaseUser := "developers" - if value := os.Getenv("TELEPORT_DATABASE_USER"); value != "" { - databaseUser = value - } - if err := shell.New("tsh", "db", "login", "--db-user", databaseUser, database). - WithPassArgs(passArgs...). - WithPipeArgs(pipeArgs...). - WithEnv(env). - Run(); err != nil { - return err - } - case "kubeconfig": - cluster, _ := args[0], args[1:] - env = append(env, fmt.Sprintf("KUBECONFIG=%s", kubectl.Cluster(cluster).GetConfig())) - // delete old config - kubectl.Cluster(cluster).DeleteConfig() - // generate & filter new config - if err := shell.New("tsh", "kube", "login", cluster). - WithPassArgs(passArgs...). - WithPipeArgs(pipeArgs...). - WithEnv(env). - Run(); err != nil { - return err - } else if err = config.GenerateTeleportKubeconfig(kubectl.Cluster(cluster).GetConfig(), cluster); err != nil { - return errors.Wrap(err, "failed to create teleport kubeconfig") - } - } - - cache.Clear("") - - return nil - } - - */ - return inst } @@ -167,7 +133,6 @@ func (c *Command) database(ctx context.Context, r *readline.Readline) error { databse := r.Args().At(1) return shell.New(ctx, c.l, "tsh", "db", "login", - fmt.Sprintf("--proxy=%s", c.teleport.Config().Hostname), "--db-user", c.teleport.Config().Database.EnvUser(), databse, ). @@ -178,26 +143,31 @@ func (c *Command) database(ctx context.Context, r *readline.Readline) error { } func (c *Command) kubeconfig(ctx context.Context, r *readline.Readline) error { + ifs := r.FlagSets().Internal() cluster := c.kubectl.Cluster(r.Args().At(1)) + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + // delete old config - if err := cluster.DeleteConfig(); err != nil { + if err := cluster.DeleteConfig(profile); err != nil { return err } // generate & filter new config return shell.New(ctx, c.l, "tsh", "kube", "login", - fmt.Sprintf("--proxy=%s", c.teleport.Config().Hostname), cluster.Name(), ). - Env(cluster.Env()). + Env(cluster.Env(profile)). Args(r.Flags()...). Args(r.AdditionalArgs()...). Args(r.AdditionalFlags()...). Run() } -func (c *Command) login(ctx context.Context, r *readline.Readline) error { +func (c *Command) auth(ctx context.Context, r *readline.Readline) error { if err := shell.New(ctx, c.l, "tsh", "login", fmt.Sprintf("--proxy=%s", c.teleport.Config().Hostname), "--auth=github", diff --git a/gravitational/teleport/teleport.go b/gravitational/teleport/teleport.go index 4cb3b6e..bddf8b7 100644 --- a/gravitational/teleport/teleport.go +++ b/gravitational/teleport/teleport.go @@ -76,7 +76,7 @@ func (t *Teleport) Config() Config { return t.cfg } -func (t *Teleport) IsSignedIn(ctx context.Context) bool { +func (t *Teleport) IsAuthenticated(ctx context.Context) bool { if t.signedIn && time.Since(t.signedInTime) < 12*time.Hour { return true } else if _, err := shell.New(ctx, t.l, "tsh", "status").Quiet().Output(); err != nil { @@ -90,8 +90,10 @@ func (t *Teleport) IsSignedIn(ctx context.Context) bool { } // Clusters returns a list of cluster +// +//nolint:forcetypeassert func (t *Teleport) Clusters(ctx context.Context) []string { - if !t.IsSignedIn(ctx) { + if !t.IsAuthenticated(ctx) { return nil } return t.cache.Get("clusters", func() interface{} { @@ -102,7 +104,6 @@ func (t *Teleport) Clusters(ctx context.Context) []string { } value, err := shell.New(ctx, t.l, "tsh", "kube", "ls", - fmt.Sprintf("--proxy=%s", t.cfg.Hostname), fmt.Sprintf("--query='%s'", t.cfg.Query()), "--format", "json", ). @@ -126,8 +127,10 @@ func (t *Teleport) Clusters(ctx context.Context) []string { } // Databases returns a list of cluster +// +//nolint:forcetypeassert func (t *Teleport) Databases(ctx context.Context) []string { - if !t.IsSignedIn(ctx) { + if !t.IsAuthenticated(ctx) { return nil } return t.cache.Get("databases", func() interface{} { @@ -142,7 +145,6 @@ func (t *Teleport) Databases(ctx context.Context) []string { } ) value, err := shell.New(ctx, t.l, "tsh", "db", "ls", - fmt.Sprintf("--proxy=%s", t.cfg.Hostname), fmt.Sprintf("--query='%s'", t.cfg.Query()), "--format", "json", ). diff --git a/hashicorp/cdktf/README.md b/hashicorp/cdktf/README.md new file mode 100644 index 0000000..f918c92 --- /dev/null +++ b/hashicorp/cdktf/README.md @@ -0,0 +1,18 @@ +# POSH CDKTF provider + +## Config + +```yaml +cdktf: + path: devops/cdktf +``` + +## Usage + +```go +func New(l log.Logger) (plugin.Plugin, error) { + // ... + inst.commands.Add(helm.NewCommand(l, kubectl)) + // ... +} +``` diff --git a/hashicorp/cdktf/command.go b/hashicorp/cdktf/command.go new file mode 100644 index 0000000..0877f70 --- /dev/null +++ b/hashicorp/cdktf/command.go @@ -0,0 +1,181 @@ +package cdktf + +import ( + "context" + "errors" + "os/exec" + + "github.com/foomo/posh/pkg/cache" + "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/shell" + "github.com/spf13/viper" +) + +type ( + Command struct { + l log.Logger + cfg Config + name string + cache cache.Namespace + configKey string + commandTree tree.Root + } + CommandOption func(*Command) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func CommandWithName(v string) CommandOption { + return func(o *Command) { + o.name = v + } +} + +func WithConfigKey(v string) CommandOption { + return func(o *Command) { + o.configKey = v + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewCommand(l log.Logger, cache cache.Cache, opts ...CommandOption) (*Command, error) { + inst := &Command{ + l: l.Named("cdktf"), + name: "cdktf", + cache: cache.Get("cdktf"), + } + for _, opt := range opts { + if opt != nil { + opt(inst) + } + } + if err := viper.UnmarshalKey(inst.configKey, &inst.cfg); err != nil { + return nil, err + } + + inst.commandTree = tree.New(&tree.Node{ + Name: inst.name, + Description: "Run cdktf", + Nodes: tree.Nodes{ + { + Name: "list", + Description: "List stacks in app", + Execute: inst.list, + }, + { + Name: "diff", + Description: "Perform a diff (terraform plan) for the given stack", + Execute: inst.diff, + }, + { + Name: "deploy", + Description: "Deploy the given stacks", + Execute: inst.deploy, + }, + { + Name: "destroy", + Description: "Destroy the given stacks", + Execute: inst.destroy, + }, + { + Name: "output", + Description: "Prints the output of stacks", + Execute: inst.output, + }, + }, + }) + + 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) Validate(ctx context.Context, r *readline.Readline) error { + if _, err := exec.LookPath("cdktf"); err != nil { + c.l.Print() + return errors.New(` +Please ensure you have the cdktf installed! + +- Install binary: + +$ npm install --global cdktf-cli + `) + } + return nil +} + +func (c *Command) Help(ctx context.Context, r *readline.Readline) string { + return c.commandTree.Help(ctx, r) +} + +// ------------------------------------------------------------------------------------------------ +// ~ Private methods +// ------------------------------------------------------------------------------------------------ + +func (c *Command) list(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, "cdktf", "list"). + Dir(c.cfg.Path). + Run() +} + +func (c *Command) diff(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, "cdktf", "diff"). + Dir(c.cfg.Path). + Run() +} + +func (c *Command) deploy(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, "cdktf", "deploy"). + Dir(c.cfg.Path). + Run() +} + +func (c *Command) destroy(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, "cdktf", "destroy"). + Dir(c.cfg.Path). + Run() +} + +func (c *Command) output(ctx context.Context, r *readline.Readline) error { + return shell.New(ctx, c.l, "cdktf", "output"). + Dir(c.cfg.Path). + Run() +} + +//nolint:forcetypeassert +//func (c *Command) paths(ctx context.Context) []string { +// return c.cache.Get("paths", func() any { +// if value, err := files.Find(ctx, ".", "cdktf.yml"); err != nil { +// c.l.Debug("failed to walk files", err.Error()) +// return []string{} +// } else { +// return value +// } +// }).([]string) +//} diff --git a/hashicorp/cdktf/config.go b/hashicorp/cdktf/config.go new file mode 100644 index 0000000..3d5ae87 --- /dev/null +++ b/hashicorp/cdktf/config.go @@ -0,0 +1,5 @@ +package cdktf + +type Config struct { + Path string `yaml:"path"` +} diff --git a/helm/helm/command.go b/helm/helm/command.go index cc7f356..d6fa778 100644 --- a/helm/helm/command.go +++ b/helm/helm/command.go @@ -2,6 +2,7 @@ package helm import ( "context" + "fmt" "github.com/foomo/posh-providers/kubernets/kubectl" "github.com/foomo/posh/pkg/command/tree" @@ -43,7 +44,8 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl) *Command { name: "helm", kubectl: kubectl, } - allFlags := func(fs *readline.FlagSets) { + + allFlags := func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { fs.Default().Bool("help", false, "help for helm") fs.Default().Bool("debug", false, "enable verbose output") fs.Default().String("namespace", "", "namespace scope for this request") @@ -53,6 +55,13 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl) *Command { fs.Default().Bool("dry-run", false, "assume aws profile") fs.Default().Bool("atomic", false, "delete installation on failure") fs.Default().Bool("wait", false, "wait until all resources a ready") + fs.Internal().String("profile", "", "Profile to use.") + if r.Args().HasIndex(0) { + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } + return nil } inst.commandTree = tree.New(&tree.Node{ @@ -69,46 +78,33 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl) *Command { { Name: "create", Description: "Create a new chart with the given name", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "dependency", Description: "Manage a chart's dependencies", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "diff", Description: "Preview helm upgrade changes as a diff", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "env", Description: "Helm client environment information", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "get", Description: "Download extended information of a named release", Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) fs.Default().String("revision", "", "get the named release with revision") - return nil + return allFlags(ctx, r, fs) }, Args: tree.Args{ { @@ -131,174 +127,119 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl) *Command { { Name: "help", Description: "Help about any command", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "history", Description: "Fetch release history", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "install", Description: "Install a chart", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "lint", Description: "Examine a chart for possible issues", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "list", Description: "List releases", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "package", Description: "Package a chart directory into a chart archive", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "plugin", Description: "Install, list, or uninstall Helm plugins", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "pull", Description: "Download a chart from a repository and (optionally) unpack it in local directory", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "repo", Description: "Add, list, remove, update, and index chart repositories", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "rollback", Description: "Roll back a release to a previous revision", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "search", Description: "Search for a keyword in charts", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "show", Description: "Show information of a chart", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "status", Description: "Display the status of the named release", Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) fs.Default().Bool("show-desc", false, "show description") - return nil + return allFlags(ctx, r, fs) }, Execute: inst.execute, }, { Name: "template", Description: "Locally render templates", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "test", Description: "Run tests for a release", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "uninstall", Description: "Uninstall a release", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "upgrade", Description: "Upgrade a release", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "verify", Description: "Verify that a chart at the given path has been signed and is valid", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, { Name: "version", Description: "Print the client version information", - Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { - allFlags(fs) - return nil - }, - Execute: inst.execute, + Flags: allFlags, + Execute: inst.execute, }, }, }, @@ -328,7 +269,7 @@ func (c *Command) Validate(ctx context.Context, r *readline.Readline) error { switch { case r.Args().LenIs(0): return errors.New("missing [CLUSTER] argument") - case !c.kubectl.Cluster(r.Args().At(0)).ConfigExists(): + case !c.kubectl.Cluster(r.Args().At(0)).ConfigExists(""): return errors.New("invalid [CLUSTER] argument") case r.Args().LenIs(1): return errors.New("missing [CMD] argument") @@ -350,12 +291,22 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ------------------------------------------------------------------------------------------------ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { + fs := r.FlagSets().Default() + ifs := r.FlagSets().Internal() cluster, args := c.kubectl.Cluster(r.Args().At(0)), r.Args()[1:] + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + + fmt.Println(args) + return shell.New(ctx, c.l, "helm"). Args(args...). - Args(r.Flags()...). + Args(fs.Visited().Args()...). Args(r.AdditionalArgs()...). - Env(cluster.Env()). + Args(r.AdditionalFlags()...). + Env(cluster.Env(profile)). Run() } diff --git a/jlesquembre/kubeprompt/command.go b/jlesquembre/kubeprompt/command.go index dbadea3..97a8e7f 100644 --- a/jlesquembre/kubeprompt/command.go +++ b/jlesquembre/kubeprompt/command.go @@ -36,6 +36,15 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl) *Command { Suggest: inst.completeClusters, }, }, + Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + if r.Args().HasIndex(0) { + fs.Internal().String("profile", "", "Profile to use.") + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } + return nil + }, Execute: inst.execute, }) @@ -71,9 +80,16 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ------------------------------------------------------------------------------------------------ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { + ifs := r.FlagSets().Internal() + + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + return shell.New(ctx, c.l, "kube-prompt"). Args(r.AdditionalArgs()...). - Env(c.kubectl.Cluster(r.Args().At(0)).Env()). + Env(c.kubectl.Cluster(r.Args().At(0)).Env(profile)). Run() } diff --git a/kubernets/kubectl/cluster.go b/kubernets/kubectl/cluster.go index 7e1ead5..3ada4b3 100644 --- a/kubernets/kubectl/cluster.go +++ b/kubernets/kubectl/cluster.go @@ -41,32 +41,54 @@ func (c *Cluster) String() string { return c.name } -func (c *Cluster) Env() string { - return fmt.Sprintf("KUBECONFIG=%s", c.Config()) +func (c *Cluster) Env(profile string) string { + return fmt.Sprintf("KUBECONFIG=%s", c.Config(profile)) } -func (c *Cluster) Config() string { +func (c *Cluster) Config(profile string) string { + if profile != "" { + return env.Path(c.kubectl.cfg.ConfigPath, profile, c.Name()+".yaml") + } return env.Path(c.kubectl.cfg.ConfigPath, c.Name()+".yaml") } -func (c *Cluster) ConfigExists() bool { - if _, err := os.Stat(c.Config()); err == nil { +func (c *Cluster) ConfigExists(profile string) bool { + if _, err := os.Stat(c.Config(profile)); err == nil { return true } return false } -func (c *Cluster) DeleteConfig() error { - if !c.ConfigExists() { +func (c *Cluster) DeleteConfig(profile string) error { + if !c.ConfigExists(profile) { return nil } - return os.Remove(c.Config()) + return os.Remove(c.Config(profile)) } //nolint:forcetypeassert -func (c *Cluster) Namespaces(ctx context.Context) []string { - return c.kubectl.cache.Get(c.name+"-namespaces", func() any { - if sh, err := c.shell(ctx, +func (c *Cluster) Profiles(ctx context.Context) []string { + return c.kubectl.cache.Get("profiles", func() any { + files, err := os.ReadDir(c.kubectl.cfg.ConfigPath) + if err != nil { + c.l.Debug(err.Error()) + return []string{} + } + + ret := []string{} + for _, f := range files { + if f.IsDir() && !strings.HasPrefix(f.Name(), ".") { + ret = append(ret, f.Name()) + } + } + return ret + }).([]string) +} + +//nolint:forcetypeassert +func (c *Cluster) Namespaces(ctx context.Context, profile string) []string { + return c.kubectl.cache.Get(profile+"-"+c.name+"-namespaces", func() any { + if sh, err := c.shell(ctx, profile, "get", "namespaces", "-o", "jsonpath='{.items[*].metadata.name}'", ); err != nil { @@ -82,9 +104,9 @@ func (c *Cluster) Namespaces(ctx context.Context) []string { } //nolint:forcetypeassert -func (c *Cluster) Pods(ctx context.Context, namespace string) []string { - return c.kubectl.cache.Get(c.name+"-"+namespace+"-pods", func() any { - if sh, err := c.shell(ctx, +func (c *Cluster) Pods(ctx context.Context, profile, namespace string) []string { + return c.kubectl.cache.Get(profile+"-"+c.name+"-"+namespace+"-pods", func() any { + if sh, err := c.shell(ctx, profile, "get", "pods", "-n", namespace, "-o", "jsonpath='{.items[*].metadata.name}'", @@ -104,7 +126,7 @@ func (c *Cluster) Pods(ctx context.Context, namespace string) []string { // ~ Private methods // ------------------------------------------------------------------------------------------------ -func (c *Cluster) shell(ctx context.Context, args ...string) (*shell.Shell, error) { +func (c *Cluster) shell(ctx context.Context, profile string, args ...string) (*shell.Shell, error) { sh := shell.New(ctx, c.l, "kubectl").Args(args...) if c.kubectl.authTokenProvider != nil { if token, err := c.kubectl.authTokenProvider(ctx, c.Name()); err != nil { @@ -113,5 +135,6 @@ func (c *Cluster) shell(ctx context.Context, args ...string) (*shell.Shell, erro sh.Args("--token", token) } } - return sh.Env(c.Env()), nil + + return sh.Env(c.Env(profile)), nil } diff --git a/onepassword/checker.go b/onepassword/checker.go index f6355dc..93a8ad9 100644 --- a/onepassword/checker.go +++ b/onepassword/checker.go @@ -7,13 +7,13 @@ import ( "github.com/foomo/posh/pkg/prompt/check" ) -func SessionChecker(p *OnePassword) check.Checker { +func AuthChecker(p *OnePassword) check.Checker { return func(ctx context.Context, l log.Logger) check.Info { - name := "1Password: Session" - if ok, _ := p.Session(); ok { - return check.NewSuccessInfo(name, "Signed in") + name := "1Password" + if ok, _ := p.IsAuthenticated(); ok { + return check.NewSuccessInfo(name, "Authenticated") } else { - return check.NewFailureInfo(name, "Run `op signin` to sign into 1password") + return check.NewFailureInfo(name, "Run `op auth` to sign into 1password") } } } diff --git a/onepassword/command.go b/onepassword/command.go index 5da7e6b..1fdd088 100644 --- a/onepassword/command.go +++ b/onepassword/command.go @@ -42,12 +42,12 @@ func NewCommand(l log.Logger, op *OnePassword, opts ...CommandOption) (*Command, inst.commandTree = tree.New(&tree.Node{ Name: "op", Description: "Execute 1Password commands", - Execute: inst.signin, + Execute: inst.auth, Nodes: tree.Nodes{ { - Name: "signin", + Name: "auth", Description: "Sign into your account", - Execute: inst.signin, + Execute: inst.auth, }, { Name: "get", @@ -125,8 +125,8 @@ func (c *Command) register(ctx context.Context, r *readline.Readline) error { Wait() } -func (c *Command) signin(ctx context.Context, r *readline.Readline) error { - if ok, _ := c.op.Session(); ok { +func (c *Command) auth(ctx context.Context, r *readline.Readline) error { + if ok, _ := c.op.IsAuthenticated(); ok { c.l.Info("Already signed in") return nil } else if err := c.op.SignIn(ctx); err != nil { diff --git a/onepassword/onepassword.go b/onepassword/onepassword.go index 0007f9c..4d5360e 100644 --- a/onepassword/onepassword.go +++ b/onepassword/onepassword.go @@ -85,7 +85,7 @@ func New(l log.Logger, cache cache.Cache, opts ...Option) (*OnePassword, error) // ~ Public methods // ------------------------------------------------------------------------------------------------ -func (op *OnePassword) Session() (bool, error) { +func (op *OnePassword) IsAuthenticated() (bool, error) { var sessChanged bool sess := os.Getenv("OP_SESSION_" + op.cfg.Account) op.isSignedInLock.Lock() @@ -127,7 +127,7 @@ func (op *OnePassword) Session() (bool, error) { } func (op *OnePassword) SignIn(ctx context.Context) error { - if ok, _ := op.Session(); ok { + if ok, _ := op.IsAuthenticated(); ok { return nil } @@ -189,7 +189,7 @@ func (op *OnePassword) Get(ctx context.Context, secret Secret) (string, error) { return strings.ReplaceAll(strings.TrimSpace(value), "\\n", "\n"), nil } } else { - if ok, _ := op.Session(); !ok { + if ok, _ := op.IsAuthenticated(); !ok { return "", ErrNotSignedIn } else if fields := op.clientGet(ctx, secret.Vault, secret.Item); len(fields) == 0 { return "", fmt.Errorf("could not find secret '%s' '%s'", secret.Vault, secret.Item) @@ -209,7 +209,7 @@ func (op *OnePassword) GetDocument(ctx context.Context, secret Secret) (string, return value, nil } } else { - if ok, _ := op.Session(); !ok { + if ok, _ := op.IsAuthenticated(); !ok { return "", ErrNotSignedIn } else if value := op.clientGetDoument(ctx, secret.Vault, secret.Item); len(value) == 0 { return "", fmt.Errorf("could not find document '%s' '%s'", secret.Vault, secret.Item) @@ -220,7 +220,7 @@ func (op *OnePassword) GetDocument(ctx context.Context, secret Secret) (string, } func (op *OnePassword) GetOnetimePassword(ctx context.Context, account, uuid string) (string, error) { - if ok, _ := op.Session(); !ok { + if ok, _ := op.IsAuthenticated(); !ok { return "", ErrNotSignedIn } @@ -421,7 +421,7 @@ func (op *OnePassword) watch() { if v, ok := op.watching[op.cfg.Account]; !ok || !v { go func() { for { - if ok, err := op.Session(); err != nil { + if ok, err := op.IsAuthenticated(); err != nil { op.l.Warnf("\n1password session keep alive failed for '%s' (%s)", op.cfg.Account, err.Error()) op.watching[op.cfg.Account] = false return diff --git a/pivotal/licensefinder/command.go b/pivotal/licensefinder/command.go index 0788b7f..1ea9780 100644 --- a/pivotal/licensefinder/command.go +++ b/pivotal/licensefinder/command.go @@ -2,9 +2,9 @@ package licensefinder import ( "context" - "os" "os/exec" "path" + "sort" "strings" "github.com/foomo/posh/pkg/cache" @@ -15,6 +15,8 @@ import ( "github.com/foomo/posh/pkg/shell" "github.com/foomo/posh/pkg/util/files" "github.com/pkg/errors" + "github.com/samber/lo" + "github.com/spf13/viper" ) type ( @@ -49,11 +51,11 @@ func CommandWithConfigKey(v string) CommandOption { // ~ Constructor // ------------------------------------------------------------------------------------------------ -func NewCommand(l log.Logger, cache cache.Cache, opts ...CommandOption) *Command { +func NewCommand(l log.Logger, cache cache.Cache, opts ...CommandOption) (*Command, error) { inst := &Command{ l: l.Named("licensefinder"), name: "licensefinder", - configKey: "licensefinder", + configKey: "licenseFinder", cache: cache.Get("licensefinder"), } for _, opt := range opts { @@ -61,77 +63,82 @@ func NewCommand(l log.Logger, cache cache.Cache, opts ...CommandOption) *Command opt(inst) } } + if err := viper.UnmarshalKey(inst.configKey, &inst.cfg); err != nil { + return nil, err + } - addNode := tree.Node{ - Name: "add", - Args: tree.Args{{Name: "name"}}, - Description: "Add entry", - Execute: inst.execute, - } - listNode := tree.Node{ - Name: "list", - Description: "List entry", - Execute: inst.execute, - } - removeNode := tree.Node{ - Name: "remove", - Args: tree.Args{{Name: "name"}}, - Description: "Remove entry", - Execute: inst.execute, + nameArg := &tree.Arg{ + Name: "name", + Description: "Name of the license", } inst.commandTree = tree.New(&tree.Node{ Name: inst.name, - Description: "Run license finder", - Execute: inst.execute, + Description: "List unapproved dependencies", + Execute: inst.actionItems, Nodes: tree.Nodes{ { - Name: "restricted_licenses", - Description: "Manage restricted licenses", + Name: "report", + Description: "Print a report of the project's dependencies", + Execute: inst.report, + }, + { + Name: "add", + Description: "Add licenses or dependencies", Nodes: tree.Nodes{ - &addNode, - &listNode, - &removeNode, + { + Name: "permitted", + Description: "Add permitted licenses", + Args: tree.Args{nameArg}, + Execute: inst.addPermitted, + }, + { + Name: "ignored", + Description: "Add ignored dependencies", + Args: tree.Args{nameArg}, + Execute: inst.addIgnored, + }, }, }, { - Name: "ignored_dependencies", - Description: "Manage ignored dependencies", + Name: "list", + Description: "List licenses or dependencies", Nodes: tree.Nodes{ - &addNode, - &listNode, - &removeNode, + { + Name: "permitted", + Description: "Add permitted licenses", + Execute: inst.listPermitted, + }, + { + Name: "ignored", + Description: "List ignored dependencies", + Args: tree.Args{nameArg}, + Execute: inst.listIgnored, + }, }, }, { - Name: "permitted_licenses", - Description: "Manage permitted licenses", + Name: "remove", + Description: "Remove licenses or dependencies", Nodes: tree.Nodes{ - &addNode, - &listNode, - &removeNode, - }, - }, - { - Name: "approvals", - Description: "Manage approvals", - Nodes: tree.Nodes{ - &addNode, - &removeNode, - }, - }, - { - Name: "licenses", - Description: "Manage licenses", - Nodes: tree.Nodes{ - &addNode, - &removeNode, + { + Name: "permitted", + Description: "Add permitted licenses", + Args: tree.Args{nameArg}, + Execute: inst.removePermitted, + }, + { + Name: "ignored", + Description: "Remove ignored dependencies", + Args: tree.Args{nameArg}, + Execute: inst.removeIgnored, + }, }, }, }, }) - return inst + return inst, nil } // ------------------------------------------------------------------------------------------------ @@ -162,17 +169,6 @@ $ brew update $ brew install licensefinder `) } - switch { - case r.Args().LenIs(0): - return nil - case r.Args().LenGt(1): - return errors.New("too many arguments") - } - - if info, err := os.Stat(r.Args().At(0)); err != nil || info.IsDir() { - return errors.New("invalid [path] parameter") - } - return nil } @@ -188,31 +184,70 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ~ Private methods // ------------------------------------------------------------------------------------------------ -func (c *Command) execute(ctx context.Context, r *readline.Readline) error { - var paths []string - args := []string{ - "--log-directory=" + c.cfg.LogPath, - "--decisions_file=" + c.cfg.DecisionsPath, - } - if r.Args().LenIs(0) { - paths = append(paths, c.paths(ctx, "go.sum")...) - paths = append(paths, c.paths(ctx, "yarn.lock")...) - args = append(args, "--aggregate_paths="+strings.Join(paths, " ")) - } +func (c *Command) addPermitted(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, append([]string{"permitted_licenses", "add"}, r.Args().From(2)...)...) +} + +func (c *Command) listPermitted(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, "permitted_licenses", "list") +} + +func (c *Command) removePermitted(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, append([]string{"permitted_licenses", "remove"}, r.Args().From(2)...)...) +} + +func (c *Command) addIgnored(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, append([]string{"ignored_dependencies", "add"}, r.Args().From(2)...)...) +} + +func (c *Command) listIgnored(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, "ignored_dependencies", "list") +} + +func (c *Command) removeIgnored(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, append([]string{"ignored_dependencies", "remove"}, r.Args().From(2)...)...) +} + +func (c *Command) actionItems(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, "action_items", c.aggregatePaths(ctx)) +} + +func (c *Command) report(ctx context.Context, r *readline.Readline) error { + return c.execute(ctx, r, "report", c.aggregatePaths(ctx)) +} + +func (c *Command) execute(ctx context.Context, r *readline.Readline, args ...string) error { return shell.New(ctx, c.l, "license_finder"). Args(args...). - Args(r.Args()...). + Args( + "--log-directory="+c.cfg.LogPath, + "--decisions-file="+c.cfg.DecisionsPath, + ). Args(r.Flags()...). Args(r.AdditionalArgs()...). + Args(r.AdditionalFlags()...). Run() } +func (c *Command) aggregatePaths(ctx context.Context) string { + var paths []string + paths = append(paths, c.paths(ctx, "go.sum")...) + paths = append(paths, c.paths(ctx, "yarn.lock")...) + paths = lo.Uniq(paths) + sort.Strings(paths) + c.l.Info("Aggregating liceses from:") + for _, value := range paths { + c.l.Info("└ " + value) + } + return "--aggregate_paths=" + strings.Join(paths, " ") +} + //nolint:forcetypeassert func (c *Command) paths(ctx context.Context, filename string) []string { return c.cache.Get("paths-"+filename, func() any { - if value, err := files.Find(ctx, ".", filename); err != nil { + if value, err := files.Find(ctx, ".", filename, files.FindWithIgnore(`^\.`, "vendor", "node_modules")); err != nil { c.l.Debug("failed to walk files", err.Error()) - return nil + return []string{} } else { for i, s := range value { value[i] = path.Dir(s) diff --git a/stern/stern/command.go b/stern/stern/command.go index 2483296..9e69ee6 100644 --- a/stern/stern/command.go +++ b/stern/stern/command.go @@ -69,6 +69,12 @@ func NewCommand(l log.Logger, kubectl *kubectl.Kubectl, squadron *squadron.Squad fs.Default().String("selector", "", "Selector (label query) to filter on. If present, default to \".*\" for the pod-query.") fs.Default().String("since", "", "Return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to 48h") fs.Default().String("tail", "", "The number of lines from the end of the logs to show. Defaults to -1, showing all logs. (default -1)") + fs.Internal().String("profile", "", "Profile to use.") + if r.Args().HasIndex(0) { + if err := fs.Internal().SetValues("profile", inst.kubectl.Cluster(r.Args().At(0)).Profiles(ctx)...); err != nil { + return err + } + } return nil }, Args: tree.Args{ @@ -124,9 +130,16 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ------------------------------------------------------------------------------------------------ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { + ifs := r.FlagSets().Internal() cluster, fleet, squad, unit := r.Args().At(0), r.Args().At(1), r.Args().At(2), r.Args().At(3) + + profile, err := ifs.GetString("profile") + if err != nil { + return err + } + return shell.New(ctx, c.l, "stern"). - Env(c.kubectl.Cluster(cluster).Env()). + Env(c.kubectl.Cluster(cluster).Env(profile)). Args("--namespace", c.namespaceFn(cluster, fleet, squad)). Args("--selector", "\"app.kubernetes.io/name="+squad+"-"+unit+"\""). Args(r.AdditionalArgs()...). @@ -152,6 +165,7 @@ func (c *Command) completeSquadrons(ctx context.Context, t tree.Root, r *readlin return suggests.List(value) } } + func (c *Command) completeSquadronUnits(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest { cluster, fleet, squad := r.Args().At(0), r.Args().At(1), r.Args().At(2) if value, err := c.squadron.ListUnits(ctx, squad, cluster, fleet, true); err != nil { diff --git a/webdriverio/webdriverio/command.go b/webdriverio/webdriverio/command.go index 48eaba1..2c522b4 100644 --- a/webdriverio/webdriverio/command.go +++ b/webdriverio/webdriverio/command.go @@ -195,17 +195,21 @@ func (c *Command) execute(ctx context.Context, r *readline.Readline) error { if log.MustGet(ifs.GetBool("debug"))(c.l) { envs = append(envs, fmt.Sprintf("debug=%s", "true")) } + if log.MustGet(ifs.GetBool("headless"))(c.l) { envs = append(envs, fmt.Sprintf("HEADLESS=%s", "true")) } + if log.MustGet(ifs.GetBool("ci"))(c.l) { envs = append(envs, fmt.Sprintf("E2E_ENV=%s", "ci")) } else { envs = append(envs, fmt.Sprintf("E2E_ENV=%s", "chromium")) } + if value := log.MustGet(ifs.GetString("scenario"))(c.l); value != "" { envs = append(envs, fmt.Sprintf("SCENARIOS=%s", strings.Trim(value, "\""))) } + if value := log.MustGet(ifs.GetString("tag"))(c.l); value != "" { args = append(args, "--cucumberOpts.tagExpression", "'"+strings.Trim(value, "\"")+"'") }