Merge pull request #689 from vrothberg/image-use-spinners

update c/image
This commit is contained in:
Valentin Rothberg 2019-07-17 14:15:43 +02:00 committed by GitHub
commit 5f45112678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 490 additions and 325 deletions

4
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8 // indirect
github.com/containers/buildah v1.8.4
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293
github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1
github.com/containers/storage v1.12.10
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 // indirect
@ -51,7 +51,7 @@ require (
github.com/ulikunitz/xz v0.5.4 // indirect
github.com/urfave/cli v1.20.0
github.com/vbatts/tar-split v0.10.2 // indirect
github.com/vbauerster/mpb v3.3.4+incompatible // indirect
github.com/vbauerster/mpb v3.4.0+incompatible // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.1.0 // indirect

6
go.sum
View File

@ -12,6 +12,8 @@ github.com/containers/buildah v1.8.4 h1:06c+UNeEWMa2wA1Z7muZ0ZqUzE91sDuZJbB0BiZa
github.com/containers/buildah v1.8.4/go.mod h1:1CsiLJvyU+h+wOjnqJJOWuJCVcMxZOr5HN/gHGdzJxY=
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293 h1:EalCgZ875kDCN2HcOch50q48GKerWGc5eV0BllCvln8=
github.com/containers/image v1.5.2-0.20190620105408-93b1deece293/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1 h1:RGlzwWSoGBbc5fgGysRrGAPLn8xQwihzRVPVDW5yQlo=
github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk=
github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M=
github.com/containers/storage v1.12.10 h1:vw1aiLsZ1LvO09ELMxVBTe35tThRiMftI2cPeH+G5ow=
@ -101,6 +103,10 @@ github.com/vbatts/tar-split v0.10.2 h1:CXd7HEKGkTLjBMinpObcJZU5Hm8EKlor2a1JtX6ms
github.com/vbatts/tar-split v0.10.2/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4=
github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw=
github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vrothberg/image v0.0.0-20190717060034-cd5ce8239f51 h1:u4Hw4D3PLODtsZJ1FKi7j8bkd+zyJOc28dRSiVTOgyE=
github.com/vrothberg/image v0.0.0-20190717060034-cd5ce8239f51/go.mod h1:/hIyjuUvIY6X2wGj/fbsA9zwlfAize8B2DLPishEHHg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=

View File

@ -597,15 +597,32 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind
prefix = prefix[:maxPrefixLen]
}
bar := pool.AddBar(info.Size,
mpb.BarClearOnComplete(),
mpb.PrependDecorators(
decor.Name(prefix),
),
mpb.AppendDecorators(
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete),
),
)
// Use a normal progress bar when we know the size (i.e., size > 0).
// Otherwise, use a spinner to indicate that something's happening.
var bar *mpb.Bar
if info.Size > 0 {
bar = pool.AddBar(info.Size,
mpb.BarClearOnComplete(),
mpb.PrependDecorators(
decor.Name(prefix),
),
mpb.AppendDecorators(
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete),
),
)
} else {
bar = pool.AddSpinner(info.Size,
mpb.SpinnerOnLeft,
mpb.BarClearOnComplete(),
mpb.SpinnerStyle([]string{".", "..", "...", "....", ""}),
mpb.PrependDecorators(
decor.Name(prefix),
),
mpb.AppendDecorators(
decor.OnComplete(decor.Name(""), " "+onComplete),
),
)
}
if c.progressOutput == ioutil.Discard {
c.Printf("Copying %s %s\n", kind, info.Digest)
}

View File

@ -226,6 +226,7 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI
}
if s1.Config != nil {
i.Labels = s1.Config.Labels
i.Env = s1.Config.Env
}
return i, nil
}

View File

@ -241,6 +241,7 @@ func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*t
}
if s2.Config != nil {
i.Labels = s2.Config.Labels
i.Env = s2.Config.Env
}
return i, nil
}

View File

@ -116,6 +116,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type
Architecture: v1.Architecture,
Os: v1.OS,
Layers: layerInfosToStrings(m.LayerInfos()),
Env: d1.Config.Env,
}
return i, nil
}

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub
// +build containers_image_ostree
package ostree

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub
// +build containers_image_ostree
package ostree

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub
// +build containers_image_ostree
package ostree

View File

@ -56,6 +56,7 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st
// If an entry is not found empty strings are returned for the username and password
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
if sys != nil && sys.DockerAuthConfig != nil {
logrus.Debug("Returning credentials from DockerAuthConfig")
return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil
}
@ -76,12 +77,15 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin
legacyFormat := path == dockerLegacyPath
username, password, err := findAuthentication(registry, path, legacyFormat)
if err != nil {
logrus.Debugf("Credentials not found")
return "", "", err
}
if username != "" && password != "" {
logrus.Debugf("Returning credentials from %s", path)
return username, password, nil
}
}
logrus.Debugf("Credentials not found")
return "", "", nil
}

