From baa5d8cbbf25d5ae8ff80c28873234dcfa74892a Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Fri, 24 May 2024 16:33:55 -0700 Subject: [PATCH 01/18] feat: Use progress bar to display docker output --- go.mod | 12 +++++---- go.sum | 13 ++++++++++ internal/cmd/docker/push.go | 49 +++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 8adea1fbb..ab93e3de6 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/go.sum b/go.sum index 588096369..1adc31d51 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= @@ -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= @@ -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= @@ -577,6 +585,7 @@ 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= @@ -584,10 +593,14 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc 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= diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index d41f8f379..6e817cd90 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -7,15 +7,16 @@ import ( "errors" "fmt" "io" - "os" "time" "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/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" @@ -96,11 +97,49 @@ 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 status string + var msg dockerMsg.JSONMessage + var bar *progressbar.ProgressBar + + decoder := json.NewDecoder(reader) + for { + // Decode the message from the Docker API output. + 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 bar to display progress whenever the Docker push status changes. + if status != msg.Status { + status = msg.Status + // Create a spinner-based progress bar for statuses other than 'pushing', like 'prepare'. + if msg.Progress == nil || msg.Progress.Total == 0 { + bar = progressbar.Default(-1, status) + continue + } + // Create a new progress bar for 'pushing' status with total bytes. + bar = progressbar.Default(msg.Progress.Total, status) + } + + // Update current progress based on msg.Progress.Total when in 'pushing' status. + if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 { + bar.Set64(msg.Progress.Current) + } } + if bar != nil { + bar.Finish() + } + fmt.Println("\nSuccessfully pushed the Docker image!") return nil } From d02744b999ee71b8f65013127ec78a3b0f5694cb Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Fri, 24 May 2024 16:37:30 -0700 Subject: [PATCH 02/18] make linter happy --- internal/cmd/docker/push.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 6e817cd90..44061f4cc 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -133,12 +133,16 @@ func logPushProgress(reader io.ReadCloser) error { // Update current progress based on msg.Progress.Total when in 'pushing' status. if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 { - bar.Set64(msg.Progress.Current) + if err := bar.Set64(msg.Progress.Current); err != nil { + return err + } } } if bar != nil { - bar.Finish() + if err := bar.Finish(); err != nil { + return err + } } fmt.Println("\nSuccessfully pushed the Docker image!") return nil From 5c73e44fdb5c2d192e15fff907d4efcb49b0f400 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Sun, 26 May 2024 14:30:55 -0700 Subject: [PATCH 03/18] construct docker own progress bar --- internal/cmd/docker/push.go | 46 +++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 44061f4cc..c5a64c428 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -7,12 +7,13 @@ import ( "errors" "fmt" "io" + "os" "time" "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" + dockermsg "github.com/moby/moby/pkg/jsonmessage" cmds "github.com/saucelabs/saucectl/internal/cmd" "github.com/saucelabs/saucectl/internal/segment" "github.com/saucelabs/saucectl/internal/usage" @@ -102,7 +103,7 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration func logPushProgress(reader io.ReadCloser) error { var status string - var msg dockerMsg.JSONMessage + var msg dockermsg.JSONMessage var bar *progressbar.ProgressBar decoder := json.NewDecoder(reader) @@ -122,18 +123,19 @@ func logPushProgress(reader io.ReadCloser) error { // Create a new progress bar to display progress whenever the Docker push status changes. if status != msg.Status { status = msg.Status - // Create a spinner-based progress bar for statuses other than 'pushing', like 'prepare'. - if msg.Progress == nil || msg.Progress.Total == 0 { - bar = progressbar.Default(-1, status) - continue + if bar != nil { + // Finish the previous progress bar. + if err := bar.Finish(); err != nil { + return err + } } - // Create a new progress bar for 'pushing' status with total bytes. - bar = progressbar.Default(msg.Progress.Total, status) + // Set an arbitrary maximum value as the number of steps per status is unknown. + bar = createBar(100, status) + continue } - // Update current progress based on msg.Progress.Total when in 'pushing' status. - if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 { - if err := bar.Set64(msg.Progress.Current); err != nil { + if bar != nil { + if err := bar.Add(1); err != nil { return err } } @@ -144,6 +146,26 @@ func logPushProgress(reader io.ReadCloser) error { return err } } - fmt.Println("\nSuccessfully pushed the Docker image!") + fmt.Println("Successfully pushed the Docker image!") return nil } + +func createBar(max int64, desc string) *progressbar.ProgressBar { + return progressbar.NewOptions64( + max, + progressbar.OptionSetDescription(desc), + progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionOnCompletion(func() { + fmt.Fprint(os.Stderr, "\n") + }), + progressbar.OptionFullWidth(), + progressbar.OptionSetRenderBlankState(true), + progressbar.OptionSetTheme(progressbar.Theme{ + Saucer: "=", + SaucerHead: ">", + SaucerPadding: " ", + BarStart: "[", + BarEnd: ">]", + }), + ) +} From 9f410ebbb57c5163b0284908b64cf18250567d67 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 10:16:51 -0700 Subject: [PATCH 04/18] wrap bar functions --- internal/cmd/docker/push.go | 38 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index c5a64c428..5efc8c707 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -123,33 +123,29 @@ func logPushProgress(reader io.ReadCloser) error { // Create a new progress bar to display progress whenever the Docker push status changes. if status != msg.Status { status = msg.Status - if bar != nil { - // Finish the previous progress bar. - if err := bar.Finish(); err != nil { - return err - } + // Finish the previous progress bar. + if err := finishBar(bar); err != nil { + return err } // Set an arbitrary maximum value as the number of steps per status is unknown. bar = createBar(100, status) continue } - if bar != nil { - if err := bar.Add(1); err != nil { - return err - } + if err := addToBar(bar, 1); err != nil { + return err } } - if bar != nil { - if err := bar.Finish(); err != nil { - return err - } + if err := finishBar(bar); 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, @@ -169,3 +165,19 @@ func createBar(max int64, desc string) *progressbar.ProgressBar { }), ) } + +// finishBar completes a progress bar by setting its progress to 100%. +func finishBar(bar *progressbar.ProgressBar) error { + if bar == nil { + return nil + } + return bar.Finish() +} + +// addToBar increments the progress on the progress bar by a specified amount. +func addToBar(bar *progressbar.ProgressBar, inc int) error { + if bar == nil { + return nil // Skip increment if the progress bar is not initialized + } + return bar.Add(inc) +} From badfb5b9ef84f55813b6790c26740802838b1372 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 10:51:22 -0700 Subject: [PATCH 05/18] use another spinner --- internal/cmd/docker/push.go | 57 ++----------------------------------- 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 5efc8c707..712591331 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "os" "time" "github.com/docker/docker/api/types" @@ -15,9 +14,9 @@ import ( "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" @@ -104,7 +103,6 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration func logPushProgress(reader io.ReadCloser) error { var status string var msg dockermsg.JSONMessage - var bar *progressbar.ProgressBar decoder := json.NewDecoder(reader) for { @@ -120,64 +118,15 @@ func logPushProgress(reader io.ReadCloser) error { return fmt.Errorf("server error during Docker image push: %s", msg.Error.Message) } - // Create a new progress bar to display progress whenever the Docker push status changes. + // Create a new progress spinner to display progress whenever the Docker push status changes. if status != msg.Status { status = msg.Status - // Finish the previous progress bar. - if err := finishBar(bar); err != nil { - return err - } - // Set an arbitrary maximum value as the number of steps per status is unknown. - bar = createBar(100, status) + progress.Show(status) continue } - if err := addToBar(bar, 1); err != nil { - return err - } - } - - if err := finishBar(bar); 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.OptionOnCompletion(func() { - fmt.Fprint(os.Stderr, "\n") - }), - progressbar.OptionFullWidth(), - progressbar.OptionSetRenderBlankState(true), - progressbar.OptionSetTheme(progressbar.Theme{ - Saucer: "=", - SaucerHead: ">", - SaucerPadding: " ", - BarStart: "[", - BarEnd: ">]", - }), - ) -} - -// finishBar completes a progress bar by setting its progress to 100%. -func finishBar(bar *progressbar.ProgressBar) error { - if bar == nil { - return nil - } - return bar.Finish() -} - -// addToBar increments the progress on the progress bar by a specified amount. -func addToBar(bar *progressbar.ProgressBar, inc int) error { - if bar == nil { - return nil // Skip increment if the progress bar is not initialized - } - return bar.Add(inc) -} From a63f75232d5edce4dbad36696b7e3373b9c9032d Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 11:00:44 -0700 Subject: [PATCH 06/18] cleanup --- internal/cmd/docker/push.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 712591331..5fcc51eb8 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -122,9 +122,7 @@ func logPushProgress(reader io.ReadCloser) error { if status != msg.Status { status = msg.Status progress.Show(status) - continue } - } fmt.Println("Successfully pushed the Docker image!") From 15215d41def48d7a2daaae3c1d3b379bc322c6cb Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 13:36:26 -0700 Subject: [PATCH 07/18] use spinner and bar both --- internal/cmd/docker/push.go | 64 +++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 5fcc51eb8..87ef16996 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "os" "time" "github.com/docker/docker/api/types" @@ -17,6 +18,7 @@ import ( "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" @@ -102,11 +104,12 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration func logPushProgress(reader io.ReadCloser) error { var status string - var msg dockermsg.JSONMessage + var bar *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 { @@ -118,13 +121,68 @@ func logPushProgress(reader io.ReadCloser) error { return fmt.Errorf("server error during Docker image push: %s", msg.Error.Message) } - // Create a new progress spinner to display progress whenever the Docker push status changes. + // Create a new progress spinner or bar to display progress whenever the Docker push status changes. if status != msg.Status { + if err := clearProgress(bar); err != nil { + return err + } + status = msg.Status - progress.Show(status) + // Create a progress spinner for statuses other than 'Pushing', like 'Preparing'. + if msg.Progress == nil || msg.Progress.Total == 0 { + progress.Show(status) + continue + } + + // Create a progress bar for 'Pushing' status with total bytes. + bar = createBar(msg.Progress.Total, status) } + + // 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 the total. + if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { + _ = bar.Set64(msg.Progress.Current) + } + } + if err := clearProgress(bar); 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: ">]", + }), + ) +} + +// clearProgress stops the previous progress spinner or bar. +func clearProgress(bar *progressbar.ProgressBar) error { + // Stop the progress spinner. + progress.Stop() + + // Finish the progress bar by setting it to 100%. + if bar == nil { + return nil + } + return bar.Finish() +} From 2750c0e129752545a362b97884089f52c8943498 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 13:40:20 -0700 Subject: [PATCH 08/18] update condition --- internal/cmd/docker/push.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 87ef16996..46fb059a7 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -141,7 +141,7 @@ func logPushProgress(reader io.ReadCloser) error { // 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 the total. - if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { + if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current <= msg.Progress.Total { _ = bar.Set64(msg.Progress.Current) } } From ac21a7b9bcec54363b9d32fb63efe23a956b1c34 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 13:41:12 -0700 Subject: [PATCH 09/18] refine comment --- internal/cmd/docker/push.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 46fb059a7..92183d456 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -140,7 +140,7 @@ func logPushProgress(reader io.ReadCloser) error { // 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 the total. + // only update when the current is not greater than the total. if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current <= msg.Progress.Total { _ = bar.Set64(msg.Progress.Current) } From 4eab55acc9e38d6b41bc2dde7d37d2f0325df436 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 14:19:21 -0700 Subject: [PATCH 10/18] update the condition --- internal/cmd/docker/push.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 92183d456..87ef16996 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -140,8 +140,8 @@ func logPushProgress(reader io.ReadCloser) error { // 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 not greater than the total. - if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current <= msg.Progress.Total { + // only update when the current is less than the total. + if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { _ = bar.Set64(msg.Progress.Current) } } From 24b900bee6355503ae103109f9fd0fa4556068cc Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 14:22:37 -0700 Subject: [PATCH 11/18] revert bar to nil --- internal/cmd/docker/push.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 87ef16996..1a27fe879 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -184,5 +184,9 @@ func clearProgress(bar *progressbar.ProgressBar) error { if bar == nil { return nil } - return bar.Finish() + if err := bar.Finish(); err != nil { + return err + } + bar = nil + return nil } From 30e76edaf254709981f4d42d41502740fb225389 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 14:25:10 -0700 Subject: [PATCH 12/18] add comment --- internal/cmd/docker/push.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 1a27fe879..a39a6400a 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -180,13 +180,14 @@ func clearProgress(bar *progressbar.ProgressBar) error { // Stop the progress spinner. progress.Stop() - // Finish the progress bar by setting it to 100%. if bar == nil { return nil } + // Finish the progress bar by setting it to 100%. if err := bar.Finish(); err != nil { return err } + // Restore progress bar to nil. bar = nil return nil } From 0e77720b8f3bacb3362b76f6d60231e910c3a76a Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 15:29:43 -0700 Subject: [PATCH 13/18] identify each by id instead of status --- internal/cmd/docker/push.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index a39a6400a..2fae890b9 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -103,7 +103,7 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration } func logPushProgress(reader io.ReadCloser) error { - var status string + var stepID string var bar *progressbar.ProgressBar decoder := json.NewDecoder(reader) @@ -121,26 +121,27 @@ func logPushProgress(reader io.ReadCloser) error { return fmt.Errorf("server error during Docker image push: %s", msg.Error.Message) } - // Create a new progress spinner or bar to display progress whenever the Docker push status changes. - if status != msg.Status { + // Create a new progress spinner or bar to display progress whenever the Docker push ID changes. + // Each ID represents a push step. + if stepID != msg.ID { + stepID = msg.ID if err := clearProgress(bar); err != nil { return err } - status = msg.Status // Create a progress spinner for statuses other than 'Pushing', like 'Preparing'. if msg.Progress == nil || msg.Progress.Total == 0 { - progress.Show(status) + progress.Show(msg.Status) continue } // Create a progress bar for 'Pushing' status with total bytes. - bar = createBar(msg.Progress.Total, status) + bar = createBar(msg.Progress.Total, msg.Status) } // 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 the total. + // only update when the current is less than msg.Progress.Total. if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { _ = bar.Set64(msg.Progress.Current) } @@ -184,10 +185,5 @@ func clearProgress(bar *progressbar.ProgressBar) error { return nil } // Finish the progress bar by setting it to 100%. - if err := bar.Finish(); err != nil { - return err - } - // Restore progress bar to nil. - bar = nil - return nil + return bar.Finish() } From fe2bc159c95004bc6ebe8bd184c403a61372fe07 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 15:35:24 -0700 Subject: [PATCH 14/18] revise comment --- internal/cmd/docker/push.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 2fae890b9..250943cd6 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -139,7 +139,7 @@ func logPushProgress(reader io.ReadCloser) error { bar = createBar(msg.Progress.Total, msg.Status) } - // Update current progress based on msg.Progress.Total when in 'pushing' status. + // 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 msg.Progress.Total. if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { From 659ccafc89915dfc43031c19ea5adc390a9b5c12 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 15:45:12 -0700 Subject: [PATCH 15/18] revise comment --- internal/cmd/docker/push.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 250943cd6..60c0b913f 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -141,8 +141,8 @@ func logPushProgress(reader io.ReadCloser) error { // 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 msg.Progress.Total. - if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < msg.Progress.Total { + // only update when the current is less than total. + if bar != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < bar.GetMax64() { _ = bar.Set64(msg.Progress.Current) } } From edab6b5676681279993beef17f6874b0e1eb6692 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 16:13:39 -0700 Subject: [PATCH 16/18] Speed estimation is incorrect when in parallel. --- internal/cmd/docker/push.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 60c0b913f..157aeacdf 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -121,8 +121,9 @@ func logPushProgress(reader io.ReadCloser) error { return fmt.Errorf("server error during Docker image push: %s", msg.Error.Message) } - // Create a new progress spinner or bar to display progress whenever the Docker push ID changes. - // Each ID represents a push step. + // 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 if err := clearProgress(bar); err != nil { @@ -160,7 +161,6 @@ func createBar(max int64, desc string) *progressbar.ProgressBar { max, progressbar.OptionSetDescription(desc), progressbar.OptionSetWriter(os.Stderr), - progressbar.OptionShowBytes(true), progressbar.OptionOnCompletion(func() { fmt.Fprint(os.Stderr, "\n") }), From 28d82c3f4e8166a9d43a1b83b1fd36d8ab841b64 Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Mon, 27 May 2024 16:57:51 -0700 Subject: [PATCH 17/18] group output by ID --- internal/cmd/docker/push.go | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 157aeacdf..0a7c48c77 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -104,8 +104,7 @@ func pushDockerImage(imageName, username, password string, timeout time.Duration func logPushProgress(reader io.ReadCloser) error { var stepID string - var bar *progressbar.ProgressBar - + bars := map[string]*progressbar.ProgressBar{} decoder := json.NewDecoder(reader) for { // Decode the message from the Docker API output. @@ -126,28 +125,30 @@ func logPushProgress(reader io.ReadCloser) error { // Note: Outputs are in parallel; identical IDs indicate outputs from the same thread. if stepID != msg.ID { stepID = msg.ID - if err := clearProgress(bar); err != nil { - return err - } + progress.Stop() - // Create a progress spinner for statuses other than 'Pushing', like 'Preparing'. + // Create a progress spinner for statuses don't have progress details, like 'Preparing'. if msg.Progress == nil || msg.Progress.Total == 0 { progress.Show(msg.Status) continue } - // Create a progress bar for 'Pushing' status with total bytes. - bar = createBar(msg.Progress.Total, msg.Status) + // 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 != nil && msg.Progress != nil && msg.Progress.Current > 0 && msg.Progress.Current < bar.GetMax64() { - _ = bar.Set64(msg.Progress.Current) + 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 := clearProgress(bar); err != nil { + if err := closeProgress(bars); err != nil { return err } @@ -161,6 +162,7 @@ func createBar(max int64, desc string) *progressbar.ProgressBar { max, progressbar.OptionSetDescription(desc), progressbar.OptionSetWriter(os.Stderr), + progressbar.OptionShowBytes(true), progressbar.OptionOnCompletion(func() { fmt.Fprint(os.Stderr, "\n") }), @@ -176,14 +178,13 @@ func createBar(max int64, desc string) *progressbar.ProgressBar { ) } -// clearProgress stops the previous progress spinner or bar. -func clearProgress(bar *progressbar.ProgressBar) error { - // Stop the progress spinner. +// closeProgress closes all progress spinners and bars. +func closeProgress(bars map[string]*progressbar.ProgressBar) error { progress.Stop() - - if bar == nil { - return nil + for _, bar := range bars { + if err := bar.Finish(); err != nil { + return err + } } - // Finish the progress bar by setting it to 100%. - return bar.Finish() + return nil } From fd0efd1a32eddef7ae2cd6d48a7238c5eb51c4cf Mon Sep 17 00:00:00 2001 From: Tian Feng Date: Tue, 28 May 2024 10:01:19 -0700 Subject: [PATCH 18/18] Update internal/cmd/docker/push.go Co-authored-by: Alex Plischke --- internal/cmd/docker/push.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/docker/push.go b/internal/cmd/docker/push.go index 0a7c48c77..7fdce3875 100644 --- a/internal/cmd/docker/push.go +++ b/internal/cmd/docker/push.go @@ -127,7 +127,7 @@ func logPushProgress(reader io.ReadCloser) error { stepID = msg.ID progress.Stop() - // Create a progress spinner for statuses don't have progress details, like 'Preparing'. + // 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