Aleksandr 8 months ago committed by GitHub
commit 6ded4d57c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,7 @@ package app
import (
"io"
"path"
"strings"
"github.com/jesseduffield/lazydocker/pkg/commands"
@ -49,6 +50,15 @@ func NewApp(config *config.AppConfig) (*App, error) {
}
app.closers = append(app.closers, app.DockerCommand)
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
currentDir := path.Base(app.Config.ProjectDir)
for _, project := range app.DockerCommand.GetAllComposeProjects() {
if project.WorkingDir == currentDir {
app.DockerCommand.SelectedComposeProject = &project
break
}
}
if err != nil {
return app, err
}

@ -37,7 +37,7 @@ type DockerCommand struct {
Tr *i18n.TranslationSet
Config *config.AppConfig
Client *client.Client
InDockerComposeProject bool
SelectedComposeProject *ComposeProject
ErrorChan chan error
ContainerMutex deadlock.Mutex
ServiceMutex deadlock.Mutex
@ -62,6 +62,11 @@ type CommandObject struct {
Network *Network
}
type ComposeProject struct {
Name string
WorkingDir string
}
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
@ -87,14 +92,13 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
}
dockerCommand := &DockerCommand{
Log: log,
OSCommand: osCommand,
Tr: tr,
Config: config,
Client: cli,
ErrorChan: errorChan,
InDockerComposeProject: true,
Closers: []io.Closer{tunnelCloser},
Log: log,
OSCommand: osCommand,
Tr: tr,
Config: config,
Client: cli,
ErrorChan: errorChan,
Closers: []io.Closer{tunnelCloser},
}
command := utils.ApplyTemplate(
@ -104,16 +108,13 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
log.Warn(command)
err = osCommand.RunCommand(
utils.ApplyTemplate(
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
dockerCommand.NewCommandObject(CommandObject{}),
),
)
if err != nil {
dockerCommand.InDockerComposeProject = false
log.Warn(err.Error())
}
//dockerCommand.GetAllComposeProjects()
//err = osCommand.RunCommand(
// utils.ApplyTemplate(
// config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
// dockerCommand.NewCommandObject(CommandObject{}),
// ),
//)
return dockerCommand, nil
}
@ -122,6 +123,25 @@ func (c *DockerCommand) Close() error {
return utils.CloseMany(c.Closers)
}
func (c *DockerCommand) GetAllComposeProjects() map[string]ComposeProject {
containers, _ := c.Client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{
All: true,
})
projects := make(map[string]ComposeProject)
for _, c := range containers {
projectName, ok := c.Labels["com.docker.compose.project"]
if ok {
workingDir, ok := c.Labels["com.docker.compose.project.working_dir"]
//configFiles, ok := c.Labels["com.docker.compose.project.config_files"]
if ok {
projects[projectName] = ComposeProject{Name: projectName, WorkingDir: workingDir}
}
}
}
return projects
}
func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
container.MonitoringStats = true
stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
@ -249,12 +269,12 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
// GetServices gets services
func (c *DockerCommand) GetServices() ([]*Service, error) {
if !c.InDockerComposeProject {
if c.SelectedComposeProject == nil {
return nil, nil
}
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
output, err := c.OSCommand.RunCommandWithOutputFrom(fmt.Sprintf("%s config --services", composeCommand), c.SelectedComposeProject.WorkingDir)
if err != nil {
return nil, err
}
@ -312,11 +332,12 @@ func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
// DockerComposeConfig returns the result of 'docker-compose config'
func (c *DockerCommand) DockerComposeConfig() string {
output, err := c.OSCommand.RunCommandWithOutput(
output, err := c.OSCommand.RunCommandWithOutputFrom(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
c.NewCommandObject(CommandObject{}),
),
c.SelectedComposeProject.WorkingDir,
)
if err != nil {
output = err.Error()

@ -55,15 +55,21 @@ func (c *OSCommand) SetCommand(cmd func(string, ...string) *exec.Cmd) {
c.command = cmd
}
// RunCommandWithOutput wrapper around commands returning their output and error
func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
// RunCommandWithOutputFrom wrapper around commands returning their output and error
func (c *OSCommand) RunCommandWithOutputFrom(command string, workingDir string) (string, error) {
cmd := c.ExecutableFromString(command)
before := time.Now()
cmd.Dir = workingDir
output, err := sanitisedCommandOutput(cmd.Output())
c.Log.Warn(fmt.Sprintf("'%s': %s", command, time.Since(before)))
return output, err
}
// RunCommandWithOutput wrapper around commands returning their output and error
func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
return c.RunCommandWithOutputFrom(command, "")
}
// RunCommandWithOutput wrapper around commands returning their output and error
func (c *OSCommand) RunCommandWithOutputContext(ctx context.Context, command string) (string, error) {
cmd := c.ExecutableFromStringContext(ctx, command)

@ -1,5 +1,5 @@
package commands
type Project struct {
Name string
Compose ComposeProject
}

@ -19,17 +19,20 @@ import (
"github.com/samber/lo"
)
var isStandaloneContainer = func(container *commands.Container) bool {
if container.OneOff || container.ServiceName == "" {
return true
}
project, _ := container.Container.Labels["com.docker.compose.project"]
return project == ""
//return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
// return service.Name == container.ServiceName
//})
}
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
// Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
isStandaloneContainer := func(container *commands.Container) bool {
if container.OneOff || container.ServiceName == "" {
return true
}
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
return service.Name == container.ServiceName
})
}
return &panels.SideListPanel[*commands.Container]{
ContextState: &panels.ContextState[*commands.Container]{
@ -255,6 +258,12 @@ func (gui *Gui) refreshContainersAndServices() error {
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
services, err := gui.DockerCommand.GetServices()
if err != nil {
return err
}
gui.Panels.Services.List.SetItems(services)
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
gui.Panels.Services.List.GetAllItems(),
gui.Panels.Containers.List.GetAllItems(),
@ -283,7 +292,7 @@ func (gui *Gui) refreshContainersAndServices() error {
}
func (gui *Gui) renderContainersAndServices() error {
if gui.DockerCommand.InDockerComposeProject {
if gui.DockerCommand.SelectedComposeProject != nil {
if err := gui.Panels.Services.RerenderList(); err != nil {
return err
}

@ -461,7 +461,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
}
func (gui *Gui) initiallyFocusedViewName() string {
if gui.DockerCommand.InDockerComposeProject {
if gui.DockerCommand.SelectedComposeProject != nil {
return "services"
}
return "containers"

@ -129,21 +129,27 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleCustomCommand,
},
{
ViewName: "project",
ViewName: "projects",
Key: 'e',
Modifier: gocui.ModNone,
Handler: gui.handleEditConfig,
Description: gui.Tr.EditConfig,
},
{
ViewName: "project",
ViewName: "projects",
Key: 'o',
Modifier: gocui.ModNone,
Handler: gui.handleOpenConfig,
Description: gui.Tr.OpenConfig,
},
{
ViewName: "project",
ViewName: "projects",
Key: gocui.KeySpace,
Modifier: gocui.ModNone,
Handler: gui.handleComposeProjectSelect,
},
{
ViewName: "projects",
Key: 'm',
Modifier: gocui.ModNone,
Handler: gui.handleViewAllLogs,

@ -3,5 +3,6 @@ package presentation
import "github.com/jesseduffield/lazydocker/pkg/commands"
func GetProjectDisplayStrings(project *commands.Project) []string {
return []string{project.Name}
// TODO show status up down stop
return []string{project.Compose.Name}
}

@ -3,7 +3,7 @@ package gui
import (
"bytes"
"context"
"path"
"github.com/samber/lo"
"strings"
"github.com/fatih/color"
@ -23,7 +23,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
return &panels.SideListPanel[*commands.Project]{
ContextState: &panels.ContextState[*commands.Project]{
GetMainTabs: func() []panels.MainTab[*commands.Project] {
if gui.DockerCommand.InDockerComposeProject {
if gui.DockerCommand.SelectedComposeProject != nil {
return []panels.MainTab[*commands.Project]{
{
Key: "logs",
@ -52,43 +52,39 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
}
},
GetItemContextCacheKey: func(project *commands.Project) string {
return "projects-" + project.Name
return "projects-" + project.Compose.Name // TODO status
},
},
ListPanel: panels.ListPanel[*commands.Project]{
List: panels.NewFilteredList[*commands.Project](),
View: gui.Views.Project,
View: gui.Views.Projects,
},
NoItemsMessage: "",
Gui: gui.intoInterface(),
Sort: func(a *commands.Project, b *commands.Project) bool {
return false
return a.Compose.Name < b.Compose.Name
},
GetTableCells: presentation.GetProjectDisplayStrings,
// It doesn't make sense to filter a list of only one item.
DisableFilter: true,
//DisableFilter: true,
}
}
func (gui *Gui) refreshProject() error {
gui.Panels.Projects.SetItems([]*commands.Project{{Name: gui.getProjectName()}})
c := gui.getProjects()
list := make([]*commands.Project, len(c))
for i, s := range lo.Values(c) {
list[i] = &commands.Project{Compose: s}
}
gui.Panels.Projects.SetItems(list)
return gui.Panels.Projects.RerenderList()
}
func (gui *Gui) getProjectName() string {
projectName := path.Base(gui.Config.ProjectDir)
if gui.DockerCommand.InDockerComposeProject {
for _, service := range gui.Panels.Services.List.GetAllItems() {
container := service.Container
if container != nil && container.DetailsLoaded() {
return container.Details.Config.Labels["com.docker.compose.project"]
}
}
}
return projectName
func (gui *Gui) getProjects() map[string]commands.ComposeProject {
return gui.DockerCommand.GetAllComposeProjects()
}
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
@ -125,6 +121,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
),
)
cmd.Dir = _project.Compose.WorkingDir
cmd.Stdout = gui.Views.Main
cmd.Stderr = gui.Views.Main
@ -158,6 +155,20 @@ func (gui *Gui) handleEditConfig(g *gocui.Gui, v *gocui.View) error {
return gui.editFile(gui.Config.ConfigFilename())
}
func (gui *Gui) handleComposeProjectSelect(g *gocui.Gui, v *gocui.View) error {
project, err := gui.Panels.Projects.GetSelectedItem()
if err != nil {
return nil
}
//if gui.DockerCommand.SelectedComposeProject != &project.Compose {
gui.DockerCommand.SelectedComposeProject = &project.Compose
// gui.renderServiceLogs()
return gui.refreshContainersAndServices()
//}
//return nil
}
func lazydockerTitle() string {
return `
_ _ _

@ -77,9 +77,9 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
GetTableCells: func(service *commands.Service) []string {
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
},
Hide: func() bool {
return !gui.DockerCommand.InDockerComposeProject
},
//Hide: func() bool {
// return gui.DockerCommand.SelectedComposeProject == nil
//},
}
}

@ -27,7 +27,7 @@ func hideUnderScores() bool {
type Views struct {
// side panels
Project *gocui.View
Projects *gocui.View
Services *gocui.View
Containers *gocui.View
Images *gocui.View
@ -66,7 +66,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
return []viewNameMapping{
// first layer. Ordering within this layer does not matter because there are
// no overlapping views
{viewPtr: &gui.Views.Project, name: "project", autoPosition: true},
{viewPtr: &gui.Views.Projects, name: "projects", autoPosition: true},
{viewPtr: &gui.Views.Services, name: "services", autoPosition: true},
{viewPtr: &gui.Views.Containers, name: "containers", autoPosition: true},
{viewPtr: &gui.Views.Images, name: "images", autoPosition: true},
@ -107,7 +107,9 @@ func (gui *Gui) createAllViews() error {
// when you run a docker container with the -it flags (interactive mode) it adds carriage returns for some reason. This is not docker's fault, it's an os-level default.
gui.Views.Main.IgnoreCarriageReturns = true
gui.Views.Project.Title = gui.Tr.ProjectTitle
gui.Views.Projects.Highlight = true
gui.Views.Projects.Title = gui.Tr.ProjectTitle
gui.Views.Projects.SelBgColor = selectedLineBgColor
gui.Views.Services.Highlight = true
gui.Views.Services.Title = gui.Tr.ServicesTitle
@ -115,7 +117,7 @@ func (gui *Gui) createAllViews() error {
gui.Views.Containers.Highlight = true
gui.Views.Containers.SelBgColor = selectedLineBgColor
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
if gui.Config.UserConfig.Gui.ShowAllContainers || gui.DockerCommand.SelectedComposeProject == nil {
gui.Views.Containers.Title = gui.Tr.ContainersTitle
} else {
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle

@ -56,7 +56,7 @@ func dutchSet() TranslationSet {
GlobalTitle: "Globaal",
MainTitle: "Hoofd",
ProjectTitle: "Project",
ProjectTitle: "Projects",
ServicesTitle: "Diensten",
ContainersTitle: "Containers",
StandaloneContainersTitle: "Alleenstaande Containers",

@ -212,7 +212,7 @@ func englishSet() TranslationSet {
GlobalTitle: "Global",
MainTitle: "Main",
ProjectTitle: "Project",
ProjectTitle: "Projects",
ServicesTitle: "Services",
ContainersTitle: "Containers",
StandaloneContainersTitle: "Standalone Containers",

Loading…
Cancel
Save