View File

@ -1,4 +1,4 @@
// +build !containers_image_ostree_stub,linux
// +build containers_image_ostree,linux
package alltransports

View File

@ -1,4 +1,4 @@
// +build containers_image_ostree_stub !linux
// +build !containers_image_ostree !linux
package alltransports

View File

@ -398,6 +398,7 @@ type ImageInspectInfo struct {
Architecture string
Os string
Layers []string
Env []string
}
// DockerAuthConfig contains authorization information for connecting to a registry.

View File

@ -1,8 +1,8 @@
language: go
sudo: false
go:
- 1.8.x
- 1.9.x
- 1.10.x
- tip
before_install:
- go get -t -v ./...

View File

@ -31,8 +31,6 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
p := mpb.New(
// override default (80) width
mpb.WithWidth(64),
// override default "[=>-]" format
mpb.WithFormat("╢▌▌░╟"),
// override default 120ms refresh rate
mpb.WithRefreshRate(180*time.Millisecond),
)
@ -41,6 +39,8 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is
name := "Single Bar:"
// adding a single bar
bar := p.AddBar(int64(total),
// override default "[=>-]" style
mpb.BarStyle("╢▌▌░╟"),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),

View File

@ -2,6 +2,7 @@ package mpb
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
@ -11,21 +12,8 @@ import (
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
"github.com/vbauerster/mpb/internal"
)
const (
rLeft = iota
rFill
rTip
rEmpty
rRight
)
const formatLen = 5
type barRunes [formatLen]rune
// Bar represents a progress Bar
type Bar struct {
priority int
@ -45,15 +33,30 @@ type Bar struct {
shutdown chan struct{}
}
// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
Fill(w io.Writer, width int, s *decor.Statistics)
}
// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)
func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
f(w, width, stat)
}
type (
bState struct {
filler Filler
id int
width int
alignment int
total int64
current int64
runes barRunes
trimLeftSpace bool
trimRightSpace bool
trimSpace bool
toComplete bool
removeOnComplete bool
barClearOnComplete bool
@ -73,8 +76,8 @@ type (
runningBar *Bar
}
refill struct {
char rune
till int64
r rune
limit int64
}
frameReader struct {
io.Reader
@ -84,14 +87,20 @@ type (
}
)
func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar {
if total <= 0 {
total = time.Now().Unix()
}
func newBar(
ctx context.Context,
wg *sync.WaitGroup,
filler Filler,
id, width int,
total int64,
options ...BarOption,
) *Bar {
s := &bState{
filler: filler,
id: id,
priority: id,
width: width,
total: total,
}
@ -104,6 +113,9 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
s.bufP = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufB = bytes.NewBuffer(make([]byte, 0, s.width))
s.bufA = bytes.NewBuffer(make([]byte, 0, s.width))
if s.newLineExtendFn != nil {
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
b := &Bar{
priority: s.priority,
@ -121,11 +133,7 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt
b.priority = b.runningBar.priority
}
if s.newLineExtendFn != nil {
s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width))
}
go b.serve(wg, s, cancel)
go b.serve(ctx, wg, s)
return b
}
@ -178,52 +186,42 @@ func (b *Bar) Current() int64 {
}
// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
// Set complete to true, to trigger bar complete event now.
func (b *Bar) SetTotal(total int64, complete bool) {
select {
case b.operateState <- func(s *bState) {
if total > 0 {
s.total = total
}
if final {
s.total = total
if complete && !s.toComplete {
s.current = s.total
s.toComplete = true
}
}:
return true
case <-b.done:
return false
}
}
// SetRefill sets fill rune to r, up until n.
func (b *Bar) SetRefill(n int, r rune) {
if n <= 0 {
return
}
// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(amount int64) {
b.operateState <- func(s *bState) {
s.refill = &refill{r, int64(n)}
if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
f.SetRefill(amount)
}
}
}
// RefillBy is deprecated, use SetRefill
func (b *Bar) RefillBy(n int, r rune) {
b.SetRefill(n, r)
}
// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
b.IncrBy(1)
}
// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start),
// which expected to be provided, if any ewma based decorator is used.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
select {
case b.operateState <- func(s *bState) {
s.current += int64(n)
if s.current >= s.total {
if s.total > 0 && s.current >= s.total {
s.current = s.total
s.toComplete = true
}
@ -238,9 +236,9 @@ func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
// omit select here, because primary usage of the method is for loop
// condition, like for !bar.Completed() {...}
// so when toComplete=true it is called once (at which time, the bar is still alive),
// then quits the loop and never suppose to be called afterwards.
// condition, like for !bar.Completed() {...} so when toComplete=true
// it is called once (at which time, the bar is still alive), then
// quits the loop and never suppose to be called afterwards.
return <-b.boolCh
}
@ -253,8 +251,9 @@ func (b *Bar) wSyncTable() [][]chan int {
}
}
func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) {
func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
defer wg.Done()
cancel := ctx.Done()
for {
select {
case op := <-b.operateState:
@ -322,8 +321,6 @@ func (b *Bar) render(debugOut io.Writer, tw int) {
}
func (s *bState) draw(termWidth int) io.Reader {
defer s.bufA.WriteByte('\n')
if s.panicMsg != "" {
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
}
@ -338,77 +335,32 @@ func (s *bState) draw(termWidth int) io.Reader {
s.bufA.WriteString(d.Decor(stat))
}
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes())
if s.barClearOnComplete && s.completeFlushed {
s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufA)
}
s.fillBar(s.width)
barCount := utf8.RuneCount(s.bufB.Bytes())
totalCount := prependCount + barCount + appendCount
if spaceCount := 0; totalCount > termWidth {
if !s.trimLeftSpace {
spaceCount++
}
if !s.trimRightSpace {
spaceCount++
}
s.fillBar(termWidth - prependCount - appendCount - spaceCount)
}
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes())
return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
func (s *bState) fillBar(width int) {
defer func() {
s.bufB.WriteRune(s.runes[rRight])
if !s.trimRightSpace {
s.bufB.WriteByte(' ')
}
}()
s.bufB.Reset()
if !s.trimLeftSpace {
if !s.trimSpace {
// reserve space for edge spaces
termWidth -= 2
s.bufB.WriteByte(' ')
}
s.bufB.WriteRune(s.runes[rLeft])
if width <= 2 {
return
}
// bar s.width without leftEnd and rightEnd runes
barWidth := width - 2
completedWidth := internal.Percentage(s.total, s.current, int64(barWidth))
if s.refill != nil {
till := internal.Percentage(s.total, s.refill.till, int64(barWidth))
// append refill rune
var i int64
for i = 0; i < till; i++ {
s.bufB.WriteRune(s.refill.char)
}
for i = till; i < completedWidth; i++ {
s.bufB.WriteRune(s.runes[rFill])
}
if prependCount+s.width+appendCount > termWidth {
s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat)
} else {
var i int64
for i = 0; i < completedWidth; i++ {
s.bufB.WriteRune(s.runes[rFill])
}
s.filler.Fill(s.bufB, s.width, stat)
}
if completedWidth < int64(barWidth) && completedWidth > 0 {
_, size := utf8.DecodeLastRune(s.bufB.Bytes())
s.bufB.Truncate(s.bufB.Len() - size)
s.bufB.WriteRune(s.runes[rTip])
if !s.trimSpace {
s.bufB.WriteByte(' ')
}
for i := completedWidth; i < int64(barWidth); i++ {
s.bufB.WriteRune(s.runes[rEmpty])
}
s.bufA.WriteByte('\n')
return io.MultiReader(s.bufP, s.bufB, s.bufA)
}
func (s *bState) wSyncTable() [][]chan int {
@ -442,14 +394,6 @@ func newStatistics(s *bState) *decor.Statistics {
}
}
func strToBarRunes(format string) (array barRunes) {
for i, n := 0, 0; len(format) > 0; i++ {
array[i], n = utf8.DecodeRuneInString(format)
format = format[n:]
}
return
}
func countLines(b []byte) int {
return bytes.Count(b, []byte("\n"))
}

111
vendor/github.com/vbauerster/mpb/bar_filler.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package mpb
import (
"io"
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
"github.com/vbauerster/mpb/internal"
)
const (
rLeft = iota
rFill
rTip
rEmpty
rRight
rRevTip
rRefill
)
var defaultBarStyle = "[=>-]<+"
type barFiller struct {
format [][]byte
refillAmount int64
reverse bool
}
func newDefaultBarFiller() Filler {
bf := &barFiller{
format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)),
}
bf.setStyle(defaultBarStyle)
return bf
}
func (s *barFiller) setStyle(style string) {
if !utf8.ValidString(style) {
return
}
src := make([][]byte, 0, utf8.RuneCountInString(style))
for _, r := range style {
src = append(src, []byte(string(r)))
}
copy(s.format, src)
}
func (s *barFiller) setReverse() {
s.reverse = true
}
func (s *barFiller) SetRefill(amount int64) {
s.refillAmount = amount
}
func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
// don't count rLeft and rRight [brackets]
width -= 2
if width < 2 {
return
}
w.Write(s.format[rLeft])
if width == 2 {
w.Write(s.format[rRight])
return
}
bb := make([][]byte, width)
cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width)))
for i := 0; i < cwidth; i++ {
bb[i] = s.format[rFill]
}
if s.refillAmount > 0 {
var rwidth int
if s.refillAmount > stat.Current {
rwidth = cwidth
} else {
rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width)))
}
for i := 0; i < rwidth; i++ {
bb[i] = s.format[rRefill]
}
}
if cwidth > 0 && cwidth < width {
bb[cwidth-1] = s.format[rTip]
}
for i := cwidth; i < width; i++ {
bb[i] = s.format[rEmpty]
}
if s.reverse {
if cwidth > 0 && cwidth < width {
bb[cwidth-1] = s.format[rRevTip]
}
for i := len(bb) - 1; i >= 0; i-- {
w.Write(bb[i])
}
} else {
for i := 0; i < len(bb); i++ {
w.Write(bb[i])
}
}
w.Write(s.format[rRight])
}

