diff --git a/common/go.mod b/common/go.mod index 80ff2c7168..9f9345c48c 100644 --- a/common/go.mod +++ b/common/go.mod @@ -112,7 +112,7 @@ require ( github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/vbatts/tar-split v0.12.3 // indirect - github.com/vbauerster/mpb/v8 v8.12.1 // indirect + github.com/vbauerster/mpb/v8 v8.13.0 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect diff --git a/common/go.sum b/common/go.sum index 89ce5a9b5f..8943cb797d 100644 --- a/common/go.sum +++ b/common/go.sum @@ -246,8 +246,8 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw= github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0= -github.com/vbauerster/mpb/v8 v8.12.1 h1:pyj3yQ2ZGQJgUXm4h17QpR+eERaNz5OQ1ftPSEE/sMM= -github.com/vbauerster/mpb/v8 v8.12.1/go.mod h1:XLXRfStkw/6i5k0aQltijDHT1Z93fD1DVwmIdcFUp6k= +github.com/vbauerster/mpb/v8 v8.13.0 h1:uv5hrYmoVNy+3w4mjePgQXwmqUzLjhKX5p2+77CFfwo= +github.com/vbauerster/mpb/v8 v8.13.0/go.mod h1:yY6DDD9c3idT01aDMzs98st5aotXIOXXUgPG3hAbAHk= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= diff --git a/go.work.sum b/go.work.sum index 0457c78a17..509ced10a7 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1178,6 +1178,7 @@ golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1255,6 +1256,7 @@ golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= @@ -1262,6 +1264,7 @@ golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3c golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= @@ -1360,6 +1363,7 @@ golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/image/go.mod b/image/go.mod index f58f69f1a7..c58e3d3b4a 100644 --- a/image/go.mod +++ b/image/go.mod @@ -35,7 +35,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/sylabs/sif/v2 v2.24.1 github.com/ulikunitz/xz v0.5.15 - github.com/vbauerster/mpb/v8 v8.12.1 + github.com/vbauerster/mpb/v8 v8.13.0 go.etcd.io/bbolt v1.5.0 go.podman.io/storage v1.63.0 go.yaml.in/yaml/v3 v3.0.4 diff --git a/image/go.sum b/image/go.sum index 657316fc02..5cc32405b3 100644 --- a/image/go.sum +++ b/image/go.sum @@ -186,8 +186,8 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw= github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0= -github.com/vbauerster/mpb/v8 v8.12.1 h1:pyj3yQ2ZGQJgUXm4h17QpR+eERaNz5OQ1ftPSEE/sMM= -github.com/vbauerster/mpb/v8 v8.12.1/go.mod h1:XLXRfStkw/6i5k0aQltijDHT1Z93fD1DVwmIdcFUp6k= +github.com/vbauerster/mpb/v8 v8.13.0 h1:uv5hrYmoVNy+3w4mjePgQXwmqUzLjhKX5p2+77CFfwo= +github.com/vbauerster/mpb/v8 v8.13.0/go.mod h1:yY6DDD9c3idT01aDMzs98st5aotXIOXXUgPG3hAbAHk= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= diff --git a/vendor/github.com/vbauerster/mpb/v8/bar.go b/vendor/github.com/vbauerster/mpb/v8/bar.go index 6ae9458a12..907f563417 100644 --- a/vendor/github.com/vbauerster/mpb/v8/bar.go +++ b/vendor/github.com/vbauerster/mpb/v8/bar.go @@ -16,6 +16,8 @@ import ( // Bar represents a progress bar. type Bar struct { + ctx context.Context + cancel func() index int // used by heap priority int // used by heap frameCh chan *renderFrame @@ -23,8 +25,6 @@ type Bar struct { container *Progress bs *bState bsOk chan struct{} - ctx context.Context - cancel func() } type decorSyncTable [2][]*decor.Sync @@ -44,14 +44,12 @@ type bState struct { ewmaDecorators []decor.EwmaDecorator filler BarFiller extender extenderFunc - renderReq chan<- time.Time waitBar *Bar // key for (*pState).queueBars trimSpace bool aborted bool triggerComplete bool rmOnComplete bool noPop bool - autoRefresh bool } type renderFrame struct { @@ -171,7 +169,7 @@ func (b *Bar) EnableTriggerComplete() { s.triggerComplete = true if s.current >= s.total { s.current = s.total - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -197,7 +195,7 @@ func (b *Bar) SetTotal(total int64, complete bool) { if complete { s.current = s.total s.triggerComplete = true - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -214,7 +212,7 @@ func (b *Bar) SetCurrent(current int64) { s.current = current if s.triggerComplete && s.current >= s.total { s.current = s.total - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -238,7 +236,7 @@ func (b *Bar) IncrInt64(n int64) { s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -266,7 +264,7 @@ func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) { s.current += n if s.triggerComplete && s.current >= s.total { s.current = s.total - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -288,7 +286,7 @@ func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) { s.current = current if s.triggerComplete && s.current >= s.total { s.current = s.total - b.done(s.renderReq, s.autoRefresh) + b.done() } }: case <-b.ctx.Done(): @@ -325,7 +323,7 @@ func (b *Bar) Abort(drop bool) { s.aborted = true s.rmOnComplete = drop s.triggerComplete = true - b.done(s.renderReq, s.autoRefresh) + b.done() }: case <-b.ctx.Done(): } @@ -503,33 +501,33 @@ func (s *bState) wSyncTable() (table decorSyncTable) { return table } -func (b *Bar) done(renderReq chan<- time.Time, autoRefresh bool) { - if autoRefresh { +func (b *Bar) done() { + if b.container.autoRefresh { // Technically this call isn't required, but if refresh rate is set to // one hour for example and bar completes within a few minutes p.Wait() // will wait for one hour. This call helps to avoid unnecessary waiting. - go b.tryEarlyRefresh(renderReq) + go b.tryEarlyRefresh() } else { b.cancel() } } -func (b *Bar) tryEarlyRefresh(renderReq chan<- time.Time) { +func (b *Bar) tryEarlyRefresh() { otherRunning := make(chan struct{}) - ok := b.container.iterateBars(func(bar *Bar) bool { + yield := func(bar *Bar) bool { if b != bar && bar.isRunning() { close(otherRunning) return false // stop traverse } return true // continue traverse - }) - if ok { + } + if err := b.container.iterateBars(yield); err == nil { select { case <-otherRunning: default: for { select { - case renderReq <- time.Now(): + case b.container.renderReq <- time.Now(): case <-b.ctx.Done(): return } diff --git a/vendor/github.com/vbauerster/mpb/v8/container_option.go b/vendor/github.com/vbauerster/mpb/v8/container_option.go index a520064ccc..34e52f0161 100644 --- a/vendor/github.com/vbauerster/mpb/v8/container_option.go +++ b/vendor/github.com/vbauerster/mpb/v8/container_option.go @@ -97,6 +97,21 @@ func WithAutoRefresh() ContainerOption { } } +// ForceAutoRefresh is an alias of WithAutoRefresh. +func ForceAutoRefresh() ContainerOption { + return WithAutoRefresh() +} + +// ForceTTY force treating output as tty. +// This one implicitly enables WithAutoRefresh unless WithManualRefresh specified. +// Can be handy if you need to wrap os.Stdout or os.Stderr for example like: +// mpb.WithOutput(io.MultiWriter(os.Stdout, &someTestBuf)). +func ForceTTY() ContainerOption { + return func(s *pState) { + s.forceTTY = true + } +} + // PopCompletedMode pop completed bars out of progress container. // In this mode completed bars get moved to the top and stop // participating in rendering cycle. diff --git a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go index ff530fcdc2..54731b2248 100644 --- a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go +++ b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go @@ -2,35 +2,32 @@ package cwriter import ( "bytes" - "errors" + "cmp" "io" "os" "strconv" ) -// https://github.com/dylanaraps/pure-sh-bible#cursor-movement const ( + defaultWidth = 80 + + // https://github.com/dylanaraps/pure-sh-bible#cursor-movement escOpen = "\x1b[" cuuAndEd = "A\x1b[J" ) -// ErrNotTTY not a TeleTYpewriter error. -var ErrNotTTY = errors.New("not a terminal") - // New returns a new Writer with defaults. -func New(out io.Writer) *Writer { +func New(out io.Writer, width int, forceTTY bool) *Writer { w := &Writer{ - Buffer: new(bytes.Buffer), - out: out, - termSize: func(_ int) (int, int, error) { - return -1, -1, ErrNotTTY - }, + Buffer: new(bytes.Buffer), + out: out, + width: cmp.Or(width, defaultWidth), + forceTTY: forceTTY, } if f, ok := out.(*os.File); ok { - w.fd = int(f.Fd()) - if IsTerminal(w.fd) { + if fd := int(f.Fd()); IsTerminal(fd) { + w.fd = fd w.terminal = true - w.termSize = GetSize } } bb := make([]byte, 16) @@ -38,19 +35,28 @@ func New(out io.Writer) *Writer { return w } -// IsTerminal tells whether underlying io.Writer is terminal. +// IsTerminal tells whether underlying io.Writer is terminal aka TTY. func (w *Writer) IsTerminal() bool { return w.terminal } // GetTermSize returns WxH of underlying terminal. func (w *Writer) GetTermSize() (width, height int, err error) { - return w.termSize(w.fd) + if !w.terminal { + width, height = w.width, w.width*3/2+1 + return + } + return GetSize(w.fd) } type escWriter []byte func (b escWriter) ansiCuuAndEd(out io.Writer, n int) error { + // some terminals interpret 'cursor up 0' as 'cursor up 1' + // therefore ignore n <= 0 case + if n <= 0 { + return nil + } b = strconv.AppendInt(b, int64(n), 10) _, err := out.Write(append(b, []byte(cuuAndEd)...)) return err diff --git a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go index e80d757afe..9a3187ceee 100644 --- a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go +++ b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_posix.go @@ -17,26 +17,31 @@ type Writer struct { out io.Writer ew escWriter fd int + width int terminal bool - termSize func(int) (int, int, error) + forceTTY bool } // Flush flushes the underlying buffer. // It's caller's responsibility to pass correct number of lines. func (w *Writer) Flush(lines int) error { _, err := w.WriteTo(w.out) - // some terminals interpret 'cursor up 0' as 'cursor up 1' - if err == nil && lines > 0 { - err = w.ew.ansiCuuAndEd(w, lines) + if err != nil { + return err + } + + if w.terminal || w.forceTTY { + return w.ew.ansiCuuAndEd(w, lines) } - return err + + return nil } // GetSize returns the dimensions of the given terminal. func GetSize(fd int) (width, height int, err error) { ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) if err != nil { - return -1, -1, err + return } return int(ws.Col), int(ws.Row), nil } diff --git a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_windows.go index 44293f26ac..ddc7cd21b8 100644 --- a/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_windows.go +++ b/vendor/github.com/vbauerster/mpb/v8/cwriter/writer_windows.go @@ -4,17 +4,17 @@ package cwriter import ( "bytes" + "fmt" "io" "unsafe" "golang.org/x/sys/windows" ) -var kernel32 = windows.NewLazySystemDLL("kernel32.dll") - var ( + kernel32 = windows.NewLazySystemDLL("kernel32.dll") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterA") ) // Writer is a buffered terminal writer, which moves cursor N lines up @@ -24,59 +24,84 @@ type Writer struct { *bytes.Buffer out io.Writer ew escWriter - lines int fd int + width int + lines int terminal bool - termSize func(int) (int, int, error) + forceTTY bool } // Flush flushes the underlying buffer. // It's caller's responsibility to pass correct number of lines. func (w *Writer) Flush(lines int) error { - if w.lines > 0 { - err := w.clearLines(w.lines) + if w.terminal { + err := w.clearLines() if err != nil { return err } + w.lines = lines // save lines for the next clearLines } - w.lines = lines + _, err := w.WriteTo(w.out) - return err + if err != nil { + return err + } + + if !w.terminal && w.forceTTY { + return w.ew.ansiCuuAndEd(w, lines) + } + + return nil } -func (w *Writer) clearLines(n int) error { - if !w.terminal { - // hope it's cygwin or similar - return w.ew.ansiCuuAndEd(w.out, n) +func (w *Writer) clearLines() error { + if w.lines <= 0 { + return nil } var info windows.ConsoleScreenBufferInfo - if err := windows.GetConsoleScreenBufferInfo(windows.Handle(w.fd), &info); err != nil { + err := windows.GetConsoleScreenBufferInfo(windows.Handle(w.fd), &info) + if err != nil { return err } - info.CursorPosition.Y -= int16(n) - if info.CursorPosition.Y < 0 { - info.CursorPosition.Y = 0 + newPosition := info.CursorPosition + + if y := int(newPosition.Y); w.lines > y { + w.lines = y + } else { + y -= w.lines + newPosition.Y = int16(y) } - _, _, _ = procSetConsoleCursorPosition.Call( + + // clear lines by writing space character n times starting at newPosition + // if we don't some artefacts of a previous write may retain + var r1 uintptr + var written uint32 + n := uint32(info.Size.X) * uint32(w.lines) + r1, _, err = procFillConsoleOutputCharacter.Call( uintptr(w.fd), - uintptr(uint32(uint16(info.CursorPosition.Y))<<16|uint32(uint16(info.CursorPosition.X))), + uintptr(byte(' ')), + uintptr(n), + *(*uintptr)(unsafe.Pointer(&newPosition)), + uintptr(unsafe.Pointer(&written)), ) - - // clear the lines - cursor := &windows.Coord{ - X: info.Window.Left, - Y: info.CursorPosition.Y, + if r1 == 0 { + return err + } + if written != n { + return fmt.Errorf("FillConsoleOutputCharacterA: written != n (%d != %d)", written, n) } - count := uint32(info.Size.X) * uint32(n) - _, _, _ = procFillConsoleOutputCharacter.Call( + + // move cursor to newPosition for the next write + r1, _, err = procSetConsoleCursorPosition.Call( uintptr(w.fd), - uintptr(' '), - uintptr(count), - *(*uintptr)(unsafe.Pointer(cursor)), - uintptr(unsafe.Pointer(new(uint32))), + uintptr(uint32(uint16(newPosition.Y))<<16|uint32(uint16(newPosition.X))), ) + if r1 == 0 { + return err + } + return nil } @@ -84,8 +109,9 @@ func (w *Writer) clearLines(n int) error { // These dimensions don't include any scrollback buffer height. func GetSize(fd int) (width, height int, err error) { var info windows.ConsoleScreenBufferInfo - if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil { - return 0, 0, err + err = windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info) + if err != nil { + return } // terminal.GetSize from crypto/ssh adds "+ 1" to both width and height: // https://go.googlesource.com/crypto/+/refs/heads/release-branch.go1.14/ssh/terminal/util_windows.go#75 diff --git a/vendor/github.com/vbauerster/mpb/v8/progress.go b/vendor/github.com/vbauerster/mpb/v8/progress.go index c2cbdffc6f..cf75efdba7 100644 --- a/vendor/github.com/vbauerster/mpb/v8/progress.go +++ b/vendor/github.com/vbauerster/mpb/v8/progress.go @@ -2,7 +2,6 @@ package mpb import ( "bytes" - "cmp" "context" "fmt" "io" @@ -24,12 +23,17 @@ var ErrDone = fmt.Errorf("%T instance can't be reused after %[1]T.Wait()", (*Pro // Progress represents a container that renders one or more progress bars. type Progress struct { + // Render error if any, to be inspected after (*Progress).Wait call only. + Error error + + ctx context.Context + cancel func() pwg, bwg *sync.WaitGroup operateState chan func(*pState) interceptIO chan func(io.Writer) - done <-chan struct{} - ctx context.Context - cancel func() + renderReq chan time.Time + done chan struct{} + autoRefresh bool } type queueBar struct { @@ -40,7 +44,6 @@ type queueBar struct { // pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine. type pState struct { hm heapManager - renderReq chan time.Time idCount int popPriority int @@ -59,6 +62,7 @@ type pState struct { popCompleted bool autoRefresh bool rmOnComplete bool + forceTTY bool } // New creates new Progress container instance. It's not possible to @@ -77,9 +81,8 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { ctx, cancel := context.WithCancel(ctx) s := &pState{ - hmQueueLen: defaultHmQueueLength, - renderReq: make(chan time.Time), popPriority: math.MinInt32, + hmQueueLen: defaultHmQueueLength, refreshRate: defaultRefreshRate, queueBars: make(map[*Bar]*queueBar), output: os.Stdout, @@ -99,34 +102,33 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { s.hm = make(heapManager, s.hmQueueLen) p := &Progress{ + ctx: ctx, + cancel: cancel, pwg: new(sync.WaitGroup), bwg: new(sync.WaitGroup), operateState: make(chan func(*pState)), interceptIO: make(chan func(io.Writer)), - ctx: ctx, - cancel: cancel, + done: make(chan struct{}), } - cw := cwriter.New(s.output) + var refreshStrategy func(*Progress, *pState) + cw := cwriter.New(s.output, s.reqWidth, s.forceTTY) switch { case s.manualRC != nil: - done := make(chan struct{}) - p.done = done - s.autoRefresh = false - go s.manualRefreshListener(ctx, done) - case s.autoRefresh || cw.IsTerminal(): - done := make(chan struct{}) - p.done = done - s.autoRefresh = true - go s.autoRefreshListener(ctx, done) + p.renderReq = make(chan time.Time) + refreshStrategy = (*Progress).manualRefreshListener + case s.autoRefresh || s.forceTTY || cw.IsTerminal(): + p.autoRefresh = true + p.renderReq = make(chan time.Time) + refreshStrategy = (*Progress).autoRefreshListener default: - p.done = ctx.Done() - s.autoRefresh = false + refreshStrategy = (*Progress).nopRefreshListener } - p.pwg.Add(2) + p.pwg.Add(3) go s.hm.run(p.pwg, s.shutdownNotifier, s.handOverBarHeap) go p.serve(s, cw) + go refreshStrategy(p, s) return p } @@ -192,21 +194,19 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) (*Ba func (p *Progress) makeBar(priority int) *Bar { ctx, cancel := context.WithCancel(p.ctx) - bar := &Bar{ + return &Bar{ + ctx: ctx, + cancel: cancel, priority: priority, frameCh: make(chan *renderFrame, 1), operateState: make(chan func(*bState)), bsOk: make(chan struct{}), container: p, - ctx: ctx, - cancel: cancel, } - - return bar } // blocks until iteration is done -func (p *Progress) iterateBars(yield func(*Bar) bool) (ok bool) { +func (p *Progress) iterateBars(yield func(*Bar) bool) error { seqCh := make(chan iter.Seq[*Bar], 1) select { case p.operateState <- func(s *pState) { s.hm.iter(seqCh) }: @@ -215,9 +215,9 @@ func (p *Progress) iterateBars(yield func(*Bar) bool) (ok bool) { break } } - return true + return nil case <-p.done: - return false + return ErrDone } } @@ -256,8 +256,8 @@ func (p *Progress) Write(b []byte) (int, error) { } } -// Wait waits for all bars to complete and finally shutdowns container. After -// this method has been called, there is no way to reuse `*Progress` instance. +// Wait waits for all bars to complete and then shutdowns the container. +// There is no way to reuse `*Progress` instance after this method has been called. func (p *Progress) Wait() { p.bwg.Wait() p.Shutdown() @@ -284,7 +284,7 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) { var dw *cwriter.Writer if s.delayRC != nil { - dw = cwriter.New(io.Discard) + dw = cwriter.New(io.Discard, 0, false) } else { dw = cw } @@ -298,25 +298,29 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) { op(s) case fn := <-p.interceptIO: fn(cw) - case <-s.renderReq: + case <-p.renderReq: err := s.render(dw) if err != nil { p.cancel() - // (*pState).(autoRefreshListener|manualRefreshListener) may block - // if not depleting s.renderReq + // refreshStrategy goroutine is sending to p.renderReq unbuffered chan + // without any select therefore p.renderReq must be depleted here + // otherwise refreshStrategy goroutine may block and leak. for { select { - case <-s.renderReq: + case <-p.renderReq: case <-p.done: _, _ = fmt.Fprintln(s.debugOut, err.Error()) + p.Error = err return } } } case <-p.done: - if s.autoRefresh && s.rmOnComplete { - if err := s.render(cw); err != nil { + if p.autoRefresh && s.rmOnComplete { + err := s.render(cw) + if err != nil { _, _ = fmt.Fprintln(s.debugOut, err.Error()) + p.Error = err } } return @@ -324,48 +328,50 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) { } } -func (s *pState) autoRefreshListener(ctx context.Context, done chan struct{}) { +func (p *Progress) autoRefreshListener(s *pState) { + defer p.pwg.Done() ticker := time.NewTicker(s.refreshRate) defer ticker.Stop() for { select { case t := <-ticker.C: - s.renderReq <- t - case <-ctx.Done(): - close(done) + p.renderReq <- t + case <-p.ctx.Done(): + close(p.done) return } } } -func (s *pState) manualRefreshListener(ctx context.Context, done chan struct{}) { +func (p *Progress) manualRefreshListener(s *pState) { + defer p.pwg.Done() for { select { case x := <-s.manualRC: if t, ok := x.(time.Time); ok { - s.renderReq <- t + p.renderReq <- t } else { - s.renderReq <- time.Now() + p.renderReq <- time.Now() } - case <-ctx.Done(): - close(done) + case <-p.ctx.Done(): + close(p.done) return } } } -func (s *pState) render(cw *cwriter.Writer) (err error) { +func (p *Progress) nopRefreshListener(_ *pState) { + defer p.pwg.Done() + <-p.ctx.Done() + close(p.done) +} + +func (s *pState) render(cw *cwriter.Writer) error { s.hm.sync() - var width, height int - if cw.IsTerminal() { - width, height, err = cw.GetTermSize() - if err != nil { - return err - } - } else { - width = cmp.Or(s.reqWidth, 80) - height = width + width, height, err := cw.GetTermSize() + if err != nil { + return err } return s.flush(cw, height, s.hm.render(width)) @@ -444,9 +450,7 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio reqWidth: s.reqWidth, total: total, filler: filler, - renderReq: s.renderReq, triggerComplete: total > 0, - autoRefresh: s.autoRefresh, } bs.extender = func(_ decor.Statistics, rows ...io.Reader) ([]io.Reader, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 0583e22dd7..0130fa4d40 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -448,7 +448,7 @@ github.com/ulikunitz/xz/lzma github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/storage -# github.com/vbauerster/mpb/v8 v8.12.1 +# github.com/vbauerster/mpb/v8 v8.13.0 ## explicit; go 1.25.0 github.com/vbauerster/mpb/v8 github.com/vbauerster/mpb/v8/cwriter