From 7ba2c45a3a9fc75acb79d3d34a19273ac3997073 Mon Sep 17 00:00:00 2001 From: 26tanishabanik <26tanishabanik@gmail.com> Date: Fri, 2 Dec 2022 02:12:59 +0530 Subject: [PATCH] added key bindings for pushing, pulling and tagging an image Signed-off-by: 26tanishabanik <26tanishabanik@gmail.com> --- pkg/commands/image.go | 16 +++ pkg/gui/images_panel.go | 259 +++++++++++++++++++++++++++++++++++++++- pkg/gui/keybindings.go | 21 ++++ pkg/i18n/english.go | 6 + 4 files changed, 301 insertions(+), 1 deletion(-) diff --git a/pkg/commands/image.go b/pkg/commands/image.go index 5e29f198..36136875 100644 --- a/pkg/commands/image.go +++ b/pkg/commands/image.go @@ -36,6 +36,22 @@ func (i *Image) Remove(options dockerTypes.ImageRemoveOptions) error { return nil } +// Pulls an image +func (i *Image) Pull(options dockerTypes.ImagePullOptions) error { + if _, err := i.Client.ImagePull(context.Background(), i.ID, options); err != nil { + return err + } + return nil +} + +// Pushes an image +func (i *Image) Push(options dockerTypes.ImagePushOptions) error { + if _, err := i.Client.ImagePush(context.Background(), i.ID, options); err != nil { + return err + } + return nil +} + func getHistoryResponseItemDisplayStrings(layer image.HistoryResponseItem) []string { tag := "" if len(layer.Tags) > 0 { diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go index 6ef50fa9..7ad13d0b 100644 --- a/pkg/gui/images_panel.go +++ b/pkg/gui/images_panel.go @@ -4,7 +4,9 @@ import ( "fmt" "strings" "time" - + "os" + "io" + "encoding/hex" dockerTypes "github.com/docker/docker/api/types" "github.com/fatih/color" "github.com/jesseduffield/gocui" @@ -18,6 +20,7 @@ import ( "github.com/samber/lo" ) + func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] { noneLabel := "" @@ -119,6 +122,260 @@ func (gui *Gui) FilterString(view *gocui.View) string { return gui.State.Filter.needle } +func layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + + if v, err := g.SetView("help", maxX-23, 0, maxX-1, maxY-50, 5); err != nil { + if err != gocui.ErrUnknownView { + return err + } + fmt.Fprintln(v, "KEYBINDINGS") + fmt.Fprintln(v, "↑ ↓: Seek input") + fmt.Fprintln(v, "a: Enable autoscroll") + fmt.Fprintln(v, "^C: Exit") + } + + if v, err := g.SetView("stdin", 0, 0, 80, 80, 35); err != nil { + if err != gocui.ErrUnknownView { + return err + } + if _, err := g.SetCurrentView("stdin"); err != nil { + return err + } + dumper := hex.Dumper(v) + if _, err := io.Copy(dumper, os.Stdin); err != nil { + return err + } + v.Wrap = true + } + + return nil +} + +func initKeybindings(g *gocui.Gui) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + return err + } + if err := g.SetKeybinding("stdin", 'a', gocui.ModNone, autoscroll); err != nil { + return err + } + if err := g.SetKeybinding("stdin", gocui.KeyArrowUp, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + scrollView(v, -1) + return nil + }); err != nil { + return err + } + if err := g.SetKeybinding("stdin", gocui.KeyArrowDown, gocui.ModNone, + func(g *gocui.Gui, v *gocui.View) error { + scrollView(v, 1) + return nil + }); err != nil { + return err + } + return nil +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} + +func autoscroll(g *gocui.Gui, v *gocui.View) error { + v.Autoscroll = true + return nil +} + +func scrollView(v *gocui.View, dy int) error { + if v != nil { + v.Autoscroll = false + ox, oy := v.Origin() + if err := v.SetOrigin(ox, oy+dy); err != nil { + return err + } + } + return nil +} + +func (gui *Gui) handleImagePush(g *gocui.Gui, v *gocui.View) error { + type pushImageOption struct { + description string + command string + configOptions dockerTypes.ImagePushOptions + } + image, err := gui.Panels.Images.GetSelectedItem() + if err != nil { + return err + } + + + err = gui.createPromptPanel("Push " + image.Name, func (g *gocui.Gui, v *gocui.View) error { + value := gui.trimmedContent(v) + if value != "" { + options := []*pushImageOption{ + { + description: gui.Tr.ImagePull, + command: "docker push " + value, + configOptions: dockerTypes.ImagePushOptions{}, + }, + } + + menuItems := lo.Map(options, func(option *pushImageOption, _ int) *types.MenuItem { + return &types.MenuItem{ + LabelColumns: []string{ + option.description, + color.New(color.FgRed).Sprint(option.command), + }, + OnPress: func() error { + if err := image.Push(option.configOptions); err != nil { + return gui.createErrorPanel(err.Error()) + } + + return nil + }, + } + }) + + return gui.Menu(CreateMenuOptions{ + Title: "", + Items: menuItems, + }) + } + return gui.createErrorPanel("empty image:tag passed") + }) + if err != nil { + return err + } + + + if err := initKeybindings(g); err != nil { + fmt.Println("error in setting key binding: ", err) + return err + } + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + fmt.Println("error in quitting input prompt: ", err) + return err + } + + + return nil +} + +func (gui *Gui) handleImagePull(g *gocui.Gui, v *gocui.View) error { + type pullImageOption struct { + description string + command string + configOptions dockerTypes.ImagePullOptions + } + + image, err := gui.Panels.Images.GetSelectedItem() + if err != nil { + return err + } + + + err = gui.createPromptPanel("Pull for " + image.Name, func (g *gocui.Gui, v *gocui.View) error { + value := gui.trimmedContent(v) + if value != "" { + options := []*pullImageOption{ + { + description: gui.Tr.ImagePull, + command: "docker pull " + value, + configOptions: dockerTypes.ImagePullOptions{}, + }, + } + + menuItems := lo.Map(options, func(option *pullImageOption, _ int) *types.MenuItem { + return &types.MenuItem{ + LabelColumns: []string{ + option.description, + color.New(color.FgRed).Sprint(option.command), + }, + OnPress: func() error { + if err := image.Pull(option.configOptions); err != nil { + return gui.createErrorPanel(err.Error()) + } + + return nil + }, + } + }) + + return gui.Menu(CreateMenuOptions{ + Title: "", + Items: menuItems, + }) + } + return gui.createErrorPanel("empty image:tag passed") + // err = gui.createPopupPanel("Image Name", value, false, func (g *gocui.Gui, v *gocui.View) error { + // return nil + // }, func(*gocui.Gui, *gocui.View) error { + // if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + // return err + // } + // return nil + // }) + // if err != nil { + // return err + // } + // return nil + }) + if err != nil { + return err + } + + + if err := initKeybindings(g); err != nil { + fmt.Println("error in setting key binding: ", err) + return err + } + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + fmt.Println("error in quitting input prompt: ", err) + return err + } + + + return nil +} + +func (gui *Gui) handleImageTagging(g *gocui.Gui, v *gocui.View) error { + + image, err := gui.Panels.Images.GetSelectedItem() + if err != nil { + return err + } + + + err = gui.createPopupPanel("Image Name", image.Name, false, func (g *gocui.Gui, v *gocui.View) error { + return nil + }, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + + + if err := initKeybindings(g); err != nil { + fmt.Println("error in setting key binding: ", err) + return err + } + + if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { + fmt.Println("error in quitting popUp panel: ", err) + return err + } + + + return nil +} + + + func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error { type removeImageOption struct { description string diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 4c086469..1fa5fc24 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -388,6 +388,27 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleImagesBulkCommand, Description: gui.Tr.ViewBulkCommands, }, + { + ViewName: "images", + Key: 't', + Modifier: gocui.ModNone, + Handler: gui.handleImageTagging, + Description: gui.Tr.ImageTagging, + }, + { + ViewName: "images", + Key: 'p', + Modifier: gocui.ModNone, + Handler: gui.handleImagePull, + Description: gui.Tr.ImagePull, + }, + { + ViewName: "images", + Key: 'P', + Modifier: gocui.ModNone, + Handler: gui.handleImagePush, + Description: gui.Tr.ImagePush, + }, { ViewName: "volumes", Key: 'c', diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 4d9aa1d2..33a38c2c 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -122,6 +122,9 @@ type TranslationSet struct { LcNextScreenMode string LcPrevScreenMode string FilterPrompt string + ImageTagging string + ImagePull string + ImagePush string } func englishSet() TranslationSet { @@ -251,5 +254,8 @@ func englishSet() TranslationSet { LcNextScreenMode: "next screen mode (normal/half/fullscreen)", LcPrevScreenMode: "prev screen mode", FilterPrompt: "filter", + ImageTagging: "tag an image", + ImagePull: "pull an image", + ImagePush: "push an image", } }