4593d0a641
- script/vendor received an update in order to work with Glide - import paths have been rewritten to work with GO15VENDOREXPERIMENT
168 lines
4.4 KiB
Go
168 lines
4.4 KiB
Go
package progress
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/olekukonko/ts"
|
|
)
|
|
|
|
// ProgressMeter provides a progress bar type output for the TransferQueue. It
|
|
// is given an estimated file count and size up front and tracks the number of
|
|
// files and bytes transferred as well as the number of files and bytes that
|
|
// get skipped because the transfer is unnecessary.
|
|
type ProgressMeter struct {
|
|
finishedFiles int64 // int64s must come first for struct alignment
|
|
skippedFiles int64
|
|
transferringFiles int64
|
|
estimatedBytes int64
|
|
currentBytes int64
|
|
skippedBytes int64
|
|
started int32
|
|
estimatedFiles int
|
|
startTime time.Time
|
|
finished chan interface{}
|
|
logger *progressLogger
|
|
fileIndex map[string]int64 // Maps a file name to its transfer number
|
|
fileIndexMutex *sync.Mutex
|
|
dryRun bool
|
|
}
|
|
|
|
// NewProgressMeter creates a new ProgressMeter for the number and size of
|
|
// files given.
|
|
func NewProgressMeter(estFiles int, estBytes int64, dryRun bool, logPath string) *ProgressMeter {
|
|
logger, err := newProgressLogger(logPath)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error creating progress logger: %s\n", err)
|
|
}
|
|
|
|
return &ProgressMeter{
|
|
logger: logger,
|
|
startTime: time.Now(),
|
|
fileIndex: make(map[string]int64),
|
|
fileIndexMutex: &sync.Mutex{},
|
|
finished: make(chan interface{}),
|
|
estimatedFiles: estFiles,
|
|
estimatedBytes: estBytes,
|
|
dryRun: dryRun,
|
|
}
|
|
}
|
|
|
|
func (p *ProgressMeter) Start() {
|
|
if atomic.SwapInt32(&p.started, 1) == 0 {
|
|
go p.writer()
|
|
}
|
|
}
|
|
|
|
// Add tells the progress meter that a transferring file is being added to the
|
|
// TransferQueue.
|
|
func (p *ProgressMeter) Add(name string) {
|
|
idx := atomic.AddInt64(&p.transferringFiles, 1)
|
|
p.fileIndexMutex.Lock()
|
|
p.fileIndex[name] = idx
|
|
p.fileIndexMutex.Unlock()
|
|
}
|
|
|
|
// Skip tells the progress meter that a file of size `size` is being skipped
|
|
// because the transfer is unnecessary.
|
|
func (p *ProgressMeter) Skip(size int64) {
|
|
atomic.AddInt64(&p.skippedFiles, 1)
|
|
atomic.AddInt64(&p.skippedBytes, size)
|
|
}
|
|
|
|
// TransferBytes increments the number of bytes transferred
|
|
func (p *ProgressMeter) TransferBytes(direction, name string, read, total int64, current int) {
|
|
atomic.AddInt64(&p.currentBytes, int64(current))
|
|
p.logBytes(direction, name, read, total)
|
|
}
|
|
|
|
// FinishTransfer increments the finished transfer count
|
|
func (p *ProgressMeter) FinishTransfer(name string) {
|
|
atomic.AddInt64(&p.finishedFiles, 1)
|
|
p.fileIndexMutex.Lock()
|
|
delete(p.fileIndex, name)
|
|
p.fileIndexMutex.Unlock()
|
|
}
|
|
|
|
// Finish shuts down the ProgressMeter
|
|
func (p *ProgressMeter) Finish() {
|
|
close(p.finished)
|
|
p.update()
|
|
p.logger.Close()
|
|
if !p.dryRun && p.estimatedBytes > 0 {
|
|
fmt.Fprintf(os.Stdout, "\n")
|
|
}
|
|
}
|
|
|
|
func (p *ProgressMeter) logBytes(direction, name string, read, total int64) {
|
|
p.fileIndexMutex.Lock()
|
|
idx := p.fileIndex[name]
|
|
p.fileIndexMutex.Unlock()
|
|
line := fmt.Sprintf("%s %d/%d %d/%d %s\n", direction, idx, p.estimatedFiles, read, total, name)
|
|
if err := p.logger.Write([]byte(line)); err != nil {
|
|
p.logger.Shutdown()
|
|
}
|
|
}
|
|
|
|
func (p *ProgressMeter) writer() {
|
|
p.update()
|
|
for {
|
|
select {
|
|
case <-p.finished:
|
|
return
|
|
case <-time.After(time.Millisecond * 200):
|
|
p.update()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *ProgressMeter) update() {
|
|
if p.dryRun || p.estimatedFiles == 0 {
|
|
return
|
|
}
|
|
|
|
width := 80 // default to 80 chars wide if ts.GetSize() fails
|
|
size, err := ts.GetSize()
|
|
if err == nil {
|
|
width = size.Col()
|
|
}
|
|
|
|
// (%d of %d files, %d skipped) %f B / %f B, %f B skipped
|
|
// skipped counts only show when > 0
|
|
|
|
out := fmt.Sprintf("\rGit LFS: (%d of %d files", p.finishedFiles, p.estimatedFiles)
|
|
if p.skippedFiles > 0 {
|
|
out += fmt.Sprintf(", %d skipped", p.skippedFiles)
|
|
}
|
|
out += fmt.Sprintf(") %s / %s", formatBytes(p.currentBytes), formatBytes(p.estimatedBytes))
|
|
if p.skippedBytes > 0 {
|
|
out += fmt.Sprintf(", %s skipped", formatBytes(p.skippedBytes))
|
|
}
|
|
|
|
padlen := width - len(out)
|
|
if 0 < padlen {
|
|
out += strings.Repeat(" ", padlen)
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, out)
|
|
}
|
|
|
|
func formatBytes(i int64) string {
|
|
switch {
|
|
case i > 1099511627776:
|
|
return fmt.Sprintf("%#0.2f TB", float64(i)/1099511627776)
|
|
case i > 1073741824:
|
|
return fmt.Sprintf("%#0.2f GB", float64(i)/1073741824)
|
|
case i > 1048576:
|
|
return fmt.Sprintf("%#0.2f MB", float64(i)/1048576)
|
|
case i > 1024:
|
|
return fmt.Sprintf("%#0.2f KB", float64(i)/1024)
|
|
}
|
|
|
|
return fmt.Sprintf("%d B", i)
|
|
}
|