cp: Reduce number of progress updates

Only show progress updates after a time threshold has elapsed in order
to reduce the number of writes to the terminal.
This improves readability of the progress.

Also moves cursor show/hide into the progress printer to reduce chances
if messing up the user's terminal in case of cancellation.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff 2023-03-30 00:32:43 +00:00
parent efd011b793
commit 90b7bc36d4
1 changed files with 22 additions and 16 deletions

View File

@ -7,6 +7,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
@ -49,15 +50,17 @@ type cpConfig struct {
// copying files to/from a container. // copying files to/from a container.
type copyProgressPrinter struct { type copyProgressPrinter struct {
io.ReadCloser io.ReadCloser
total *float64 total *float64
writer io.Writer writer io.Writer
isTerm bool isTerm bool
header string header string
lastUpdate time.Time
} }
const ( const (
copyToContainerHeader = "Copying to container - " copyToContainerHeader = "Copying to container - "
copyFromContainerHeader = "Copying from container - " copyFromContainerHeader = "Copying from container - "
copyProgressUpdateThreshold = 75 * time.Millisecond
) )
func (pt *copyProgressPrinter) Read(p []byte) (int, error) { func (pt *copyProgressPrinter) Read(p []byte) (int, error) {
@ -66,23 +69,30 @@ func (pt *copyProgressPrinter) Read(p []byte) (int, error) {
if n > 0 { if n > 0 {
*pt.total += float64(n) *pt.total += float64(n)
} }
if err != nil && err != io.EOF { if !pt.isTerm {
return n, err return n, err
} }
if !pt.isTerm { doUpdate := func() {
return n, err fmt.Fprint(pt.writer, aec.Hide)
fmt.Fprint(pt.writer, aec.Column(uint(len(pt.header)+1)))
fmt.Fprint(pt.writer, aec.EraseLine(aec.EraseModes.Tail))
fmt.Fprint(pt.writer, units.HumanSize(*pt.total))
fmt.Fprint(pt.writer, aec.Show)
pt.lastUpdate = time.Now()
} }
if isFirst { if isFirst {
fmt.Fprint(pt.writer, aec.Restore) fmt.Fprint(pt.writer, aec.Restore)
fmt.Fprint(pt.writer, aec.EraseLine(aec.EraseModes.All)) fmt.Fprint(pt.writer, aec.EraseLine(aec.EraseModes.All))
fmt.Fprint(pt.writer, pt.header) fmt.Fprint(pt.writer, pt.header)
doUpdate()
return n, err
} }
fmt.Fprint(pt.writer, aec.Column(uint(len(pt.header)+1)))
fmt.Fprint(pt.writer, aec.EraseLine(aec.EraseModes.Tail))
fmt.Fprint(pt.writer, units.HumanSize(*pt.total))
if err != nil || time.Since(pt.lastUpdate) > copyProgressUpdateThreshold {
doUpdate()
}
return n, err return n, err
} }
@ -251,7 +261,6 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
} }
if stderrIsTerm { if stderrIsTerm {
fmt.Fprint(dockerCli.Err(), aec.Hide)
fmt.Fprint(dockerCli.Err(), aec.Save) fmt.Fprint(dockerCli.Err(), aec.Save)
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...") fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
} }
@ -259,7 +268,6 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp
if stderrIsTerm { if stderrIsTerm {
fmt.Fprint(dockerCli.Err(), aec.Restore) fmt.Fprint(dockerCli.Err(), aec.Restore)
fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All)) fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All))
fmt.Fprint(dockerCli.Err(), aec.Show)
} }
fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", dstPath) fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", dstPath)
@ -383,7 +391,6 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
} }
if stderrIsTerm { if stderrIsTerm {
fmt.Fprint(dockerCli.Err(), aec.Hide)
fmt.Fprint(dockerCli.Err(), aec.Save) fmt.Fprint(dockerCli.Err(), aec.Save)
fmt.Fprintln(dockerCli.Err(), "Preparing to copy...") fmt.Fprintln(dockerCli.Err(), "Preparing to copy...")
} }
@ -391,7 +398,6 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
if stderrIsTerm { if stderrIsTerm {
fmt.Fprint(dockerCli.Err(), aec.Restore) fmt.Fprint(dockerCli.Err(), aec.Restore)
fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All)) fmt.Fprint(dockerCli.Err(), aec.EraseLine(aec.EraseModes.All))
fmt.Fprint(dockerCli.Err(), aec.Show)
} }
fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path) fmt.Fprintln(dockerCli.Err(), "Successfully copied", units.HumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)