From 1f9f82601b317e4fc32ad4a350e829637c9f5c52 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 6 Jul 2019 11:40:23 +1000 Subject: [PATCH] support bulk commands in each of the panels --- pkg/commands/container.go | 9 ++++- pkg/config/app_config.go | 57 +++++++++++++++++++++++++++- pkg/gui/containers_panel.go | 53 +++++++++++++++++++++++++- pkg/gui/custom_commands.go | 18 +++++++-- pkg/gui/gui.go | 4 ++ pkg/gui/images_panel.go | 34 ++++++++++++----- pkg/gui/keybindings.go | 47 ++++++++++++++++------- pkg/gui/services_panel.go | 11 +++++- pkg/gui/volumes_panel.go | 26 ++++++++++--- pkg/i18n/english.go | 76 ++++++++++++++++++++++--------------- 10 files changed, 265 insertions(+), 70 deletions(-) diff --git a/pkg/commands/container.go b/pkg/commands/container.go index 137fc25c..c7faa01e 100644 --- a/pkg/commands/container.go +++ b/pkg/commands/container.go @@ -2,12 +2,14 @@ package commands import ( "context" - "github.com/docker/docker/api/types/container" + "fmt" "os/exec" "strconv" "strings" "time" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" @@ -317,6 +319,7 @@ func (c *Container) GetColor() color.Attribute { // Remove removes the container func (c *Container) Remove(options types.ContainerRemoveOptions) error { + c.Log.Warn(fmt.Sprintf("removing container %s", c.Name)) if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil { if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") { return ComplexError{ @@ -333,16 +336,20 @@ func (c *Container) Remove(options types.ContainerRemoveOptions) error { // Stop stops the container func (c *Container) Stop() error { + c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name)) return c.Client.ContainerStop(context.Background(), c.ID, nil) } // Restart restarts the container func (c *Container) Restart() error { + c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name)) return c.Client.ContainerRestart(context.Background(), c.ID, nil) } // Attach attaches the container func (c *Container) Attach() (*exec.Cmd, error) { + c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name)) + // verify that we can in fact attach to this container if !c.Details.Config.OpenStdin { return nil, errors.New(c.Tr.UnattachableContainerError) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 79ed97d0..a0bbcb70 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -50,6 +50,10 @@ type UserConfig struct { // those are found in the commands package CustomCommands CustomCommands `yaml:"customCommands,omitempty"` + // BulkCommands are commands that apply to all items in a panel e.g. + // killing all containers, stopping all services, or pruning all images + BulkCommands CustomCommands `yaml:"bulkCommands,omitempty"` + // OS determines what defaults are set for opening files and links OS OSConfig `yaml:"oS,omitempty"` @@ -259,10 +263,10 @@ type CustomCommands struct { // Services contains the custom commands for services Services []CustomCommand `yaml:"services,omitempty"` - // Services contains the custom commands for services + // Images contains the custom commands for images Images []CustomCommand `yaml:"images,omitempty"` - // Services contains the custom commands for services + // Volumes contains the custom commands for volumes Volumes []CustomCommand `yaml:"volumes,omitempty"` } @@ -288,6 +292,9 @@ type CustomCommand struct { // field has no effect on customcommands under the 'communications' part of // the customCommand config. ServiceNames []string `yaml:"serviceNames"` + + // InternalFunction is the name of a function inside lazydocker that we want to run, as opposed to a command-line command. This is only used internally and can't be configured by the user + InternalFunction func() error `yaml:"internalFunction"` } // GetDefaultConfig returns the application default configuration NOTE (to @@ -344,6 +351,52 @@ func GetDefaultConfig() UserConfig { Images: []CustomCommand{}, Volumes: []CustomCommand{}, }, + BulkCommands: CustomCommands{ + Services: []CustomCommand{ + { + Name: "up", + Command: "{{ .DockerCompose }} up -d", + }, + { + Name: "up (attached)", + Command: "{{ .DockerCompose }} up", + Attach: true, + }, + { + Name: "stop", + Command: "{{ .DockerCompose }} stop", + }, + { + Name: "pull", + Command: "{{ .DockerCompose }} pull", + Attach: true, + }, + { + Name: "build", + Command: "{{ .DockerCompose }} build --parallel --force-rm", + Attach: true, + }, + { + Name: "down", + Command: "{{ .DockerCompose }} down", + }, + { + Name: "down with volumes", + Command: "{{ .DockerCompose }} down --volumes", + }, + { + Name: "down with images", + Command: "{{ .DockerCompose }} down --rmi all", + }, + { + Name: "down with volumes and images", + Command: "{{ .DockerCompose }} down --volumes --rmi all", + }, + }, + Containers: []CustomCommand{}, + Images: []CustomCommand{}, + Volumes: []CustomCommand{}, + }, OS: GetPlatformDefaultConfig(), Update: UpdateConfig{ Method: "never", diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index d1c61f5e..65a74e4a 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -11,6 +11,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazydocker/pkg/commands" + "github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/utils" ) @@ -476,8 +477,8 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error { return gui.Errors.ErrSubProcess } -func (gui *Gui) handlePruneContainers(g *gocui.Gui, v *gocui.View) error { - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handlePruneContainers() error { + return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneContainers() if err != nil { @@ -517,3 +518,51 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error return gui.createCustomCommandMenu(customCommands, commandObject) } + +func (gui *Gui) handleStopContainers() error { + return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error { + return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error { + + for _, container := range gui.DockerCommand.Containers { + _ = container.Stop() + } + + return nil + }) + }, nil) +} + +func (gui *Gui) handleRemoveContainers() error { + return gui.createConfirmationPanel(gui.g, gui.getContainersView(), gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error { + return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error { + + for _, container := range gui.DockerCommand.Containers { + _ = container.Remove(types.ContainerRemoveOptions{Force: true}) + } + + return nil + }) + }, nil) +} + +func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error { + baseBulkCommands := []config.CustomCommand{ + { + Name: gui.Tr.StopAllContainers, + InternalFunction: gui.handleStopContainers, + }, + { + Name: gui.Tr.RemoveAllContainers, + InternalFunction: gui.handleRemoveContainers, + }, + { + Name: gui.Tr.PruneContainers, + InternalFunction: gui.handlePruneContainers, + }, + } + + bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Containers...) + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{}) + + return gui.createBulkCommandMenu(bulkCommands, commandObject) +} diff --git a/pkg/gui/custom_commands.go b/pkg/gui/custom_commands.go index 7d858d2e..1a23b719 100644 --- a/pkg/gui/custom_commands.go +++ b/pkg/gui/custom_commands.go @@ -21,7 +21,7 @@ func (r *customCommandOption) GetDisplayStrings(isFocused bool) []string { return []string{r.name, utils.ColoredString(r.description, color.FgCyan)} } -func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject) error { +func (gui *Gui) createCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject, title string, waitingStatus string) error { options := make([]*customCommandOption, len(customCommands)+1) for i, command := range customCommands { resolvedCommand := utils.ApplyTemplate(command.Command, commandObject) @@ -46,6 +46,10 @@ func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, c return nil } + if option.customCommand.InternalFunction != nil { + return option.customCommand.InternalFunction() + } + // if we have a command for attaching, we attach and return the subprocess error if option.customCommand.Attach { cmd := gui.OSCommand.ExecutableFromString(option.command) @@ -53,7 +57,7 @@ func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, c return gui.Errors.ErrSubProcess } - return gui.WithWaitingStatus(gui.Tr.RunningCustomCommandStatus, func() error { + return gui.WithWaitingStatus(waitingStatus, func() error { if err := gui.OSCommand.RunCommand(option.command); err != nil { return gui.createErrorPanel(gui.g, err.Error()) } @@ -61,5 +65,13 @@ func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, c }) } - return gui.createMenu("", options, len(options), handleMenuPress) + return gui.createMenu(title, options, len(options), handleMenuPress) +} + +func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject) error { + return gui.createCommandMenu(customCommands, commandObject, gui.Tr.CustomCommandTitle, gui.Tr.RunningCustomCommandStatus) +} + +func (gui *Gui) createBulkCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject) error { + return gui.createCommandMenu(customCommands, commandObject, gui.Tr.BulkCommandTitle, gui.Tr.RunningBulkCommandStatus) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 8ef53626..ae63141d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -225,6 +225,7 @@ func (gui *Gui) renderGlobalOptions() error { "PgUp/PgDn": gui.Tr.Scroll, "← → ↑ ↓": gui.Tr.Navigate, "esc/q": gui.Tr.Close, + "b": gui.Tr.ViewBulkCommands, "x": gui.Tr.Menu, }) } @@ -282,6 +283,9 @@ func (gui *Gui) Run() error { go func() { for err := range gui.ErrorChan { + if err == nil { + continue + } if strings.Contains(err.Error(), "No such container") { // this happens all the time when e.g. restarting containers so we won't worry about it gui.Log.Warn(err) diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go index 1e2c2b74..878ce1ac 100644 --- a/pkg/gui/images_panel.go +++ b/pkg/gui/images_panel.go @@ -10,6 +10,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazydocker/pkg/commands" + "github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/utils" ) @@ -23,7 +24,7 @@ func (gui *Gui) getImageContextTitles() []string { return []string{gui.Tr.ConfigTitle} } -func (gui *Gui) getSelectedImage(g *gocui.Gui) (*commands.Image, error) { +func (gui *Gui) getSelectedImage() (*commands.Image, error) { selectedLine := gui.State.Panels.Images.SelectedLine if selectedLine == -1 { return &commands.Image{}, gui.Errors.ErrNoImages @@ -41,7 +42,7 @@ func (gui *Gui) handleImagesClick(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleImageSelect(g *gocui.Gui, v *gocui.View) error { - Image, err := gui.getSelectedImage(g) + Image, err := gui.getSelectedImage() if err != nil { if err != gui.Errors.ErrNoImages { return err @@ -207,7 +208,7 @@ func (r *removeImageOption) GetDisplayStrings(isFocused bool) []string { } func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error { - Image, err := gui.getSelectedImage(g) + Image, err := gui.getSelectedImage() if err != nil { return nil } @@ -248,8 +249,8 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error { return gui.createMenu("", options, len(options), handleMenuPress) } -func (gui *Gui) handlePruneImages(g *gocui.Gui, v *gocui.View) error { - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handlePruneImages() error { + return gui.createConfirmationPanel(gui.g, gui.getImagesView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneImages() if err != nil { @@ -261,17 +262,30 @@ func (gui *Gui) handlePruneImages(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error { - image, err := gui.getSelectedImage(g) + image, err := gui.getSelectedImage() if err != nil { return nil } - commandObject := gui.DockerCommand.NewCommandObject( - commands.CommandObject{ - Image: image, - }) + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{ + Image: image, + }) customCommands := gui.Config.UserConfig.CustomCommands.Images return gui.createCustomCommandMenu(customCommands, commandObject) } + +func (gui *Gui) handleImagesBulkCommand(g *gocui.Gui, v *gocui.View) error { + baseBulkCommands := []config.CustomCommand{ + { + Name: gui.Tr.PruneImages, + InternalFunction: gui.handlePruneImages, + }, + } + + bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Images...) + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{}) + + return gui.createBulkCommandMenu(bulkCommands, commandObject) +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 898a9ba9..24fe83d8 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -239,13 +239,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleContainerAttach, Description: gui.Tr.Attach, }, - { - ViewName: "containers", - Key: 'D', - Modifier: gocui.ModNone, - Handler: gui.handlePruneContainers, - Description: gui.Tr.PruneContainers, - }, { ViewName: "containers", Key: 'm', @@ -260,6 +253,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleContainersCustomCommand, Description: gui.Tr.RunCustomCommand, }, + { + ViewName: "containers", + Key: 'b', + Modifier: gocui.ModNone, + Handler: gui.handleContainersBulkCommand, + Description: gui.Tr.ViewBulkCommands, + }, { ViewName: "services", Key: 'd', @@ -323,6 +323,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Handler: gui.handleServicesCustomCommand, Description: gui.Tr.RunCustomCommand, }, + { + ViewName: "services", + Key: 'b', + Modifier: gocui.ModNone, + Handler: gui.handleServicesBulkCommand, + Description: gui.Tr.ViewBulkCommands, + }, { ViewName: "images", Key: '[', @@ -353,10 +360,17 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { }, { ViewName: "images", - Key: 'D', + Key: 'b', Modifier: gocui.ModNone, - Handler: gui.handlePruneImages, - Description: gui.Tr.PruneImages, + Handler: gui.handleImagesBulkCommand, + Description: gui.Tr.ViewBulkCommands, + }, + { + ViewName: "images", + Key: 'c', + Modifier: gocui.ModNone, + Handler: gui.handleImagesCustomCommand, + Description: gui.Tr.RunCustomCommand, }, { ViewName: "volumes", @@ -388,10 +402,17 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { }, { ViewName: "volumes", - Key: 'D', + Key: 'b', Modifier: gocui.ModNone, - Handler: gui.handlePruneVolumes, - Description: gui.Tr.PruneVolumes, + Handler: gui.handleVolumesBulkCommand, + Description: gui.Tr.ViewBulkCommands, + }, + { + ViewName: "volumes", + Key: 'c', + Modifier: gocui.ModNone, + Handler: gui.handleVolumesCustomCommand, + Description: gui.Tr.RunCustomCommand, }, { ViewName: "main", diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go index 0c1e7a43..460a3a07 100644 --- a/pkg/gui/services_panel.go +++ b/pkg/gui/services_panel.go @@ -210,7 +210,7 @@ func (gui *Gui) handleServiceRemoveMenu(g *gocui.Gui, v *gocui.View) error { }, } - return gui.createCommandMenu(options, gui.Tr.RemovingStatus) + return gui.createServiceCommandMenu(options, gui.Tr.RemovingStatus) } func (gui *Gui) handleServiceStop(g *gocui.Gui, v *gocui.View) error { @@ -349,7 +349,7 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error { return gui.createMenu("", options, len(options), handleMenuPress) } -func (gui *Gui) createCommandMenu(options []*commandOption, status string) error { +func (gui *Gui) createServiceCommandMenu(options []*commandOption, status string) error { handleMenuPress := func(index int) error { if options[index].command == "" { return nil @@ -402,3 +402,10 @@ L: return gui.createCustomCommandMenu(customCommands, commandObject) } + +func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error { + bulkCommands := gui.Config.UserConfig.BulkCommands.Services + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{}) + + return gui.createBulkCommandMenu(bulkCommands, commandObject) +} diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go index 1c2a4878..721c5169 100644 --- a/pkg/gui/volumes_panel.go +++ b/pkg/gui/volumes_panel.go @@ -7,6 +7,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazydocker/pkg/commands" + "github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/utils" ) @@ -239,8 +240,8 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error { return gui.createMenu("", options, len(options), handleMenuPress) } -func (gui *Gui) handlePruneVolumes(g *gocui.Gui, v *gocui.View) error { - return gui.createConfirmationPanel(gui.g, v, gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handlePruneVolumes() error { + return gui.createConfirmationPanel(gui.g, gui.getVolumesView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error { err := gui.DockerCommand.PruneVolumes() if err != nil { @@ -257,12 +258,25 @@ func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error { return nil } - commandObject := gui.DockerCommand.NewCommandObject( - commands.CommandObject{ - Volume: volume, - }) + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{ + Volume: volume, + }) customCommands := gui.Config.UserConfig.CustomCommands.Volumes return gui.createCustomCommandMenu(customCommands, commandObject) } + +func (gui *Gui) handleVolumesBulkCommand(g *gocui.Gui, v *gocui.View) error { + baseBulkCommands := []config.CustomCommand{ + { + Name: gui.Tr.PruneVolumes, + InternalFunction: gui.handlePruneVolumes, + }, + } + + bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Volumes...) + commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{}) + + return gui.createBulkCommandMenu(bulkCommands, commandObject) +} diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 3c2fc413..87d86336 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -28,6 +28,7 @@ type TranslationSet struct { Donate string Cancel string CustomCommandTitle string + BulkCommandTitle string Remove string HideStopped string ForceRemove string @@ -41,6 +42,7 @@ type TranslationSet struct { StoppingStatus string RemovingStatus string RunningCustomCommandStatus string + RunningBulkCommandStatus string RemoveService string Stop string Restart string @@ -67,13 +69,18 @@ type TranslationSet struct { PruneContainers string PruneVolumes string ConfirmPruneContainers string + ConfirmStopContainers string + ConfirmRemoveContainers string ConfirmPruneImages string ConfirmPruneVolumes string PruningStatus string StopService string PressEnterToReturn string + StopAllContainers string + RemoveAllContainers string ViewRestartOptions string RunCustomCommand string + ViewBulkCommands string LogsTitle string ConfigTitle string @@ -93,6 +100,7 @@ func englishSet() TranslationSet { RestartingStatus: "restarting", StoppingStatus: "stopping", RunningCustomCommandStatus: "running custom command", + RunningBulkCommandStatus: "running bulk command", RunningSubprocess: "running subprocess", NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement", @@ -106,37 +114,40 @@ func englishSet() TranslationSet { Donate: "Donate", Confirm: "Confirm", - Return: "return", - FocusMain: "focus main panel", - Navigate: "navigate", - Execute: "execute", - Close: "close", - Menu: "menu", - Scroll: "scroll", - OpenConfig: "open lazydocker config", - EditConfig: "edit lazydocker config", - Cancel: "cancel", - Remove: "remove", - HideStopped: "Hide/Show stopped containers", - ForceRemove: "force remove", - RemoveWithVolumes: "remove with volumes", - RemoveService: "remove containers", - Stop: "stop", - Restart: "restart", - Rebuild: "rebuild", - Recreate: "recreate", - PreviousContext: "previous tab", - NextContext: "next tab", - Attach: "attach", - ViewLogs: "view logs", - RemoveImage: "remove image", - RemoveVolume: "remove volume", - RemoveWithoutPrune: "remove without deleting untagged parents", - PruneContainers: "prune exited containers", - PruneVolumes: "prune unused volumes", - PruneImages: "prune unused images", - ViewRestartOptions: "view restart options", - RunCustomCommand: "run predefined custom command", + Return: "return", + FocusMain: "focus main panel", + Navigate: "navigate", + Execute: "execute", + Close: "close", + Menu: "menu", + Scroll: "scroll", + OpenConfig: "open lazydocker config", + EditConfig: "edit lazydocker config", + Cancel: "cancel", + Remove: "remove", + HideStopped: "Hide/Show stopped containers", + ForceRemove: "force remove", + RemoveWithVolumes: "remove with volumes", + RemoveService: "remove containers", + Stop: "stop", + Restart: "restart", + Rebuild: "rebuild", + Recreate: "recreate", + PreviousContext: "previous tab", + NextContext: "next tab", + Attach: "attach", + ViewLogs: "view logs", + RemoveImage: "remove image", + RemoveVolume: "remove volume", + RemoveWithoutPrune: "remove without deleting untagged parents", + PruneContainers: "prune exited containers", + PruneVolumes: "prune unused volumes", + PruneImages: "prune unused images", + StopAllContainers: "stop all containers", + RemoveAllContainers: "remove all containers (forced)", + ViewRestartOptions: "view restart options", + RunCustomCommand: "run predefined custom command", + ViewBulkCommands: "view bulk commands", AnonymousReportingTitle: "Help make lazydocker better", AnonymousReportingPrompt: "Would you like to enable anonymous reporting data to help improve lazydocker?", @@ -150,6 +161,7 @@ func englishSet() TranslationSet { ImagesTitle: "Images", VolumesTitle: "Volumes", CustomCommandTitle: "Custom Command:", + BulkCommandTitle: "Bulk Command:", ErrorTitle: "Error", LogsTitle: "Logs", ConfigTitle: "Config", @@ -169,6 +181,8 @@ func englishSet() TranslationSet { NotEnoughSpace: "Not enough space to render panels", ConfirmPruneImages: "Are you sure you want to prune all unused images?", ConfirmPruneContainers: "Are you sure you want to prune all stopped containers?", + ConfirmStopContainers: "Are you sure you want to stop all containers?", + ConfirmRemoveContainers: "Are you sure you want to remove all containers?", ConfirmPruneVolumes: "Are you sure you want to prune all unused volumes?", StopService: "Are you sure you want to stop this service's containers?", StopContainer: "Are you sure you want to stop this container?",