mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
commit
26273b3aa3
29
azure/aci.go
29
azure/aci.go
@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/docker/api/azure/login"
|
||||
"github.com/docker/api/context/store"
|
||||
"github.com/docker/api/progress"
|
||||
)
|
||||
|
||||
const aciDockerUserAgent = "docker-cli"
|
||||
@ -47,10 +48,17 @@ func createACIContainers(ctx context.Context, aciContext store.AciContext, group
|
||||
}
|
||||
|
||||
func createOrUpdateACIContainers(ctx context.Context, aciContext store.AciContext, groupDefinition containerinstance.ContainerGroup) error {
|
||||
w := progress.ContextWriter(ctx)
|
||||
containerGroupsClient, err := getContainerGroupsClient(aciContext.SubscriptionID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot get container group client")
|
||||
}
|
||||
w.Event(progress.Event{
|
||||
ID: *groupDefinition.Name,
|
||||
Status: progress.Working,
|
||||
StatusText: "Waiting",
|
||||
})
|
||||
|
||||
future, err := containerGroupsClient.CreateOrUpdate(
|
||||
ctx,
|
||||
aciContext.ResourceGroup,
|
||||
@ -61,14 +69,35 @@ func createOrUpdateACIContainers(ctx context.Context, aciContext store.AciContex
|
||||
return err
|
||||
}
|
||||
|
||||
w.Event(progress.Event{
|
||||
ID: *groupDefinition.Name,
|
||||
Status: progress.Done,
|
||||
StatusText: "Created",
|
||||
})
|
||||
for _, c := range *groupDefinition.Containers {
|
||||
w.Event(progress.Event{
|
||||
ID: *c.Name,
|
||||
Status: progress.Working,
|
||||
StatusText: "Waiting",
|
||||
})
|
||||
}
|
||||
|
||||
err = future.WaitForCompletionRef(ctx, containerGroupsClient.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containerGroup, err := future.Result(containerGroupsClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range *groupDefinition.Containers {
|
||||
w.Event(progress.Event{
|
||||
ID: *c.Name,
|
||||
Status: progress.Done,
|
||||
StatusText: "Done",
|
||||
})
|
||||
}
|
||||
|
||||
if len(*containerGroup.Containers) > 1 {
|
||||
var commands []string
|
||||
|
@ -35,6 +35,7 @@ import (
|
||||
|
||||
"github.com/docker/api/client"
|
||||
"github.com/docker/api/compose"
|
||||
"github.com/docker/api/progress"
|
||||
)
|
||||
|
||||
func upCommand() *cobra.Command {
|
||||
@ -64,5 +65,7 @@ func runUp(ctx context.Context, opts compose.ProjectOptions) error {
|
||||
return errors.New("compose not implemented in current context")
|
||||
}
|
||||
|
||||
return composeService.Up(ctx, opts)
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return composeService.Up(ctx, opts)
|
||||
})
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import (
|
||||
|
||||
"github.com/docker/api/cli/options/run"
|
||||
"github.com/docker/api/client"
|
||||
"github.com/docker/api/progress"
|
||||
)
|
||||
|
||||
// Command runs a container
|
||||
@ -68,10 +69,11 @@ func runRun(ctx context.Context, image string, opts run.Opts) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.ContainerService().Run(ctx, containerConfig); err != nil {
|
||||
return err
|
||||
err = progress.Run(ctx, func(ctx context.Context) error {
|
||||
return c.ContainerService().Run(ctx, containerConfig)
|
||||
})
|
||||
if err == nil {
|
||||
fmt.Println(opts.Name)
|
||||
}
|
||||
fmt.Println(opts.Name)
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
5
go.mod
5
go.mod
@ -6,7 +6,6 @@ require (
|
||||
github.com/AlecAivazis/survey/v2 v2.0.7
|
||||
github.com/Azure/azure-sdk-for-go v43.2.0+incompatible
|
||||
github.com/Azure/azure-storage-file-go v0.7.0
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.10.2
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.3
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1
|
||||
@ -32,7 +31,8 @@ require (
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/mux v1.7.4 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
@ -46,6 +46,7 @@ require (
|
||||
github.com/tj/survey v2.0.6+incompatible
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.24.0
|
||||
|
7
go.sum
7
go.sum
@ -4,8 +4,6 @@ github.com/AlecAivazis/survey/v2 v2.0.7 h1:+f825XHLse/hWd2tE/V5df04WFGimk34Eyg/z
|
||||
github.com/AlecAivazis/survey/v2 v2.0.7/go.mod h1:mlizQTaPjnR4jcpwRSaSlkbsRfYFEyKgLQvYTzxxiHA=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-sdk-for-go v43.1.0+incompatible h1:m6EAp2Dmb8/t+ToZ2jtmvdp+JBwsdfSlZuBV31WGLGQ=
|
||||
github.com/Azure/azure-sdk-for-go v43.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v43.2.0+incompatible h1:H8jfb+wuVlLqyP1Nr6zqapNxqhgwshD5OETJsBO74iY=
|
||||
github.com/Azure/azure-sdk-for-go v43.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-storage-file-go v0.7.0 h1:yWoV0MYwzmoSgWACcVkdPolvAULFPNamcQLpIvS/Et4=
|
||||
@ -80,6 +78,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -204,6 +204,8 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74 h1:kvRIeqJNICemq2UFLx8q/Pj+1IRNZS0XPTaMFkuNsvg=
|
||||
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74/go.mod h1:pJ0Ot5YGdTcMdxnPMyGCfAr6fKXe0g9cDlz16MuFEBE=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@ -330,6 +332,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
29
progress/plain.go
Normal file
29
progress/plain.go
Normal file
@ -0,0 +1,29 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type plainWriter struct {
|
||||
out io.Writer
|
||||
done chan bool
|
||||
}
|
||||
|
||||
func (p *plainWriter) Start(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-p.done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *plainWriter) Event(e Event) {
|
||||
fmt.Println(e.ID, e.Text, e.StatusText)
|
||||
}
|
||||
|
||||
func (p *plainWriter) Stop() {
|
||||
p.done <- true
|
||||
}
|
39
progress/spinner.go
Normal file
39
progress/spinner.go
Normal file
@ -0,0 +1,39 @@
|
||||
package progress
|
||||
|
||||
import "time"
|
||||
|
||||
type spinner struct {
|
||||
time time.Time
|
||||
index int
|
||||
chars []string
|
||||
stop bool
|
||||
done string
|
||||
}
|
||||
|
||||
func newSpinner() *spinner {
|
||||
return &spinner{
|
||||
index: 0,
|
||||
time: time.Now(),
|
||||
chars: []string{
|
||||
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
|
||||
},
|
||||
done: "⠿",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spinner) String() string {
|
||||
if s.stop {
|
||||
return s.done
|
||||
}
|
||||
|
||||
d := time.Since(s.time)
|
||||
if d.Milliseconds() > 100 {
|
||||
s.index = (s.index + 1) % len(s.chars)
|
||||
}
|
||||
|
||||
return s.chars[s.index]
|
||||
}
|
||||
|
||||
func (s *spinner) Stop() {
|
||||
s.stop = true
|
||||
}
|
169
progress/tty.go
Normal file
169
progress/tty.go
Normal file
@ -0,0 +1,169 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/buger/goterm"
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
type ttyWriter struct {
|
||||
out io.Writer
|
||||
events map[string]Event
|
||||
eventIDs []string
|
||||
repeated bool
|
||||
numLines int
|
||||
done chan bool
|
||||
mtx *sync.RWMutex
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Start(ctx context.Context) error {
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
w.print()
|
||||
return ctx.Err()
|
||||
case <-w.done:
|
||||
w.print()
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
w.print()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Stop() {
|
||||
w.done <- true
|
||||
}
|
||||
|
||||
func (w *ttyWriter) Event(e Event) {
|
||||
w.mtx.Lock()
|
||||
defer w.mtx.Unlock()
|
||||
if !contains(w.eventIDs, e.ID) {
|
||||
w.eventIDs = append(w.eventIDs, e.ID)
|
||||
}
|
||||
if _, ok := w.events[e.ID]; ok {
|
||||
event := w.events[e.ID]
|
||||
if event.Status != Done && e.Status == Done {
|
||||
event.stop()
|
||||
}
|
||||
event.Status = e.Status
|
||||
event.Text = e.Text
|
||||
event.StatusText = e.StatusText
|
||||
w.events[e.ID] = event
|
||||
} else {
|
||||
e.startTime = time.Now()
|
||||
e.spinner = newSpinner()
|
||||
w.events[e.ID] = e
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ttyWriter) print() {
|
||||
w.mtx.Lock()
|
||||
defer w.mtx.Unlock()
|
||||
if len(w.eventIDs) == 0 {
|
||||
return
|
||||
}
|
||||
terminalWidth := goterm.Width()
|
||||
b := aec.EmptyBuilder
|
||||
for i := 0; i <= w.numLines; i++ {
|
||||
b = b.Up(1)
|
||||
}
|
||||
if !w.repeated {
|
||||
b = b.Down(1)
|
||||
}
|
||||
w.repeated = true
|
||||
fmt.Fprint(w.out, b.Column(0).ANSI)
|
||||
|
||||
// Hide the cursor while we are printing
|
||||
fmt.Fprint(w.out, aec.Hide)
|
||||
defer fmt.Fprint(w.out, aec.Show)
|
||||
|
||||
firstLine := fmt.Sprintf("[+] Running %d/%d", numDone(w.events), w.numLines)
|
||||
if w.numLines != 0 && numDone(w.events) == w.numLines {
|
||||
firstLine = aec.Apply(firstLine, aec.BlueF)
|
||||
}
|
||||
fmt.Fprintln(w.out, firstLine)
|
||||
|
||||
var statusPadding int
|
||||
for _, v := range w.eventIDs {
|
||||
l := len(fmt.Sprintf("%s %s", w.events[v].ID, w.events[v].Text))
|
||||
if statusPadding < l {
|
||||
statusPadding = l
|
||||
}
|
||||
}
|
||||
|
||||
numLines := 0
|
||||
for _, v := range w.eventIDs {
|
||||
line := lineText(w.events[v], terminalWidth, statusPadding)
|
||||
// nolint: errcheck
|
||||
fmt.Fprint(w.out, line)
|
||||
numLines++
|
||||
}
|
||||
|
||||
w.numLines = numLines
|
||||
}
|
||||
|
||||
func lineText(event Event, terminalWidth, statusPadding int) string {
|
||||
endTime := time.Now()
|
||||
if event.Status != Working {
|
||||
endTime = event.endTime
|
||||
}
|
||||
|
||||
elapsed := endTime.Sub(event.startTime).Seconds()
|
||||
|
||||
textLen := len(fmt.Sprintf("%s %s", event.ID, event.Text))
|
||||
padding := statusPadding - textLen
|
||||
if padding < 0 {
|
||||
padding = 0
|
||||
}
|
||||
text := fmt.Sprintf(" %s %s %s%s %s",
|
||||
event.spinner.String(),
|
||||
event.ID,
|
||||
event.Text,
|
||||
strings.Repeat(" ", padding),
|
||||
event.StatusText,
|
||||
)
|
||||
timer := fmt.Sprintf("%.1fs\n", elapsed)
|
||||
o := align(text, timer, terminalWidth)
|
||||
|
||||
color := aec.WhiteF
|
||||
if event.Status == Done {
|
||||
color = aec.BlueF
|
||||
}
|
||||
if event.Status == Error {
|
||||
color = aec.RedF
|
||||
}
|
||||
|
||||
return aec.Apply(o, color)
|
||||
}
|
||||
|
||||
func numDone(events map[string]Event) int {
|
||||
i := 0
|
||||
for _, e := range events {
|
||||
if e.Status == Done {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func align(l, r string, w int) string {
|
||||
return fmt.Sprintf("%-[2]*[1]s %[3]s", l, w-len(r)-1, r)
|
||||
}
|
||||
|
||||
func contains(ar []string, needle string) bool {
|
||||
for _, v := range ar {
|
||||
if needle == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
37
progress/tty_test.go
Normal file
37
progress/tty_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLineText(t *testing.T) {
|
||||
now := time.Now()
|
||||
ev := Event{
|
||||
ID: "id",
|
||||
Text: "Text",
|
||||
Status: Working,
|
||||
StatusText: "Status",
|
||||
endTime: now,
|
||||
startTime: now,
|
||||
spinner: &spinner{
|
||||
chars: []string{"."},
|
||||
},
|
||||
}
|
||||
|
||||
lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text))
|
||||
|
||||
out := lineText(ev, 50, lineWidth)
|
||||
assert.Equal(t, "\x1b[37m . id Text Status 0.0s\n\x1b[0m", out)
|
||||
|
||||
ev.Status = Done
|
||||
out = lineText(ev, 50, lineWidth)
|
||||
assert.Equal(t, "\x1b[34m . id Text Status 0.0s\n\x1b[0m", out)
|
||||
|
||||
ev.Status = Error
|
||||
out = lineText(ev, 50, lineWidth)
|
||||
assert.Equal(t, "\x1b[31m . id Text Status 0.0s\n\x1b[0m", out)
|
||||
}
|
112
progress/writer.go
Normal file
112
progress/writer.go
Normal file
@ -0,0 +1,112 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/moby/term"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// EventStatus indicates the status of an action
|
||||
type EventStatus int
|
||||
|
||||
const (
|
||||
// Working means that the current task is working
|
||||
Working EventStatus = iota
|
||||
// Done means that the current task is done
|
||||
Done
|
||||
// Error means that the current task has errored
|
||||
Error
|
||||
)
|
||||
|
||||
// Event reprensents a progress event
|
||||
type Event struct {
|
||||
ID string
|
||||
Text string
|
||||
Status EventStatus
|
||||
StatusText string
|
||||
Done bool
|
||||
|
||||
startTime time.Time
|
||||
endTime time.Time
|
||||
spinner *spinner
|
||||
}
|
||||
|
||||
func (e *Event) stop() {
|
||||
e.endTime = time.Now()
|
||||
e.spinner.Stop()
|
||||
}
|
||||
|
||||
// Writer can write multiple progress events
|
||||
type Writer interface {
|
||||
Start(context.Context) error
|
||||
Stop()
|
||||
Event(Event)
|
||||
}
|
||||
|
||||
type writerKey struct{}
|
||||
|
||||
// WithContextWriter adds the writer to the context
|
||||
func WithContextWriter(ctx context.Context, writer Writer) context.Context {
|
||||
return context.WithValue(ctx, writerKey{}, writer)
|
||||
}
|
||||
|
||||
// ContextWriter returns the writer from the context
|
||||
func ContextWriter(ctx context.Context) Writer {
|
||||
s, _ := ctx.Value(writerKey{}).(Writer)
|
||||
return s
|
||||
}
|
||||
|
||||
type progressFunc func(context.Context) error
|
||||
|
||||
// Run will run a writer and the progress function
|
||||
// in parallel
|
||||
func Run(ctx context.Context, pf progressFunc) error {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
w, err := NewWriter(os.Stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eg.Go(func() error {
|
||||
return w.Start(context.Background())
|
||||
})
|
||||
|
||||
ctx = WithContextWriter(ctx, w)
|
||||
|
||||
eg.Go(func() error {
|
||||
defer w.Stop()
|
||||
return pf(ctx)
|
||||
})
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
// NewWriter returns a new multi-progress writer
|
||||
func NewWriter(out console.File) (Writer, error) {
|
||||
_, isTerminal := term.GetFdInfo(out)
|
||||
|
||||
if isTerminal {
|
||||
con, err := console.ConsoleFromFile(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ttyWriter{
|
||||
out: con,
|
||||
eventIDs: []string{},
|
||||
events: map[string]Event{},
|
||||
repeated: false,
|
||||
done: make(chan bool),
|
||||
mtx: &sync.RWMutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &plainWriter{
|
||||
out: out,
|
||||
done: make(chan bool),
|
||||
}, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user