github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runners/artifacts/progress.go (about) 1 package artifacts 2 3 import ( 4 "context" 5 "io" 6 "os" 7 8 "github.com/ActiveState/cli/internal/constants" 9 "github.com/ActiveState/cli/internal/locale" 10 "github.com/ActiveState/cli/internal/output" 11 "github.com/vbauerster/mpb/v7" 12 "github.com/vbauerster/mpb/v7/decor" 13 ) 14 15 type downloadProgress interface { 16 Start(size int64) 17 Inc(inc int) 18 Abort() 19 Stop() 20 } 21 22 type interactiveProgress struct { 23 pg *mpb.Progress 24 bar *mpb.Bar 25 artifactName string 26 artifactSize int 27 out output.Outputer 28 } 29 30 func newDownloadProgress(ctx context.Context, out output.Outputer, artifactName, downloadPath string) downloadProgress { 31 if !out.Config().Interactive { 32 return &nonInteractiveProgress{ 33 artifactName: artifactName, 34 downloadPath: downloadPath, 35 out: out, 36 } 37 } 38 39 var w io.Writer = os.Stdout 40 if out.Type() != output.PlainFormatName { 41 w = nil 42 } 43 pg := mpb.NewWithContext( 44 ctx, 45 mpb.WithOutput(w), 46 mpb.WithWidth(40), 47 mpb.WithRefreshRate(constants.TerminalAnimationInterval), 48 ) 49 50 return &interactiveProgress{ 51 pg: pg, 52 artifactName: artifactName, 53 out: out, 54 } 55 } 56 57 func (p *interactiveProgress) Start(size int64) { 58 p.out.Notice(locale.Tr("builds_dl_downloading", p.artifactName)) 59 prependDecorators := []decor.Decorator{ 60 decor.Name(locale.T("downloading")), 61 decor.OnComplete( 62 decor.Spinner(output.SpinnerFrames, decor.WCSyncSpace), "", 63 ), 64 decor.CountersKiloByte("%.1f/%.1f", decor.WC{W: 17}), 65 } 66 67 options := []mpb.BarOption{ 68 mpb.BarFillerClearOnComplete(), 69 mpb.PrependDecorators(prependDecorators...), 70 mpb.AppendDecorators( 71 decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""), 72 ), 73 } 74 75 p.artifactSize = int(size) 76 p.bar = p.pg.AddBar(size, options...) 77 } 78 79 func (p *interactiveProgress) Inc(inc int) { 80 p.bar.IncrBy(inc) 81 } 82 83 func (p *interactiveProgress) Abort() { 84 p.bar.Abort(true) 85 } 86 87 func (p *interactiveProgress) Stop() { 88 // The download bar should be complete at this point, but if it's not, we'll 89 // just set it to complete and let the progress bar finish. 90 if !p.bar.Completed() { 91 p.bar.IncrBy(p.artifactSize - int(p.bar.Current())) 92 } 93 p.pg.Wait() 94 } 95 96 type nonInteractiveProgress struct { 97 spinner *output.Spinner 98 artifactName string 99 downloadPath string 100 out output.Outputer 101 } 102 103 func (p *nonInteractiveProgress) Start(_ int64) { 104 p.spinner = output.StartSpinner(p.out, locale.Tr("builds_dl_downloading", p.artifactName), constants.TerminalAnimationInterval) 105 } 106 107 func (p *nonInteractiveProgress) Inc(_ int) {} 108 109 func (p *nonInteractiveProgress) Abort() {} 110 111 func (p *nonInteractiveProgress) Stop() { 112 p.spinner.Stop(locale.Tl("msg_download_success", "[SUCCESS]Downloaded {{.V0}} to {{.V1}}[/RESET]", p.artifactName, p.downloadPath)) 113 }