From 19b2560e2dbca88c44b3c384583fb5d5fd23c969 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 24 Aug 2024 09:54:25 +0100 Subject: [PATCH] cmd/htpacker: swap out progress bar library Switch to a simpler progress bar library. I've been having trouble tracking down a panic that seemed to affect the prior code; the new code is much easier and uses far fewer async channels and things. --- cmd/htpacker/go.mod | 9 +-- cmd/htpacker/go.sum | 27 ++----- cmd/htpacker/pack.go | 2 +- cmd/htpacker/pack_progress.go | 148 ---------------------------------- cmd/htpacker/packer/packer.go | 4 +- cmd/htpacker/progress.go | 131 ++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 177 deletions(-) delete mode 100644 cmd/htpacker/pack_progress.go create mode 100644 cmd/htpacker/progress.go diff --git a/cmd/htpacker/go.mod b/cmd/htpacker/go.mod index 0b5c398..889caa3 100644 --- a/cmd/htpacker/go.mod +++ b/cmd/htpacker/go.mod @@ -5,9 +5,8 @@ go 1.22 require ( github.com/andybalholm/brotli v1.1.0 github.com/foobaz/go-zopfli v0.0.0-20140122214029-7432051485e2 - github.com/logrusorgru/aurora/v4 v4.0.0 + github.com/gosuri/uiprogress v0.0.1 github.com/spf13/cobra v1.8.1 - github.com/vbauerster/mpb/v8 v8.7.3 golang.org/x/sys v0.22.0 gopkg.in/yaml.v2 v2.4.0 src.lwithers.me.uk/go/htpack v1.3.3 @@ -15,13 +14,11 @@ require ( ) require ( - github.com/VividCortex/ewma v1.2.0 // indirect - github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/gosuri/uilive v0.0.4 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/spf13/pflag v1.0.5 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/cmd/htpacker/go.sum b/cmd/htpacker/go.sum index f291e48..d0f5e7b 100644 --- a/cmd/htpacker/go.sum +++ b/cmd/htpacker/go.sum @@ -1,16 +1,14 @@ -github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= -github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= -github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/foobaz/go-zopfli v0.0.0-20140122214029-7432051485e2 h1:VA6jElpcJ+wkwEBufbnVkSBCA2TEnxdRppjRT5Kvh0A= github.com/foobaz/go-zopfli v0.0.0-20140122214029-7432051485e2/go.mod h1:Yi95+RbwKz7uGndSuUhoq7LJKh8qH8DT9fnL4ewU30k= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= +github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= +github.com/gosuri/uiprogress v0.0.1 h1:0kpv/XY/qTmFWl/SkaJykZXrBBzwwadmW8fRb7RJSxw= +github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -20,24 +18,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= -github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/vbauerster/mpb/v8 v8.7.3 h1:n/mKPBav4FFWp5fH4U0lPpXfiOmCEgl5Yx/NM3tKJA0= -github.com/vbauerster/mpb/v8 v8.7.3/go.mod h1:9nFlNpDGVoTmQ4QvNjSLtwLmAFjwmq0XaAF26toHGNM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -56,6 +43,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -73,7 +61,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= src.lwithers.me.uk/go/htpack v1.3.3 h1:Xvl6qR9HfSblmCgPyu+ACQ9o3aLQSIy3l8CrMbzj/jc= src.lwithers.me.uk/go/htpack v1.3.3/go.mod h1:qKgCBgZ6iiiuYOxZkYOPVpXLBzp6gXEd4A0ksxgR6Nk= diff --git a/cmd/htpacker/pack.go b/cmd/htpacker/pack.go index 86d2791..e829950 100644 --- a/cmd/htpacker/pack.go +++ b/cmd/htpacker/pack.go @@ -144,7 +144,7 @@ func PackSpec(c *cobra.Command, spec, out string) error { } func doPack(ftp packer.FilesToPack, out string) error { - prog := mpbProgress(ftp) + prog := newUiProgress(ftp) err := packer.Pack2(ftp, out, prog) prog.Complete() diff --git a/cmd/htpacker/pack_progress.go b/cmd/htpacker/pack_progress.go deleted file mode 100644 index adfe6a9..0000000 --- a/cmd/htpacker/pack_progress.go +++ /dev/null @@ -1,148 +0,0 @@ -package main - -import ( - "strconv" - - "github.com/logrusorgru/aurora/v4" - "github.com/vbauerster/mpb/v8" - "github.com/vbauerster/mpb/v8/decor" - "src.lwithers.me.uk/go/htpack/cmd/htpacker/packer" -) - -// mpbProgress returns a new progress object that keeps the user informed via -// the visual mpb library. -func mpbProgress(ftp packer.FilesToPack) *mpbProg { - mp := new(mpbProg) - mp.un.max = len(ftp) - - for _, f := range ftp { - if !f.DisableCompression && !f.DisableGzip { - mp.gzip.max++ - } - if !f.DisableCompression && !f.DisableBrotli { - mp.brotli.max++ - } - } - - barStyle := mpb.BarStyle().Filler("█").Refiller("█").Padding("░").Tip("█").Build() - - mp.p = mpb.New() - mp.un.bar = mp.p.MustAdd(int64(mp.un.max), - barStyle, - mpb.PrependDecorators(barName("uncompressed")), - mpb.AppendDecorators(&mp.un)) - if mp.gzip.max > 0 { - mp.gzip.bar = mp.p.MustAdd(int64(mp.gzip.max), - barStyle, - mpb.PrependDecorators(barName("gzip")), - mpb.AppendDecorators(&mp.gzip)) - } - if mp.brotli.max > 0 { - mp.brotli.bar = mp.p.MustAdd(int64(mp.brotli.max), - barStyle, - mpb.PrependDecorators(barName("brotli")), - mpb.AppendDecorators(&mp.brotli)) - } - - return mp -} - -func barName(n string) decor.Decorator { - return decor.Name(aurora.Magenta(n).String(), decor.WCSyncWidth) -} - -// mpbProg is the mpb progress tracker. It has one bar for each type of -// compression, and its methods simply dispatch onto the type-specific -// bars. -type mpbProg struct { - un, gzip, brotli mpbProg1 - p *mpb.Progress -} - -func (mp *mpbProg) Count(_ int) { -} - -func (mp *mpbProg) Begin(filename, compression string) { - switch compression { - case "uncompressed": - mp.un.Begin(filename) - case "gzip": - mp.gzip.Begin(filename) - case "brotli": - mp.brotli.Begin(filename) - default: - return - } -} - -func (mp *mpbProg) End(filename, compression string) { - switch compression { - case "uncompressed": - mp.un.End(filename) - case "gzip": - mp.gzip.End(filename) - case "brotli": - mp.brotli.End(filename) - default: - return - } -} - -func (mp *mpbProg) Complete() { - mp.un.Complete() - mp.gzip.Complete() - mp.brotli.Complete() - mp.p.Wait() -} - -// mpbProg1 is a type-specific progress bar. In addition to holding state and -// methods for updating the bar, it also implements decor.Decor. -type mpbProg1 struct { - max int // number of items we expect - done int // number of items completed - cur []string // list of currently-packing filenames - bar *mpb.Bar - - // embedding this type lets us implement decor.Decor - decor.WC -} - -func (mp1 *mpbProg1) Decor(stat decor.Statistics) (string, int) { - if stat.Completed { - return "", 0 - } - switch len(mp1.cur) { - case 0: - return aurora.Gray(8, "(idle)").String(), 6 - case 1: - return aurora.Blue(mp1.cur[0]).String(), len(mp1.cur[0]) - default: - s := mp1.cur[0] - d := strconv.Itoa(len(mp1.cur) - 1) - return aurora.Sprintf(aurora.Green("%s + %s more"), s, d), len(s) + len(d) + 8 - } -} - -func (mp1 *mpbProg1) Begin(filename string) { - mp1.cur = append(mp1.cur, filename) -} - -func (mp1 *mpbProg1) End(filename string) { - for i, v := range mp1.cur { - if v == filename { - mp1.cur[i] = mp1.cur[len(mp1.cur)-1] - mp1.cur = mp1.cur[:len(mp1.cur)-1] - break - } - } - mp1.done++ - if mp1.bar != nil { - mp1.bar.SetCurrent(int64(mp1.done)) - } -} - -func (mp1 *mpbProg1) Complete() { - if mp1.bar != nil { - mp1.bar.SetTotal(int64(mp1.max), true) - } -} diff --git a/cmd/htpacker/packer/packer.go b/cmd/htpacker/packer/packer.go index a6b0a1b..957a217 100644 --- a/cmd/htpacker/packer/packer.go +++ b/cmd/htpacker/packer/packer.go @@ -364,7 +364,7 @@ func (p *packer) packFile(path string, fileToPack FileToPack) { defer p.progress.End(fileToPack.Filename, "gzip") if err := p.Gzip(data, info); err != nil { return fmt.Errorf("gzip compression of %s "+ - "failed: %v", fileToPack.Filename, err) + "failed: %w", fileToPack.Filename, err) } return nil }) @@ -375,7 +375,7 @@ func (p *packer) packFile(path string, fileToPack FileToPack) { defer p.progress.End(fileToPack.Filename, "brotli") if err := p.Brotli(data, info); err != nil { return fmt.Errorf("brotli compression of %s "+ - "failed: %v", fileToPack.Filename, err) + "failed: %w", fileToPack.Filename, err) } return nil }) diff --git a/cmd/htpacker/progress.go b/cmd/htpacker/progress.go new file mode 100644 index 0000000..488d182 --- /dev/null +++ b/cmd/htpacker/progress.go @@ -0,0 +1,131 @@ +package main + +import ( + "bytes" + "slices" + "sync" + + "github.com/gosuri/uiprogress" + "src.lwithers.me.uk/go/htpack/cmd/htpacker/packer" +) + +type uiProgress struct { + p *uiprogress.Progress + uncompressed, gzip, brotli *uiProgressBar +} + +func newUiProgress(ftp packer.FilesToPack) *uiProgress { + up := &uiProgress{ + p: uiprogress.New(), + } + + up.uncompressed = newUiProgressBar(up.p, len(ftp), "uncompressed") + var nGzip, nBrotli int + for _, f := range ftp { + if !f.DisableCompression && !f.DisableGzip { + nGzip++ + } + if !f.DisableCompression && !f.DisableBrotli { + nBrotli++ + } + } + if nGzip > 0 { + up.gzip = newUiProgressBar(up.p, nGzip, "gzip") + } + if nBrotli > 0 { + up.brotli = newUiProgressBar(up.p, nGzip, "brotli") + } + + up.p.Start() + return up +} + +func (up *uiProgress) Count(_ int) { +} + +func (up *uiProgress) Begin(filename, compression string) { + up.bar(compression).begin(filename) +} + +func (up *uiProgress) End(filename, compression string) { + up.bar(compression).end(filename) +} + +func (up *uiProgress) bar(compression string) *uiProgressBar { + switch compression { + case "uncompressed": + return up.uncompressed + case "gzip": + return up.gzip + case "brotli": + return up.brotli + } + return nil +} + +func (up *uiProgress) Complete() { + up.p.Stop() +} + +type uiProgressBar struct { + bar *uiprogress.Bar + lock sync.Mutex + inflight []string +} + +func newUiProgressBar(p *uiprogress.Progress, total int, compression string) *uiProgressBar { + bar := &uiProgressBar{ + bar: p.AddBar(total).AppendCompleted(), + } + + var buf bytes.Buffer + bar.bar.PrependFunc(func(*uiprogress.Bar) string { + bar.lock.Lock() + defer bar.lock.Unlock() + buf.Reset() + buf.WriteString(compression) + if len(bar.inflight) > 0 { + buf.WriteString(" (") + for i, f := range bar.inflight { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(f) + } + buf.WriteRune(')') + } + if buf.Len() < 40 { + buf.WriteString(" ") + buf.Truncate(40) + } else if buf.Len() > 40 { + buf.Truncate(39) + buf.WriteString("…") + } + return buf.String() + }) + + return bar +} + +func (bar *uiProgressBar) begin(filename string) { + if bar == nil { + return + } + bar.lock.Lock() + defer bar.lock.Unlock() + + bar.inflight = append(bar.inflight, filename) +} + +func (bar *uiProgressBar) end(filename string) { + if bar == nil { + return + } + bar.lock.Lock() + defer bar.lock.Unlock() + + bar.bar.Incr() + if idx := slices.Index(bar.inflight, filename); idx != -1 { + bar.inflight = slices.Delete(bar.inflight, idx, idx+1) + } +}