Skip to content

Commit

Permalink
fix(tui): Correctly handle new ctx with custom log and iostreams out (
Browse files Browse the repository at this point in the history
#1179)


Reviewed-by: Cezar Craciunoiu <[email protected]>
Approved-by: Cezar Craciunoiu <[email protected]>
  • Loading branch information
craciunoiuc authored Jan 22, 2024
2 parents 9b6e393 + a375615 commit 39a5f10
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 60 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
api.zip v0.1.5
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/LastPossum/kamino v0.0.1
github.com/MakeNowJust/heredoc v1.0.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/acorn-io/baaah v0.0.0-20230522221318-afcc93619e30
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/LastPossum/kamino v0.0.1 h1:ewKozCZpQkwmz6/x02jgC5hPigQJEa/xu50bVec7B6s=
github.com/LastPossum/kamino v0.0.1/go.mod h1:13HlWhK7QIXYR2/9uyOnaLBMAyDsx5PxxAoVC7An8OY=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
Expand Down
8 changes: 4 additions & 4 deletions iostreams/iostreams.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,10 +498,10 @@ func (r *fdReader) Fd() uintptr {
return r.fd
}

func NewNoTTYWriter(w io.Writer) *fdWriter {
return &fdWriter{w, 0}
func NewNoTTYWriter(w io.Writer, ptr uintptr) *fdWriter {
return &fdWriter{w, ptr}
}

func NewNoTTYReader(r io.ReadCloser) *fdReader {
return &fdReader{r, 0}
func NewNoTTYReader(r io.ReadCloser, ptr uintptr) *fdReader {
return &fdReader{r, ptr}
}
69 changes: 48 additions & 21 deletions tui/paraprogress/paraprogress.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"time"

"github.com/LastPossum/kamino"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
Expand Down Expand Up @@ -64,32 +65,58 @@ func NewParaProgress(ctx context.Context, processes []*Process, opts ...ParaProg

maxNameLen := md.nameWidth
if maxNameLen <= 0 {
maxNameLen = len(processes[0].Name)
for _, download := range processes {
if nameLen := len(download.Name); nameLen > maxNameLen {
for _, process := range processes {
if nameLen := len(process.Name); nameLen > maxNameLen {
maxNameLen = nameLen
}
}
}

for i := range processes {
processes[i].norender = md.norender
processes[i].NameWidth = maxNameLen
processes[i].timeout = md.timeout

pctx := ctx
processes[i].ctx = pctx
log.G(processes[i].ctx).Level = log.G(ctx).Level

// Update formatter when using KraftKit's TextFormatter. The
// TextFormatter recognises that this is a non-standard terminal and
// changes the output to a more machine readable format. Instead we want
// to force the formatting so that the output looks seamless with the
// style of the TUI.
if formatter, ok := log.G(ctx).Formatter.(*log.TextFormatter); ok {
formatter.ForceColors = termenv.DefaultOutput().ColorProfile() != termenv.Ascii
formatter.ForceFormatting = true
log.G(processes[i].ctx).Formatter = formatter
for _, process := range processes {
process.norender = md.norender
process.NameWidth = maxNameLen
process.timeout = md.timeout

if md.norender {
process.ctx = ctx
} else {
pctx := ctx

logger, err := kamino.Clone(log.G(pctx),
kamino.WithZeroUnexported(),
)
if err != nil {
return nil, err
}

// Update formatter when using KraftKit's TextFormatter. The
// TextFormatter recognises that this is a non-standard terminal and
// changes the output to a more machine readable format. Instead we want
// to force the formatting so that the output looks seamless with the
// style of the TUI.
if formatter, ok := logger.Formatter.(*log.TextFormatter); ok {
formatter.ForceColors = termenv.DefaultOutput().ColorProfile() != termenv.Ascii
formatter.ForceFormatting = true
logger.Formatter = formatter
}

logger.Out = process
pctx = log.WithLogger(pctx, logger)

ios, err := kamino.Clone(iostreams.G(pctx),
kamino.WithZeroUnexported(),
)
if err != nil {
return nil, err
}

ios.Out = iostreams.NewNoTTYWriter(process, iostreams.G(ctx).Out.Fd())
ios.ErrOut = process
ios.In = iostreams.G(ctx).In

pctx = iostreams.WithIOStreams(pctx, ios)

process.ctx = pctx
}
}

Expand Down
6 changes: 0 additions & 6 deletions tui/paraprogress/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/charmbracelet/lipgloss"
"github.com/muesli/reflow/indent"

"kraftkit.sh/iostreams"
"kraftkit.sh/log"
"kraftkit.sh/tui"
"kraftkit.sh/utils"
Expand Down Expand Up @@ -130,11 +129,6 @@ func (p *Process) Start() tea.Cmd {

if p.norender {
log.G(p.ctx).Info(p.Name)
} else {
// Set the output to the process Writer such that we can hijack logs and
// print them in a per-process isolated view.
iostreams.G(p.ctx).Out = iostreams.NewNoTTYWriter(p)
log.G(p.ctx).Out = p
}

err := p.processFunc(p.ctx, p.onProgress)
Expand Down
73 changes: 44 additions & 29 deletions tui/processtree/processtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"time"

"github.com/LastPossum/kamino"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/stopwatch"
tea "github.com/charmbracelet/bubbletea"
Expand Down Expand Up @@ -110,6 +111,45 @@ func NewProcessTree(ctx context.Context, opts []ProcessTreeOption, tree ...*Proc
total++
item.norender = pt.norender
item.timeout = pt.timeout

if pt.norender {
item.ctx = pt.ctx
return nil
}

ictx := pt.ctx

logger, err := kamino.Clone(log.G(ictx),
kamino.WithZeroUnexported(),
)
if err != nil {
return err
}

logger.Out = item

if formatter, ok := logger.Formatter.(*log.TextFormatter); ok {
formatter.ForceColors = termenv.DefaultOutput().ColorProfile() != termenv.Ascii
formatter.ForceFormatting = true
logger.Formatter = formatter
}

ictx = log.WithLogger(ictx, logger)

ios, err := kamino.Clone(iostreams.G(ictx),
kamino.WithZeroUnexported(),
)
if err != nil {
return err
}

ios.Out = iostreams.NewNoTTYWriter(item, iostreams.G(ctx).Out.Fd())
ios.ErrOut = item
ios.In = iostreams.G(ctx).In
ictx = iostreams.WithIOStreams(ictx, ios)

item.ctx = ictx

return nil
})

Expand Down Expand Up @@ -250,21 +290,17 @@ func (pt ProcessTree) getNextReadyChildren(tree []*ProcessTreeItem) []*ProcessTr
}

func (pt *ProcessTree) traverseTreeAndCall(items []*ProcessTreeItem, callback func(*ProcessTreeItem) error) error {
for i, item := range items {
item := item

if len(item.children) > 0 {
if err := pt.traverseTreeAndCall(item.children, callback); err != nil {
for _, child := range items {
if len(child.children) > 0 {
if err := pt.traverseTreeAndCall(child.children, callback); err != nil {
return err
}
}

// Call the callback on the leaf node first
if err := callback(item); err != nil {
if err := callback(child); err != nil {
return err
}

items[i] = item
}

return nil
Expand All @@ -274,29 +310,8 @@ func (pt *ProcessTree) waitForProcessCmd(item *ProcessTreeItem) tea.Cmd {
return func() tea.Msg {
item := item // golang closures

// Clone the context to be used individually by each process.
ctx := new(context.Context)
*ctx = pt.ctx
item.ctx = *ctx

if pt.norender {
log.G(item.ctx).Info(item.textLeft)
} else {
// Set the output to the process Writer such that we can hijack logs and
// print them in a per-process isolated view.
iostreams.G(item.ctx).Out = iostreams.NewNoTTYWriter(item)
log.G(item.ctx).Out = item

// Update formatter when using KraftKit's TextFormatter. The
// TextFormatter recognises that this is a non-standard terminal and
// changes the output to a more machine readable format. Instead we want
// to force the formatting so that the output looks seamless with the
// style of the TUI.
if formatter, ok := log.G(item.ctx).Formatter.(*log.TextFormatter); ok {
formatter.ForceColors = termenv.DefaultOutput().ColorProfile() != termenv.Ascii
formatter.ForceFormatting = true
log.G(item.ctx).Formatter = formatter
}
}

// Set the process to running
Expand Down

0 comments on commit 39a5f10

Please sign in to comment.