Merge pull request #107 from jesseduffield/bulk-commands

Support bulk commands in each of the panels
This commit is contained in:
Jesse Duffield 2019-07-29 19:01:20 +10:00 committed by GitHub
commit 4dc8f76083
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 272 additions and 74 deletions

View file

@ -6,8 +6,6 @@ A simple terminal UI for both docker and docker-compose, written in Go with the
[![CircleCI](https://circleci.com/gh/jesseduffield/lazydocker.svg?style=svg)](https://circleci.com/gh/jesseduffield/lazydocker) [![Go Report Card](https://goreportcard.com/badge/github.com/jesseduffield/lazydocker)](https://goreportcard.com/report/github.com/jesseduffield/lazydocker) [![GolangCI](https://golangci.com/badges/github.com/jesseduffield/lazydocker.svg)](https://golangci.com) [![GoDoc](https://godoc.org/github.com/jesseduffield/lazydocker?status.svg)](http://godoc.org/github.com/jesseduffield/lazydocker) [![GitHub tag](https://img.shields.io/github/tag/jesseduffield/lazydocker.svg)]()
![Gif](/docs/resources/demo3.gif)
[Demo](https://youtu.be/NICqQPxwJWw)
@ -31,7 +29,7 @@ Memorising docker commands is hard. Memorising aliases is slightly less hard. Ke
## Requirements
- Docker >= **1.8** (API >= **1.20**)
- Docker >= **1.8** (API >= **1.25**)
- Docker-Compose >= **1.23.2** (optional)
## Installation
@ -197,18 +195,23 @@ If you want to see what I (Jesse) am up to in terms of development, follow me on
## FAQ
### How do I edit my config?
By opening lazydocker, clicking on the 'project' panel in the top left, and pressing 'o' (or 'e' if your editor is vim). See [Config Docs](/docs/Config.md)
### How do I get text to wrap in my main panel?
In the future I want to make this the default, but for now there are some CPU issues that arise with wrapping. If you want to enable wrapping, use `gui.wrapMainPanel: true`
### How do you select text?
Because we support mouse events, you will need to hold option while dragging the mouse to indicate you're trying to select text rather than click on something. Alternatively you can disable mouse events via the `gui.ignoreMouseEvents` config value
### Does this work with Windows?
Currently not unless you use WSL. Instructions for setting up docker for WSL can be found here [here](https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly)
### Why can't I see my container's logs?
By default we only show logs from the last hour, so that we're not putting too much strain on the machine. This may be why you can't see logs when you first start lazydocker. This can be overwritten in the config's `commandTemplates`
## Alternatives

View file

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

View file

@ -22,7 +22,7 @@ import (
)
const (
APIVersion = "1.20"
APIVersion = "1.25"
)
// DockerCommand is our main docker interface

View file

@ -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"`
@ -260,10 +264,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"`
}
@ -289,6 +293,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
@ -345,6 +352,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{
DockerRefreshInterval: time.Millisecond * 100,

View file

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

View file

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

View file

@ -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,
})
}
@ -283,6 +284,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)

View file

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

View file

@ -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",

View file

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

View file

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

View file

@ -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?",