Skip to content

Commit

Permalink
feat: Use progress bar to display docker output (#913)
Browse files Browse the repository at this point in the history
* feat: Use progress bar to display docker output

* make linter happy

* construct docker own progress bar

* wrap bar functions

* use another spinner

* cleanup

* use spinner and bar both

* update condition

* refine comment

* update the condition

* revert bar to nil

* add comment

* identify each by id instead of status

* revise comment

* revise comment

* Speed estimation is incorrect when in parallel.

* group output by ID

* Update internal/cmd/docker/push.go

Co-authored-by: Alex Plischke <[email protected]>

---------

Co-authored-by: Alex Plischke <[email protected]>
  • Loading branch information
tianfeng92 and alexplischke authored May 28, 2024
1 parent e0a8843 commit 77a9777
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 9 deletions.
12 changes: 7 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/rs/zerolog v1.18.0
github.com/ryanuber/go-glob v1.0.0
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
github.com/schollz/progressbar/v3 v3.13.1
github.com/schollz/progressbar/v3 v3.14.3
github.com/slack-go/slack v0.9.1
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
Expand All @@ -33,6 +33,7 @@ require (
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/distribution/reference v0.5.0 // indirect
Expand All @@ -43,6 +44,7 @@ require (
github.com/google/uuid v1.1.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/moby/moby v26.1.3+incompatible // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand All @@ -51,7 +53,7 @@ require (
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect
Expand Down Expand Up @@ -81,7 +83,7 @@ require (
github.com/kr/pty v1.1.5 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.18
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/mapstructure v1.5.0
Expand All @@ -93,8 +95,8 @@ require (
github.com/subosito/gotenv v1.4.1 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.14.0
golang.org/x/time v0.3.0
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand All @@ -291,6 +293,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/moby v26.1.3+incompatible h1:gIzra6kadTUzPUZWpyUfkaLKymz9I8gANMB1NKk2pF0=
github.com/moby/moby v26.1.3+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down Expand Up @@ -326,6 +330,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
Expand All @@ -343,6 +349,8 @@ github.com/saucelabs/viper v1.14.0 h1:GEkA1eBJfErquPWugtfZ9+7ccbFTalIFxquB/HbVQ1
github.com/saucelabs/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 h1:ZuhckGJ10ulaKkdvJtiAqsLTiPrLaXSdnVgXJKJkTxE=
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
Expand Down Expand Up @@ -577,17 +585,22 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
92 changes: 88 additions & 4 deletions internal/cmd/docker/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
dockermsg "github.com/moby/moby/pkg/jsonmessage"
cmds "github.com/saucelabs/saucectl/internal/cmd"
"github.com/saucelabs/saucectl/internal/progress"
"github.com/saucelabs/saucectl/internal/segment"
"github.com/saucelabs/saucectl/internal/usage"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
Expand Down Expand Up @@ -96,11 +99,92 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration
return nil
}

// Print the push output
_, err = io.Copy(os.Stdout, out)
if err != nil {
return fmt.Errorf("docker output: %v", err)
return logPushProgress(out)
}

func logPushProgress(reader io.ReadCloser) error {
var stepID string
bars := map[string]*progressbar.ProgressBar{}
decoder := json.NewDecoder(reader)
for {
// Decode the message from the Docker API output.
var msg dockermsg.JSONMessage
err := decoder.Decode(&msg)
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("failed to decode JSON during Docker image push: %v", err)
}
if msg.Error != nil {
return fmt.Errorf("server error during Docker image push: %s", msg.Error.Message)
}

// Create a new progress spinner or bar when the Docker push ID changes.
// Each ID corresponds to a distinct step in the push process.
// Note: Outputs are in parallel; identical IDs indicate outputs from the same thread.
if stepID != msg.ID {
stepID = msg.ID
progress.Stop()

// Create a progress spinner for statuses that don't have progress details, like 'Preparing'.
if msg.Progress == nil || msg.Progress.Total == 0 {
progress.Show(msg.Status)
continue
}

// Init a progress bar for 'Pushing' status with total bytes and add it to bar group.
if _, ok := bars[msg.ID]; !ok {
bars[msg.ID] = createBar(msg.Progress.Total, fmt.Sprintf("%s %s", msg.Status, msg.ID))
}
}

// Update current progress based on msg.Progress.Total when in 'Pushing' status.
// Note: The Docker API may return a current value greater than total. To prevent breaking the progress bar,
// only update when the current is less than total.
if bar, ok := bars[msg.ID]; ok {
if msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < bar.GetMax64() {
_ = bar.Set64(msg.Progress.Current)
}
}
}
if err := closeProgress(bars); err != nil {
return err
}

fmt.Println("Successfully pushed the Docker image!")
return nil
}

// createBar returns a customized progress bar for Docker image pushes.
func createBar(max int64, desc string) *progressbar.ProgressBar {
return progressbar.NewOptions64(
max,
progressbar.OptionSetDescription(desc),
progressbar.OptionSetWriter(os.Stderr),
progressbar.OptionShowBytes(true),
progressbar.OptionOnCompletion(func() {
fmt.Fprint(os.Stderr, "\n")
}),
progressbar.OptionFullWidth(),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "=",
SaucerHead: ">",
SaucerPadding: " ",
BarStart: "[",
BarEnd: ">]",
}),
)
}

// closeProgress closes all progress spinners and bars.
func closeProgress(bars map[string]*progressbar.ProgressBar) error {
progress.Stop()
for _, bar := range bars {
if err := bar.Finish(); err != nil {
return err
}
}
return nil
}

0 comments on commit 77a9777

Please sign in to comment.