View File

@ -6,11 +6,10 @@ import (
"github.com/vbauerster/mpb/decor"
)
// BarOption is a function option which changes the default behavior of a bar,
// if passed to p.AddBar(int64, ...BarOption)
// BarOption is a function option which changes the default behavior of a bar.
type BarOption func(*bState)
// AppendDecorators let you inject decorators to the bar's right side
// AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(appenders ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range appenders {
@ -25,7 +24,7 @@ func AppendDecorators(appenders ...decor.Decorator) BarOption {
}
}
// PrependDecorators let you inject decorators to the bar's left side
// PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(prependers ...decor.Decorator) BarOption {
return func(s *bState) {
for _, decorator := range prependers {
@ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption {
}
}
// BarTrimLeft trims left side space of the bar
func BarTrimLeft() BarOption {
return func(s *bState) {
s.trimLeftSpace = true
}
}
// BarTrimRight trims right space of the bar
func BarTrimRight() BarOption {
return func(s *bState) {
s.trimRightSpace = true
}
}
// BarTrim trims both left and right spaces of the bar
func BarTrim() BarOption {
return func(s *bState) {
s.trimLeftSpace = true
s.trimRightSpace = true
}
}
// BarID overwrites internal bar id
// BarID sets bar id.
func BarID(id int) BarOption {
return func(s *bState) {
s.id = id
}
}
// BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
// If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
// and then whole bar line gets removed completely.
// BarWidth sets bar width independent of the container.
func BarWidth(width int) BarOption {
return func(s *bState) {
s.width = width
}
}
// BarRemoveOnComplete is a flag, if set whole bar line will be removed
// on complete event. If both BarRemoveOnComplete and BarClearOnComplete
// are set, first bar section gets cleared and then whole bar line
// gets removed completely.
func BarRemoveOnComplete() BarOption {
return func(s *bState) {
s.removeOnComplete = true
}
}
// BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
// To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
// BarReplaceOnComplete is indicator for delayed bar start, after the
// `runningBar` is complete. To achieve bar replacement effect,
// `runningBar` should has its `BarRemoveOnComplete` option set.
func BarReplaceOnComplete(runningBar *Bar) BarOption {
return BarParkTo(runningBar)
}
// BarParkTo same as BarReplaceOnComplete
func BarParkTo(runningBar *Bar) BarOption {
return func(s *bState) {
s.runningBar = runningBar
}
}
// BarClearOnComplete is a flag, if set will clear bar section on complete event.
// If you need to remove a whole bar line, refer to BarRemoveOnComplete.
// BarClearOnComplete is a flag, if set will clear bar section on
// complete event. If you need to remove a whole bar line, refer to
// BarRemoveOnComplete.
func BarClearOnComplete() BarOption {
return func(s *bState) {
s.barClearOnComplete = true
}
}
// BarPriority sets bar's priority.
// Zero is highest priority, i.e. bar will be on top.
// If `BarReplaceOnComplete` option is supplied, this option is ignored.
// BarPriority sets bar's priority. Zero is highest priority, i.e. bar
// will be on top. If `BarReplaceOnComplete` option is supplied, this
// option is ignored.
func BarPriority(priority int) BarOption {
return func(s *bState) {
s.priority = priority
}
}
// BarNewLineExtend takes user defined efn, which gets called each render cycle.
// Any write to provided writer of efn, will appear on new line of respective bar.
// BarNewLineExtend takes user defined efn, which gets called each
// render cycle. Any write to provided writer of efn, will appear on
// new line of respective bar.
func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption {
return func(s *bState) {
s.newLineExtendFn = efn
}
}
func barWidth(w int) BarOption {
// TrimSpace trims bar's edge spaces.
func TrimSpace() BarOption {
return func(s *bState) {
s.width = w
s.trimSpace = true
}
}
func barFormat(format string) BarOption {
// BarStyle sets custom bar style, default one is "[=>-]<+".
//
// '[' left bracket rune
//
// '=' fill rune
//
// '>' tip rune
//
// '-' empty rune
//
// ']' right bracket rune
//
// '<' reverse tip rune, used when BarReverse option is set
//
// '+' refill rune, used when *Bar.SetRefill(int64) is called
//
// It's ok to provide first five runes only, for example mpb.BarStyle("╢▌▌░╟")
func BarStyle(style string) BarOption {
chk := func(filler Filler) (interface{}, bool) {
if style == "" {
return nil, false
}
t, ok := filler.(*barFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*barFiller).setStyle(style)
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// BarReverse reverse mode, bar will progress from right to left.
func BarReverse() BarOption {
chk := func(filler Filler) (interface{}, bool) {
t, ok := filler.(*barFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*barFiller).setReverse()
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// SpinnerStyle sets custom spinner style.
// Effective when Filler type is spinner.
func SpinnerStyle(frames []string) BarOption {
chk := func(filler Filler) (interface{}, bool) {
if len(frames) == 0 {
return nil, false
}
t, ok := filler.(*spinnerFiller)
return t, ok
}
cb := func(t interface{}) {
t.(*spinnerFiller).frames = frames
}
return MakeFillerTypeSpecificBarOption(chk, cb)
}
// MakeFillerTypeSpecificBarOption makes BarOption specific to Filler's
// actual type. If you implement your own Filler, so most probably
// you'll need this. See BarStyle or SpinnerStyle for example.
func MakeFillerTypeSpecificBarOption(
typeChecker func(Filler) (interface{}, bool),
cb func(interface{}),
) BarOption {
return func(s *bState) {
s.runes = strToBarRunes(format)
if t, ok := typeChecker(s.filler); ok {
cb(t)
}
}
}
// OptionOnCondition returns option when condition evaluates to true.
func OptionOnCondition(option BarOption, condition func() bool) BarOption {
if condition() {
return option
}
return nil
}

View File

@ -22,8 +22,8 @@ var (
clearCursorAndLine = cursorUp + clearLine
)
// Writer is a buffered the writer that updates the terminal.
// The contents of writer will be flushed when Flush is called.
// Writer is a buffered the writer that updates the terminal. The
// contents of writer will be flushed when Flush is called.
type Writer struct {
out io.Writer
buf bytes.Buffer
@ -64,11 +64,13 @@ func (w *Writer) WriteString(s string) (n int, err error) {
return w.buf.WriteString(s)
}
// ReadFrom reads from the provided io.Reader and writes to the underlying buffer.
// ReadFrom reads from the provided io.Reader and writes to the
// underlying buffer.
func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return w.buf.ReadFrom(r)
}
// GetWidth returns width of underlying terminal.
func (w *Writer) GetWidth() (int, error) {
if w.isTerminal {
tw, _, err := terminal.GetSize(w.fd)

View File

@ -8,7 +8,7 @@ import (
"syscall"
"unsafe"
"github.com/mattn/go-isatty"
isatty "github.com/mattn/go-isatty"
)
var kernel32 = syscall.NewLazyDLL("kernel32.dll")

View File

@ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator {
return Counters(0, pairFormat, wcc...)
}
// CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
// CountersKibiByte is a wrapper around Counters with predefined unit
// UnitKiB (bytes/1024).
func CountersKibiByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKiB, pairFormat, wcc...)
}
// CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
// CountersKiloByte is a wrapper around Counters with predefined unit
// UnitKB (bytes/1000).
func CountersKiloByte(pairFormat string, wcc ...WC) Decorator {
return Counters(UnitKB, pairFormat, wcc...)
}

View File

@ -31,8 +31,12 @@ const (
DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight
)
// TimeStyle enum.
type TimeStyle int
// TimeStyle kinds.
const (
ET_STYLE_GO = iota
ET_STYLE_GO TimeStyle = iota
ET_STYLE_HHMMSS
ET_STYLE_HHMM
ET_STYLE_MMSS
@ -47,35 +51,37 @@ type Statistics struct {
}
// Decorator interface.
// A decorator must implement this interface, in order to be used with mpb library.
// A decorator must implement this interface, in order to be used with
// mpb library.
type Decorator interface {
Decor(*Statistics) string
Syncable
}
// Syncable interface.
// All decorators implement this interface implicitly.
// Its Syncable method exposes width sync channel, if sync is enabled.
// All decorators implement this interface implicitly. Its Syncable
// method exposes width sync channel, if sync is enabled.
type Syncable interface {
Syncable() (bool, chan int)
}
// OnCompleteMessenger interface.
// Decorators implementing this interface suppose to return provided string on complete event.
// Decorators implementing this interface suppose to return provided
// string on complete event.
type OnCompleteMessenger interface {
OnCompleteMessage(string)
}
// AmountReceiver interface.
// If decorator needs to receive increment amount,
// so this is the right interface to implement.
// If decorator needs to receive increment amount, so this is the right
// interface to implement.
type AmountReceiver interface {
NextAmount(int, ...time.Duration)
}
// ShutdownListener interface.
// If decorator needs to be notified once upon bar shutdown event,
// so this is the right interface to implement.
// If decorator needs to be notified once upon bar shutdown event, so
// this is the right interface to implement.
type ShutdownListener interface {
Shutdown()
}
@ -90,6 +96,7 @@ var (
// WC is a struct with two public fields W and C, both of int type.
// W represents width and C represents bit set of width related config.
// A decorator should embed WC, in order to become Syncable.
type WC struct {
W int
C int
@ -126,12 +133,13 @@ func (wc *WC) Init() {
}
}
// Syncable is implementation of Syncable interface.
func (wc *WC) Syncable() (bool, chan int) {
return (wc.C & DSyncWidth) != 0, wc.wsync
}
// OnComplete returns decorator, which wraps provided decorator, with sole
// purpose to display provided message on complete event.
// OnComplete returns decorator, which wraps provided decorator, with
// sole purpose to display provided message on complete event.
//
// `decorator` Decorator to wrap
//

View File

@ -10,7 +10,7 @@ import (
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
func Elapsed(style int, wcc ...WC) Decorator {
func Elapsed(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator {
type elapsedDecorator struct {
WC
style int
style TimeStyle
startTime time.Time
msg string
completeMsg *string

View File

@ -6,7 +6,6 @@ import (
"time"
"github.com/VividCortex/ewma"
"github.com/vbauerster/mpb/internal"
)
type TimeNormalizer func(time.Duration) time.Duration
@ -18,7 +17,7 @@ type TimeNormalizer func(time.Duration) time.Duration
// `age` is the previous N samples to average over.
//
// `wcc` optional WC config
func EwmaETA(style int, age float64, wcc ...WC) Decorator {
func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...)
}
@ -31,7 +30,7 @@ func EwmaETA(style int, age float64, wcc ...WC) Decorator {
// `normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
//
// `wcc` optional WC config
func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@ -48,7 +47,7 @@ func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalize
type movingAverageETA struct {
WC
style int
style TimeStyle
average ewma.MovingAverage
completeMsg *string
normalizer TimeNormalizer
@ -59,7 +58,7 @@ func (d *movingAverageETA) Decor(st *Statistics) string {
return d.FormatMsg(*d.completeMsg)
}
v := internal.Round(d.average.Value())
v := math.Round(d.average.Value())
remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v)))
hours := int64((remaining / time.Hour) % 60)
minutes := int64((remaining / time.Minute) % 60)
@ -105,7 +104,7 @@ func (d *movingAverageETA) OnCompleteMessage(msg string) {
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
func AverageETA(style int, wcc ...WC) Decorator {
func AverageETA(style TimeStyle, wcc ...WC) Decorator {
var wc WC
for _, widthConf := range wcc {
wc = widthConf
@ -121,7 +120,7 @@ func AverageETA(style int, wcc ...WC) Decorator {
type averageETA struct {
WC
style int
style TimeStyle
startTime time.Time
completeMsg *string
}
@ -133,7 +132,7 @@ func (d *averageETA) Decor(st *Statistics) string {
var str string
timeElapsed := time.Since(d.startTime)
v := internal.Round(float64(timeElapsed) / float64(st.Current))
v := math.Round(float64(timeElapsed) / float64(st.Current))
if math.IsInf(v, 0) || math.IsNaN(v) {
v = 0
}

View File

@ -6,9 +6,9 @@ import (
"github.com/VividCortex/ewma"
)
// MovingAverage is the interface that computes a moving average over a time-
// series stream of numbers. The average may be over a window or exponentially
// decaying.
// MovingAverage is the interface that computes a moving average over
// a time-series stream of numbers. The average may be over a window
// or exponentially decaying.
type MovingAverage interface {
Add(float64)
Value() float64
@ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) {
s.count++
}
// NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
// NewMedianEwma is ewma based MovingAverage, which gets its values
// from median MovingAverage.
func NewMedianEwma(age ...float64) MovingAverage {
return &medianEwma{
MovingAverage: ewma.NewMovingAverage(age...),

View File

@ -137,7 +137,8 @@ func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator {
return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...)
}
// MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
// MovingAverageSpeed decorator relies on MovingAverage implementation
// to calculate its average.
//
// `unit` one of [0|UnitKiB|UnitKB] zero for no unit
//

View File

@ -1,10 +1,12 @@
package internal
import "math"
// Percentage is a helper function, to calculate percentage.
func Percentage(total, current, width int64) int64 {
if total <= 0 {
return 0
}
p := float64(width*current) / float64(total)
return int64(Round(p))
return int64(math.Round(p))
}

View File

@ -1,49 +0,0 @@
package internal
import "math"
const (
uvone = 0x3FF0000000000000
mask = 0x7FF
shift = 64 - 11 - 1
bias = 1023
signMask = 1 << 63
fracMask = 1<<shift - 1
)
// Round returns the nearest integer, rounding half away from zero.
//
// Special cases are:
// Round(±0) = ±0
// Round(±Inf) = ±Inf
// Round(NaN) = NaN
func Round(x float64) float64 {
// Round is a faster implementation of:
//
// func Round(x float64) float64 {
// t := Trunc(x)
// if Abs(x-t) >= 0.5 {
// return t + Copysign(1, x)
// }
// return t
// }
bits := math.Float64bits(x)
e := uint(bits>>shift) & mask
if e < bias {
// Round abs(x) < 1 including denormals.
bits &= signMask // +-0
if e == bias-1 {
bits |= uvone // +-1
}
} else if e < bias+shift {
// Round any abs(x) >= 1 containing a fractional component [0,1).
//
// Numbers with larger exponents are returned unchanged since they
// must be either an integer, infinity, or NaN.
const half = 1 << (shift - 1)
e -= bias
bits += half >> e
bits &^= fracMask >> e
}
return math.Float64frombits(bits)
}

View File

@ -1,29 +1,30 @@
package mpb
import (
"context"
"io"
"sync"
"time"
"unicode/utf8"
"github.com/vbauerster/mpb/cwriter"
)
// ProgressOption is a function option which changes the default behavior of
// progress pool, if passed to mpb.New(...ProgressOption)
// ProgressOption is a function option which changes the default
// behavior of progress pool, if passed to mpb.New(...ProgressOption).
type ProgressOption func(*pState)
// WithWaitGroup provides means to have a single joint point.
// If *sync.WaitGroup is provided, you can safely call just p.Wait()
// without calling Wait() on provided *sync.WaitGroup.
// Makes sense when there are more than one bar to render.
// WithWaitGroup provides means to have a single joint point. If
// *sync.WaitGroup is provided, you can safely call just p.Wait()
// without calling Wait() on provided *sync.WaitGroup. Makes sense
// when there are more than one bar to render.
func WithWaitGroup(wg *sync.WaitGroup) ProgressOption {
return func(s *pState) {
s.uwg = wg
}
}
// WithWidth overrides default width 80
// WithWidth sets container width. Default is 80. Bars inherit this
// width, as long as no BarWidth is applied.
func WithWidth(w int) ProgressOption {
return func(s *pState) {
if w >= 0 {
@ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption {
}
}
// WithFormat overrides default bar format "[=>-]"
func WithFormat(format string) ProgressOption {
return func(s *pState) {
if utf8.RuneCountInString(format) == formatLen {
s.format = format
}
}
}
// WithRefreshRate overrides default 120ms refresh rate
// WithRefreshRate overrides default 120ms refresh rate.
func WithRefreshRate(d time.Duration) ProgressOption {
return func(s *pState) {
if d < 10*time.Millisecond {
@ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption {
}
}
// WithCancel provide your cancel channel,
// which you plan to close at some point.
func WithCancel(ch <-chan struct{}) ProgressOption {
// WithContext provided context will be used for cancellation purposes.
func WithContext(ctx context.Context) ProgressOption {
return func(s *pState) {
s.cancel = ch
if ctx == nil {
return
}
s.ctx = ctx
}
}
// WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
// WithShutdownNotifier provided chanel will be closed, after all bars
// have been rendered.
func WithShutdownNotifier(ch chan struct{}) ProgressOption {
return func(s *pState) {
s.shutdownNotifier = ch
}
}
// WithOutput overrides default output os.Stdout
// WithOutput overrides default output os.Stdout.
func WithOutput(w io.Writer) ProgressOption {
return func(s *pState) {
if w == nil {

View File

@ -1,15 +0,0 @@
//+build go1.7
package mpb
import "context"
// WithContext provided context will be used for cancellation purposes
func WithContext(ctx context.Context) ProgressOption {
return func(s *pState) {
if ctx == nil {
panic("ctx must not be nil")
}
s.cancel = ctx.Done()
}
}

View File

@ -2,6 +2,7 @@ package mpb
import (
"container/heap"
"context"
"fmt"
"io"
"io/ioutil"
@ -17,8 +18,6 @@ const (
prr = 120 * time.Millisecond
// default width
pwidth = 80
// default format
pformat = "[=>-]"
)
// Progress represents the container that renders Progress bars
@ -42,24 +41,24 @@ type pState struct {
pMatrix map[int][]chan int
aMatrix map[int][]chan int
// following are provided by user
// following are provided/overrided by user
ctx context.Context
uwg *sync.WaitGroup
manualRefreshCh <-chan time.Time
cancel <-chan struct{}
shutdownNotifier chan struct{}
waitBars map[*Bar]*Bar
debugOut io.Writer
}
// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
// New creates new Progress instance, which orchestrates bars rendering
// process. Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
pq := make(priorityQueue, 0)
heap.Init(&pq)
s := &pState{
ctx: context.Background(),
bHeap: &pq,
width: pwidth,
format: pformat,
cw: cwriter.New(os.Stdout),
rr: prr,
waitBars: make(map[*Bar]*Bar),
@ -84,12 +83,28 @@ func New(options ...ProgressOption) *Progress {
// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
return p.Add(total, newDefaultBarFiller(), options...)
}
// AddSpinner creates a new spinner bar and adds to the container.
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
filler := &spinnerFiller{
frames: defaultSpinnerStyle,
alignment: alignment,
}
return p.Add(total, filler, options...)
}
// Add creates a bar which renders itself by provided filler.
func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar {
if filler == nil {
filler = newDefaultBarFiller()
}
p.wg.Add(1)
result := make(chan *Bar)
select {
case p.operateState <- func(s *pState) {
options = append(options, barWidth(s.width), barFormat(s.format))
b := newBar(p.wg, s.idCounter, total, s.cancel, options...)
b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...)
if b.runningBar != nil {
s.waitBars[b.runningBar] = b
} else {
@ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
}
}
// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
// Abort is only effective while bar progress is running, it means
// remove bar now without waiting for its completion. If bar is already
// completed, there is nothing to abort. If you need to remove bar
// after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
select {
case p.operateState <- func(s *pState) {
@ -145,9 +160,10 @@ func (p *Progress) BarCount() int {
}
}
// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
// Wait first waits for user provided *sync.WaitGroup, if any, then
// waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
if p.uwg != nil {
p.uwg.Wait()
@ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error {
defer func() {
if frameReader.toShutdown {
// shutdown at next flush, in other words decrement underlying WaitGroup
// only after the bar with completed state has been flushed.
// this ensures no bar ends up with less than 100% rendered.
// only after the bar with completed state has been flushed. this
// ensures no bar ends up with less than 100% rendered.
s.shutdownPending = append(s.shutdownPending, bar)
if replacementBar, ok := s.waitBars[bar]; ok {
heap.Push(s.bHeap, replacementBar)

48
vendor/github.com/vbauerster/mpb/spinner_filler.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package mpb
import (
"io"
"strings"
"unicode/utf8"
"github.com/vbauerster/mpb/decor"
)
// SpinnerAlignment enum.
type SpinnerAlignment int
// SpinnerAlignment kinds.
const (
SpinnerOnLeft SpinnerAlignment = iota
SpinnerOnMiddle
SpinnerOnRight
)
var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
type spinnerFiller struct {
frames []string
count uint
alignment SpinnerAlignment
}
func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
frame := s.frames[s.count%uint(len(s.frames))]
frameWidth := utf8.RuneCountInString(frame)
if width < frameWidth {
return
}
switch rest := width - frameWidth; s.alignment {
case SpinnerOnLeft:
io.WriteString(w, frame+strings.Repeat(" ", rest))
case SpinnerOnMiddle:
str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
io.WriteString(w, str)
case SpinnerOnRight:
io.WriteString(w, strings.Repeat(" ", rest)+frame)
}
s.count++
}

4
vendor/modules.txt vendored
View File

@ -26,7 +26,7 @@ github.com/VividCortex/ewma
github.com/containerd/continuity/pathdriver
# github.com/containers/buildah v1.8.4
github.com/containers/buildah/pkg/unshare
# github.com/containers/image v1.5.2-0.20190620105408-93b1deece293
# github.com/containers/image v1.5.2-0.20190717062552-2178abd5f9b1
github.com/containers/image/copy
github.com/containers/image/directory
github.com/containers/image/docker
@ -226,7 +226,7 @@ github.com/urfave/cli
github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage
github.com/vbatts/tar-split/archive/tar
# github.com/vbauerster/mpb v3.3.4+incompatible
# github.com/vbauerster/mpb v3.4.0+incompatible
github.com/vbauerster/mpb
github.com/vbauerster/mpb/decor
github.com/vbauerster/mpb/cwriter