more progress

pull/1/head
Jesse Duffield 5 years ago
parent f0ffdcd81f
commit d3ea878fc4

@ -5,11 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/fatih/color"
@ -17,8 +13,6 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/jesseduffield/pty"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/xerrors"
)
@ -74,12 +68,6 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedContainers"))
}
// if we are in an attached state, we won't do anything here
// TODO: generalise
if strings.HasPrefix(gui.State.Panels.Main.ObjectKey, "attached-") {
return nil
}
key := container.ID + "-" + gui.getContainerContexts()[gui.State.Panels.Containers.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
return nil
@ -189,17 +177,32 @@ func (gui *Gui) renderContainerLogs(mainView *gocui.View, container *commands.Co
var cmd *exec.Cmd
cmd = gui.OSCommand.RunCustomCommand("docker logs --since=60m --timestamps --follow " + container.ID)
cmd.Stdout = mainView
cmd.Start()
r, err := cmd.StdoutPipe()
if err != nil {
return err
}
go func() {
for {
time.Sleep(time.Second / 100)
if gui.State.Panels.Main.WriterID != writerID {
cmd.Process.Kill()
return
gui.State.MainProcessChan <- struct{}{}
gui.State.MainProcessMutex.Lock()
cmd.Start()
go func() {
<-gui.State.MainProcessChan
cmd.Process.Kill()
}()
go func() {
for {
s := bufio.NewScanner(r)
s.Split(bufio.ScanLines)
for s.Scan() {
mainView.Write(append(s.Bytes(), '\n'))
}
}
}
}()
cmd.Wait()
gui.State.MainProcessMutex.Unlock()
}()
return nil
@ -404,55 +407,10 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
return nil
}
gui.State.Panels.Main.WriterID++
// c := container.Attach()
// // Create arbitrary command.
c := exec.Command("bash")
// Start the command with a pty.
ptmx, err := pty.Start(c)
if err != nil {
return err
}
go func() {
// Set stdin in raw mode.
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
panic(err)
}
defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
defer func() { _ = ptmx.Close() }() // Best effort.
go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
mainView := gui.getMainView()
mainView.Clear()
mainView.Autoscroll = true
gui.State.Panels.Main.ObjectKey = "attached-" + container.ID
scanner := bufio.NewScanner(ptmx)
scanner.Split(bufio.ScanBytes)
c := container.Attach()
content := ""
for scanner.Scan() {
content += scanner.Text()
gui.Log.Warn("content")
gui.Log.Warn(content)
gui.renderString(gui.g, "main", content)
}
// reset object key
gui.State.Panels.Main.ObjectKey = ""
gui.State.Panels.Main.WriterID++
gui.handleContainerSelect(gui.g, v)
}()
return nil
gui.SubProcess = c
return gui.Errors.ErrSubProcess
}
func (gui *Gui) handleServiceRestart(g *gocui.Gui, v *gocui.View) error {

@ -1,9 +1,9 @@
package gui
import (
"bytes"
"io"
"fmt"
"math"
"strings"
"sync"
// "io"
@ -14,6 +14,7 @@ import (
"os/exec"
"time"
"github.com/fatih/color"
"github.com/go-errors/errors"
// "strings"
@ -110,6 +111,8 @@ type guiState struct {
Updating bool
Panels *panelStates
SubProcessOutput string
MainProcessMutex sync.Mutex
MainProcessChan chan struct{}
}
// NewGui builds a new gui handler
@ -128,8 +131,17 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
ObjectKey: "",
},
},
MainProcessChan: make(chan struct{}),
}
go func() {
// setting up a goroutine for listening to the first stop signal on this channel
// because whenever something wants to lock the mutex, it tells the existing process to stop
// but on startup we don't have a process so we just mock it
// this is because we're using an unbuffered channel
<-initialState.MainProcessChan
}()
gui := &Gui{
Log: log,
DockerCommand: dockerCommand,
@ -392,16 +404,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
}
if gui.State.SubProcessOutput != "" {
output := gui.State.SubProcessOutput
gui.State.SubProcessOutput = ""
x, y := gui.g.Size()
// if we just came back from vim, we don't want vim's output to show up in our popup
if float64(len(output))*1.5 < float64(x*y) {
return gui.createMessagePanel(gui.g, nil, "Output", output)
}
}
type listViewState struct {
selectedLine int
lineCount int
@ -541,16 +543,9 @@ func (gui *Gui) RunWithSubprocesses() error {
if err == gocui.ErrQuit {
break
} else if err == gui.Errors.ErrSubProcess {
gui.SubProcess.Stdin = os.Stdin
output, err := gui.runCommand(gui.SubProcess)
if err != nil {
if err := gui.runCommand(); err != nil {
return err
}
gui.State.SubProcessOutput = output
gui.SubProcess.Stdout = ioutil.Discard
gui.SubProcess.Stderr = ioutil.Discard
gui.SubProcess.Stdin = nil
gui.SubProcess = nil
} else {
return err
}
@ -559,20 +554,29 @@ func (gui *Gui) RunWithSubprocesses() error {
return nil
}
// adapted from https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html
func (gui *Gui) runCommand(cmd *exec.Cmd) (string, error) {
var stdoutBuf bytes.Buffer
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stdoutBuf)
func (gui *Gui) runCommand() error {
gui.SubProcess.Stdout = os.Stdout
gui.SubProcess.Stderr = os.Stdout
gui.SubProcess.Stdin = os.Stdin
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue))
if err := gui.SubProcess.Run(); err != nil {
// not handling the error explicitly because usually we're going to see it
// in the output anyway
gui.Log.Error(err)
}
outStr := stdoutBuf.String()
return outStr, nil
gui.SubProcess.Stdout = ioutil.Discard
gui.SubProcess.Stderr = ioutil.Discard
gui.SubProcess.Stdin = nil
gui.SubProcess = nil
// fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
// fmt.Scanln() // wait for enter press
return nil
}
func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {

Loading…
Cancel
Save