fix: add download cancel via context and update progressbar #27
							
								
								
									
										102
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								build.go
									
									
									
									
									
								
							| @@ -57,64 +57,64 @@ func BuildCmd() *cli.Command { | ||||
| 				Usage:   gotext.Get("Build package from scratch even if there's an already built package available"), | ||||
| 			}, | ||||
| 		}, | ||||
|         Action: func(c *cli.Context) error { | ||||
|             ctx := c.Context | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			ctx := c.Context | ||||
|  | ||||
|             var script string | ||||
|             packageInput := c.String("package") | ||||
| 			var script string | ||||
| 			packageInput := c.String("package") | ||||
|  | ||||
|             if packageInput != "" { | ||||
|                 // If the package input contains a '/', use it as the full path. | ||||
|                 if filepath.Dir(packageInput) == "." { | ||||
|                     // No directory specified, use 'default' as a prefix. | ||||
|                     script = filepath.Join(config.GetPaths(ctx).RepoDir, "default", packageInput, "alr.sh") | ||||
|                 } else { | ||||
|                     // Use the full path specified by the user. | ||||
|                     script = filepath.Join(config.GetPaths(ctx).RepoDir, packageInput, "alr.sh") | ||||
|                 } | ||||
|             } | ||||
| 			if packageInput != "" { | ||||
| 				// If the package input contains a '/', use it as the full path. | ||||
| 				if filepath.Dir(packageInput) == "." { | ||||
| 					// No directory specified, use 'default' as a prefix. | ||||
| 					script = filepath.Join(config.GetPaths(ctx).RepoDir, "default", packageInput, "alr.sh") | ||||
| 				} else { | ||||
| 					// Use the full path specified by the user. | ||||
| 					script = filepath.Join(config.GetPaths(ctx).RepoDir, packageInput, "alr.sh") | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
|             if config.GetInstance(ctx).AutoPull(ctx) { | ||||
|                 err := repos.Pull(ctx, config.Config(ctx).Repos) | ||||
|                 if err != nil { | ||||
|                     slog.Error(gotext.Get("Error pulling repositories"), "err", err) | ||||
|                     os.Exit(1) | ||||
|                 } | ||||
|             } | ||||
| 			if config.GetInstance(ctx).AutoPull(ctx) { | ||||
| 				err := repos.Pull(ctx, config.Config(ctx).Repos) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
|             mgr := manager.Detect() | ||||
|             if mgr == nil { | ||||
|                 slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) | ||||
|                 os.Exit(1) | ||||
|             } | ||||
| 			mgr := manager.Detect() | ||||
| 			if mgr == nil { | ||||
| 				slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
|             pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{ | ||||
|                 Script:      script, | ||||
|                 Manager:     mgr, | ||||
|                 Clean:       c.Bool("clean"), | ||||
|                 Interactive: c.Bool("interactive"), | ||||
|             }) | ||||
|             if err != nil { | ||||
|                 slog.Error(gotext.Get("Error building package"), "err", err) | ||||
|                 os.Exit(1) | ||||
|             } | ||||
| 			pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{ | ||||
| 				Script:      script, | ||||
| 				Manager:     mgr, | ||||
| 				Clean:       c.Bool("clean"), | ||||
| 				Interactive: c.Bool("interactive"), | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error building package"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
|             wd, err := os.Getwd() | ||||
|             if err != nil { | ||||
|                 slog.Error(gotext.Get("Error getting working directory"), "err", err) | ||||
|                 os.Exit(1) | ||||
|             } | ||||
| 			wd, err := os.Getwd() | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error getting working directory"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
|             for _, pkgPath := range pkgPaths { | ||||
|                 name := filepath.Base(pkgPath) | ||||
|                 err = osutils.Move(pkgPath, filepath.Join(wd, name)) | ||||
|                 if err != nil { | ||||
|                     slog.Error(gotext.Get("Error moving the package"), "err", err) | ||||
|                     os.Exit(1) | ||||
|                 } | ||||
|             } | ||||
| 			for _, pkgPath := range pkgPaths { | ||||
| 				name := filepath.Base(pkgPath) | ||||
| 				err = osutils.Move(pkgPath, filepath.Join(wd, name)) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error moving the package"), "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
|             return nil | ||||
|         }, | ||||
| 			return nil | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										22
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								go.mod
									
									
									
									
									
								
							| @@ -8,9 +8,9 @@ require ( | ||||
| 	github.com/AlecAivazis/survey/v2 v2.3.7 | ||||
| 	github.com/PuerkitoBio/purell v1.2.0 | ||||
| 	github.com/alecthomas/chroma/v2 v2.9.1 | ||||
| 	github.com/charmbracelet/bubbles v0.16.1 | ||||
| 	github.com/charmbracelet/bubbletea v0.24.2 | ||||
| 	github.com/charmbracelet/lipgloss v0.10.0 | ||||
| 	github.com/charmbracelet/bubbles v0.20.0 | ||||
| 	github.com/charmbracelet/bubbletea v1.2.4 | ||||
| 	github.com/charmbracelet/lipgloss v1.0.0 | ||||
| 	github.com/charmbracelet/log v0.4.0 | ||||
| 	github.com/go-git/go-billy/v5 v5.5.0 | ||||
| 	github.com/go-git/go-git/v5 v5.12.0 | ||||
| @@ -18,19 +18,19 @@ require ( | ||||
| 	github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 | ||||
| 	github.com/jmoiron/sqlx v1.3.5 | ||||
| 	github.com/leonelquinteros/gotext v1.7.0 | ||||
| 	github.com/mattn/go-isatty v0.0.19 | ||||
| 	github.com/mattn/go-isatty v0.0.20 | ||||
| 	github.com/mholt/archiver/v4 v4.0.0-alpha.8 | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 | ||||
| 	github.com/muesli/reflow v0.3.0 | ||||
| 	github.com/pelletier/go-toml/v2 v2.1.0 | ||||
| 	github.com/schollz/progressbar/v3 v3.13.1 | ||||
| 	github.com/schollz/progressbar/v3 v3.18.0 | ||||
| 	github.com/stretchr/testify v1.10.0 | ||||
| 	github.com/urfave/cli/v2 v2.25.7 | ||||
| 	github.com/vmihailenco/msgpack/v5 v5.3.5 | ||||
| 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | ||||
| 	golang.org/x/crypto v0.27.0 | ||||
| 	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb | ||||
| 	golang.org/x/sys v0.27.0 | ||||
| 	golang.org/x/sys v0.29.0 | ||||
| 	golang.org/x/text v0.21.0 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 | ||||
| 	modernc.org/sqlite v1.25.0 | ||||
| @@ -53,6 +53,9 @@ require ( | ||||
| 	github.com/bodgit/sevenzip v1.3.0 // indirect | ||||
| 	github.com/bodgit/windows v1.0.0 // indirect | ||||
| 	github.com/cavaliergopher/cpio v1.0.1 // indirect | ||||
| 	github.com/charmbracelet/harmonica v0.2.0 // indirect | ||||
| 	github.com/charmbracelet/x/ansi v0.4.5 // indirect | ||||
| 	github.com/charmbracelet/x/term v0.2.1 // indirect | ||||
| 	github.com/cloudflare/circl v1.3.8 // indirect | ||||
| 	github.com/connesc/cipherio v0.2.1 // indirect | ||||
| 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect | ||||
| @@ -63,6 +66,7 @@ require ( | ||||
| 	github.com/dsnet/compress v0.0.1 // indirect | ||||
| 	github.com/dustin/go-humanize v1.0.1 // indirect | ||||
| 	github.com/emirpasic/gods v1.18.1 // indirect | ||||
| 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | ||||
| 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect | ||||
| 	github.com/go-logfmt/logfmt v0.6.0 // indirect | ||||
| 	github.com/gobwas/glob v0.2.3 // indirect | ||||
| @@ -84,12 +88,12 @@ require ( | ||||
| 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect | ||||
| 	github.com/mattn/go-colorable v0.1.13 // indirect | ||||
| 	github.com/mattn/go-localereader v0.0.1 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.15 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.16 // indirect | ||||
| 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect | ||||
| 	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect | ||||
| 	github.com/mitchellh/copystructure v1.2.0 // indirect | ||||
| 	github.com/mitchellh/reflectwalk v1.0.2 // indirect | ||||
| 	github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect | ||||
| 	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect | ||||
| 	github.com/muesli/cancelreader v0.2.2 // indirect | ||||
| 	github.com/muesli/termenv v0.15.2 // indirect | ||||
| 	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect | ||||
| @@ -113,7 +117,7 @@ require ( | ||||
| 	golang.org/x/mod v0.18.0 // indirect | ||||
| 	golang.org/x/net v0.26.0 // indirect | ||||
| 	golang.org/x/sync v0.10.0 // indirect | ||||
| 	golang.org/x/term v0.25.0 // indirect | ||||
| 	golang.org/x/term v0.28.0 // indirect | ||||
| 	golang.org/x/tools v0.22.0 // indirect | ||||
| 	gopkg.in/warnings.v0 v0.1.2 // indirect | ||||
| 	lukechampine.com/uint128 v1.2.0 // indirect | ||||
|   | ||||
							
								
								
									
										51
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								go.sum
									
									
									
									
									
								
							| @@ -75,12 +75,34 @@ github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= | ||||
| github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= | ||||
| github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= | ||||
| github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= | ||||
| github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= | ||||
| github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= | ||||
| github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c= | ||||
| github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= | ||||
| github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= | ||||
| github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= | ||||
| github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= | ||||
| github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= | ||||
| github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= | ||||
| github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= | ||||
| github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= | ||||
| github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= | ||||
| github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= | ||||
| github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= | ||||
| github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= | ||||
| github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= | ||||
| github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= | ||||
| github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= | ||||
| github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= | ||||
| github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= | ||||
| github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= | ||||
| github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= | ||||
| github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= | ||||
| github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= | ||||
| github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= | ||||
| github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= | ||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||
| github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||
| @@ -115,6 +137,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc | ||||
| github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | ||||
| github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | ||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | ||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||
| github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= | ||||
| @@ -211,7 +235,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 | ||||
| github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | ||||
| github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= | ||||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||
| github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= | ||||
| github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= | ||||
| github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= | ||||
| github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= | ||||
| @@ -243,15 +266,13 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec | ||||
| github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||||
| github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||
| github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||
| github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||
| github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= | ||||
| github.com/mattn/go-isatty v0.0.19/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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= | ||||
| github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= | ||||
| github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= | ||||
| github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||||
| 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/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= | ||||
| github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||||
| github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | ||||
| github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= | ||||
| github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= | ||||
| @@ -271,6 +292,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx | ||||
| github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= | ||||
| github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= | ||||
| github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= | ||||
| github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= | ||||
| github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= | ||||
| github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= | ||||
| github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | ||||
| github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= | ||||
| @@ -307,8 +330,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD | ||||
| github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= | ||||
| github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= | ||||
| github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= | ||||
| 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.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= | ||||
| github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= | ||||
| github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= | ||||
| github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= | ||||
| github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= | ||||
| @@ -327,7 +350,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| @@ -462,6 +484,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w | ||||
| golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| @@ -471,15 +494,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= | ||||
| golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||||
| golang.org/x/sys v0.29.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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= | ||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||
| golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= | ||||
| golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= | ||||
| golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= | ||||
| golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= | ||||
| golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= | ||||
| 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= | ||||
|   | ||||
| @@ -134,7 +134,7 @@ type Manifest struct { | ||||
| type Downloader interface { | ||||
| 	Name() string | ||||
| 	MatchURL(string) bool | ||||
| 	Download(Options) (Type, string, error) | ||||
| 	Download(context.Context, Options) (Type, string, error) | ||||
| } | ||||
|  | ||||
| // Интерфейс UpdatingDownloader расширяет Downloader методом Update | ||||
| @@ -157,7 +157,7 @@ func Download(ctx context.Context, opts Options) (err error) { | ||||
| 	d := getDownloader(opts.URL) | ||||
|  | ||||
| 	if opts.CacheDisabled { | ||||
| 		_, _, err = d.Download(opts) | ||||
| 		_, _, err = d.Download(ctx, opts) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -226,7 +226,7 @@ func Download(ctx context.Context, opts Options) (err error) { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	t, name, err := d.Download(Options{ | ||||
| 	t, name, err := d.Download(ctx, Options{ | ||||
| 		Hash:          opts.Hash, | ||||
| 		HashAlgorithm: opts.HashAlgorithm, | ||||
| 		Name:          opts.Name, | ||||
|   | ||||
| @@ -22,6 +22,7 @@ package dl | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"mime" | ||||
| 	"net/http" | ||||
| @@ -30,12 +31,8 @@ import ( | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mholt/archiver/v4" | ||||
| 	"github.com/schollz/progressbar/v3" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||
| ) | ||||
|  | ||||
| // FileDownloader загружает файлы с использованием HTTP | ||||
| @@ -54,7 +51,7 @@ func (FileDownloader) MatchURL(string) bool { | ||||
|  | ||||
| // Download загружает файл с использованием HTTP. Если файл | ||||
| // сжат в поддерживаемом формате, он будет распакован | ||||
| func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| func (FileDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||
| 	// Разбор URL | ||||
| 	u, err := url.Parse(opts.URL) | ||||
| 	if err != nil { | ||||
| @@ -94,8 +91,12 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		} | ||||
| 		r = localFl | ||||
| 	} else { | ||||
| 		req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) | ||||
| 		if err != nil { | ||||
| 			return 0, "", fmt.Errorf("failed to create request: %w", err) | ||||
| 		} | ||||
| 		// Выполнение HTTP GET запроса | ||||
| 		res, err := http.Get(u.String()) | ||||
| 		res, err := http.DefaultClient.Do(req) | ||||
| 		if err != nil { | ||||
| 			return 0, "", err | ||||
| 		} | ||||
| @@ -114,29 +115,13 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
| 	defer fl.Close() | ||||
|  | ||||
| 	var bar io.WriteCloser | ||||
| 	var out io.WriteCloser | ||||
| 	// Настройка индикатора прогресса | ||||
| 	if opts.Progress != nil { | ||||
| 		bar = progressbar.NewOptions64( | ||||
| 			size, | ||||
| 			progressbar.OptionSetDescription(name), | ||||
| 			progressbar.OptionSetWriter(opts.Progress), | ||||
| 			progressbar.OptionShowBytes(true), | ||||
| 			progressbar.OptionSetWidth(10), | ||||
| 			progressbar.OptionThrottle(65*time.Millisecond), | ||||
| 			progressbar.OptionShowCount(), | ||||
| 			progressbar.OptionOnCompletion(func() { | ||||
| 				_, _ = io.WriteString(opts.Progress, "\n") | ||||
| 			}), | ||||
| 			progressbar.OptionSpinnerType(14), | ||||
| 			progressbar.OptionFullWidth(), | ||||
| 			progressbar.OptionSetRenderBlankState(true), | ||||
| 		) | ||||
| 		defer bar.Close() | ||||
| 		out = NewProgressWriter(fl, size, name, opts.Progress) | ||||
| 	} else { | ||||
| 		bar = handlers.NopRWC{} | ||||
| 		out = fl | ||||
| 	} | ||||
|  | ||||
| 	h, err := opts.NewHash() | ||||
| @@ -147,9 +132,9 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 	var w io.Writer | ||||
| 	// Настройка MultiWriter для записи в файл, хеш и индикатор прогресса | ||||
| 	if opts.Hash != nil { | ||||
| 		w = io.MultiWriter(fl, h, bar) | ||||
| 		w = io.MultiWriter(h, out) | ||||
| 	} else { | ||||
| 		w = io.MultiWriter(fl, bar) | ||||
| 		w = io.MultiWriter(out) | ||||
| 	} | ||||
|  | ||||
| 	// Копирование содержимого из источника в файл назначения | ||||
| @@ -158,6 +143,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
| 	r.Close() | ||||
| 	out.Close() | ||||
|  | ||||
| 	// Проверка контрольной суммы | ||||
| 	if opts.Hash != nil { | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| package dl | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"net/url" | ||||
| 	"path" | ||||
| @@ -47,7 +48,7 @@ func (GitDownloader) MatchURL(u string) bool { | ||||
| // Download uses git to clone the repository from the specified URL. | ||||
| // It allows specifying the revision, depth and recursion options | ||||
| // via query string | ||||
| func (GitDownloader) Download(opts Options) (Type, string, error) { | ||||
| func (GitDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||
| 	u, err := url.Parse(opts.URL) | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| @@ -89,7 +90,7 @@ func (GitDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		co.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth | ||||
| 	} | ||||
|  | ||||
| 	r, err := git.PlainClone(opts.Destination, false, co) | ||||
| 	r, err := git.PlainCloneContext(ctx, opts.Destination, false, co) | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										246
									
								
								internal/dl/progress_tui.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								internal/dl/progress_tui.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| // ALR - Any Linux Repository | ||||
| // Copyright (C) 2025 Евгений Храмов | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| package dl | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/charmbracelet/bubbles/progress" | ||||
| 	"github.com/charmbracelet/bubbles/spinner" | ||||
| 	tea "github.com/charmbracelet/bubbletea" | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| ) | ||||
|  | ||||
| type model struct { | ||||
| 	progress   progress.Model | ||||
| 	spinner    spinner.Model | ||||
| 	percent    float64 | ||||
| 	speed      float64 | ||||
| 	done       bool | ||||
| 	useSpinner bool | ||||
| 	filename   string | ||||
|  | ||||
| 	total      int64 | ||||
| 	downloaded int64 | ||||
| 	elapsed    time.Duration | ||||
| 	remaining  time.Duration | ||||
|  | ||||
| 	width int | ||||
| } | ||||
|  | ||||
| func (m model) Init() tea.Cmd { | ||||
| 	if m.useSpinner { | ||||
| 		return m.spinner.Tick | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||||
| 	if m.done { | ||||
| 		return m, tea.Quit | ||||
| 	} | ||||
|  | ||||
| 	switch msg := msg.(type) { | ||||
| 	case progressUpdate: | ||||
| 		m.percent = msg.percent | ||||
| 		m.speed = msg.speed | ||||
| 		m.downloaded = msg.downloaded | ||||
| 		m.total = msg.total | ||||
| 		m.elapsed = time.Duration(msg.elapsed) * time.Second | ||||
| 		m.remaining = time.Duration(msg.remaining) * time.Second | ||||
| 		if m.percent >= 1.0 { | ||||
| 			m.done = true | ||||
| 		} | ||||
| 		return m, nil | ||||
| 	case tea.WindowSizeMsg: | ||||
| 		m.width = msg.Width | ||||
| 		return m, nil | ||||
| 	case progress.FrameMsg: | ||||
| 		if !m.useSpinner { | ||||
| 			progressModel, cmd := m.progress.Update(msg) | ||||
| 			m.progress = progressModel.(progress.Model) | ||||
| 			return m, cmd | ||||
| 		} | ||||
| 	case spinner.TickMsg: | ||||
| 		if m.useSpinner { | ||||
| 			spinnerModel, cmd := m.spinner.Update(msg) | ||||
| 			m.spinner = spinnerModel | ||||
| 			return m, cmd | ||||
| 		} | ||||
| 	case tea.KeyMsg: | ||||
| 		if msg.String() == "q" { | ||||
| 			return m, tea.Quit | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| func (m model) View() string { | ||||
| 	if m.done { | ||||
| 		return gotext.Get("%s: done!\n", m.filename) | ||||
| 	} | ||||
| 	if m.useSpinner { | ||||
| 		return gotext.Get( | ||||
| 			"%s %s downloading at %s/s\n", | ||||
| 			m.filename, | ||||
| 			m.spinner.View(), | ||||
| 			prettyByteSize(int64(m.speed)), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	leftPart := m.filename | ||||
|  | ||||
| 	rightPart := fmt.Sprintf("%.2f%% (%s/%s, %s/s) [%v:%v]\n", m.percent*100, | ||||
| 		prettyByteSize(m.downloaded), | ||||
| 		prettyByteSize(m.total), | ||||
| 		prettyByteSize(int64(m.speed)), | ||||
| 		m.elapsed, | ||||
| 		m.remaining, | ||||
| 	) | ||||
|  | ||||
| 	m.progress.Width = m.width - len(leftPart) - len(rightPart) - 6 | ||||
| 	bar := m.progress.ViewAs(m.percent) | ||||
| 	return fmt.Sprintf( | ||||
| 		"%s %s %s", | ||||
| 		leftPart, | ||||
| 		bar, | ||||
| 		rightPart, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func prettyByteSize(b int64) string { | ||||
| 	bf := float64(b) | ||||
| 	for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { | ||||
| 		if math.Abs(bf) < 1024.0 { | ||||
| 			return fmt.Sprintf("%3.1f%sB", bf, unit) | ||||
| 		} | ||||
| 		bf /= 1024.0 | ||||
| 	} | ||||
| 	return fmt.Sprintf("%.1fYiB", bf) | ||||
| } | ||||
|  | ||||
| type progressUpdate struct { | ||||
| 	percent float64 | ||||
| 	speed   float64 | ||||
| 	total   int64 | ||||
|  | ||||
| 	downloaded int64 | ||||
| 	elapsed    float64 | ||||
| 	remaining  float64 | ||||
| } | ||||
|  | ||||
| type ProgressWriter struct { | ||||
| 	baseWriter   io.WriteCloser | ||||
| 	total        int64 | ||||
| 	downloaded   int64 | ||||
| 	startTime    time.Time | ||||
| 	onProgress   func(progressUpdate) | ||||
| 	lastReported time.Time | ||||
| 	doneChan     chan struct{} | ||||
| } | ||||
|  | ||||
| func (pw *ProgressWriter) Write(p []byte) (int, error) { | ||||
| 	n, err := pw.baseWriter.Write(p) | ||||
| 	if err != nil { | ||||
| 		return n, err | ||||
| 	} | ||||
|  | ||||
| 	pw.downloaded += int64(n) | ||||
| 	now := time.Now() | ||||
| 	elapsed := now.Sub(pw.startTime).Seconds() | ||||
| 	speed := float64(pw.downloaded) / elapsed | ||||
| 	var remaining, percent float64 | ||||
| 	if pw.total > 0 { | ||||
| 		remaining = (float64(pw.total) - float64(pw.downloaded)) / speed | ||||
| 		percent = float64(pw.downloaded) / float64(pw.total) | ||||
| 	} | ||||
|  | ||||
| 	if now.Sub(pw.lastReported) > 100*time.Millisecond { | ||||
| 		pw.onProgress(progressUpdate{ | ||||
| 			percent:    percent, | ||||
| 			speed:      speed, | ||||
| 			total:      pw.total, | ||||
| 			downloaded: pw.downloaded, | ||||
| 			elapsed:    elapsed, | ||||
| 			remaining:  remaining, | ||||
| 		}) | ||||
| 		pw.lastReported = now | ||||
| 	} | ||||
|  | ||||
| 	return n, nil | ||||
| } | ||||
|  | ||||
| func (pw *ProgressWriter) Close() error { | ||||
| 	pw.onProgress(progressUpdate{ | ||||
| 		percent:    1, | ||||
| 		speed:      0, | ||||
| 		downloaded: pw.downloaded, | ||||
| 	}) | ||||
| 	<-pw.doneChan | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func NewProgressWriter(base io.WriteCloser, max int64, filename string, out io.Writer) *ProgressWriter { | ||||
| 	var m *model | ||||
| 	if max == -1 { | ||||
| 		m = &model{ | ||||
| 			spinner:    spinner.New(), | ||||
| 			useSpinner: true, | ||||
| 			filename:   filename, | ||||
| 		} | ||||
| 		m.spinner.Spinner = spinner.Dot | ||||
| 	} else { | ||||
| 		m = &model{ | ||||
| 			progress: progress.New( | ||||
| 				progress.WithDefaultGradient(), | ||||
| 				progress.WithoutPercentage(), | ||||
| 			), | ||||
| 			useSpinner: false, | ||||
| 			filename:   filename, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	p := tea.NewProgram(m, | ||||
| 		tea.WithInput(nil), | ||||
| 		tea.WithOutput(out), | ||||
| 	) | ||||
|  | ||||
| 	pw := &ProgressWriter{ | ||||
| 		baseWriter: base, | ||||
| 		total:      max, | ||||
| 		startTime:  time.Now(), | ||||
| 		doneChan:   make(chan struct{}), | ||||
| 		onProgress: func(update progressUpdate) { | ||||
| 			p.Send(update) | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		defer close(pw.doneChan) | ||||
| 		if _, err := p.Run(); err != nil { | ||||
| 			fmt.Fprintf(os.Stderr, "Error running progress writer: %v\n", err) | ||||
| 			os.Exit(1) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return pw | ||||
| } | ||||
| @@ -20,6 +20,7 @@ | ||||
| package dl | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| @@ -49,7 +50,7 @@ func (TorrentDownloader) MatchURL(u string) bool { | ||||
| } | ||||
|  | ||||
| // Download downloads a file over the BitTorrent protocol. | ||||
| func (TorrentDownloader) Download(opts Options) (Type, string, error) { | ||||
| func (TorrentDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||
| 	aria2Path, err := exec.LookPath("aria2c") | ||||
| 	if err != nil { | ||||
| 		return 0, "", ErrAria2NotFound | ||||
| @@ -57,7 +58,7 @@ func (TorrentDownloader) Download(opts Options) (Type, string, error) { | ||||
|  | ||||
| 	opts.URL = strings.TrimPrefix(opts.URL, "torrent+") | ||||
|  | ||||
| 	cmd := exec.Command(aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL) | ||||
| 	cmd := exec.CommandContext(ctx, aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL) | ||||
| 	cmd.Stdout = os.Stdout | ||||
| 	cmd.Stderr = os.Stderr | ||||
| 	err = cmd.Run() | ||||
|   | ||||
| @@ -198,7 +198,6 @@ func TestResolveLangs(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestReleasePlatformSpecific(t *testing.T) { | ||||
|  | ||||
| 	type testCase struct { | ||||
| 		info     *distro.OSRelease | ||||
| 		expected string | ||||
|   | ||||
| @@ -26,23 +26,23 @@ msgid "" | ||||
| "Build package from scratch even if there's an already built package available" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:71 | ||||
| #: build.go:80 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:78 | ||||
| #: build.go:87 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:89 | ||||
| #: build.go:98 | ||||
| msgid "Error building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:95 | ||||
| #: build.go:104 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:103 | ||||
| #: build.go:112 | ||||
| msgid "Error moving the package" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -251,6 +251,14 @@ msgstr "" | ||||
| msgid "Downloading source" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/dl/progress_tui.go:99 | ||||
| msgid "%s: done!\n" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/dl/progress_tui.go:103 | ||||
| msgid "%s %s downloading at %s/s\n" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/logger/log.go:47 | ||||
| msgid "ERROR" | ||||
| msgstr "" | ||||
| @@ -285,81 +293,81 @@ msgstr "" | ||||
| msgid "Error while running app" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:105 | ||||
| #: pkg/build/build.go:107 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:109 | ||||
| #: pkg/build/build.go:111 | ||||
| msgid "Building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:153 | ||||
| #: pkg/build/build.go:155 | ||||
| msgid "Downloading sources" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:165 | ||||
| #: pkg/build/build.go:167 | ||||
| msgid "Building package metadata" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:187 | ||||
| #: pkg/build/build.go:189 | ||||
| msgid "Compressing package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:313 | ||||
| #: pkg/build/build.go:315 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:324 | ||||
| #: pkg/build/build.go:326 | ||||
| msgid "This package is already installed" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:352 | ||||
| #: pkg/build/build.go:354 | ||||
| msgid "Installing build dependencies" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:394 | ||||
| #: pkg/build/build.go:396 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:440 | ||||
| #: pkg/build/build.go:442 | ||||
| msgid "Executing version()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:460 | ||||
| #: pkg/build/build.go:462 | ||||
| msgid "Updating version" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:465 | ||||
| #: pkg/build/build.go:467 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:475 | ||||
| #: pkg/build/build.go:477 | ||||
| msgid "Executing build()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:487 | ||||
| #: pkg/build/build.go:489 | ||||
| msgid "Executing package()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:525 | ||||
| #: pkg/build/build.go:527 | ||||
| msgid "Executing files()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:600 | ||||
| #: pkg/build/build.go:605 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:611 | ||||
| #: pkg/build/build.go:616 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:718 | ||||
| #: pkg/build/build.go:723 | ||||
| msgid "Would you like to remove the build dependencies?" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:824 | ||||
| #: pkg/build/build.go:829 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "" | ||||
|  | ||||
|   | ||||
| @@ -33,23 +33,23 @@ msgid "" | ||||
| "Build package from scratch even if there's an already built package available" | ||||
| msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | ||||
|  | ||||
| #: build.go:71 | ||||
| #: build.go:80 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "Ошибка при извлечении репозиториев" | ||||
|  | ||||
| #: build.go:78 | ||||
| #: build.go:87 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" | ||||
|  | ||||
| #: build.go:89 | ||||
| #: build.go:98 | ||||
| msgid "Error building package" | ||||
| msgstr "Ошибка при сборке пакета" | ||||
|  | ||||
| #: build.go:95 | ||||
| #: build.go:104 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "Ошибка при получении рабочего каталога" | ||||
|  | ||||
| #: build.go:103 | ||||
| #: build.go:112 | ||||
| msgid "Error moving the package" | ||||
| msgstr "Ошибка при перемещении пакета" | ||||
|  | ||||
| @@ -263,6 +263,14 @@ msgstr "Источник обновлён и связан с пунктом на | ||||
| msgid "Downloading source" | ||||
| msgstr "Скачивание источника" | ||||
|  | ||||
| #: internal/dl/progress_tui.go:99 | ||||
| msgid "%s: done!\n" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/dl/progress_tui.go:103 | ||||
| msgid "%s %s downloading at %s/s\n" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/logger/log.go:47 | ||||
| msgid "ERROR" | ||||
| msgstr "ОШИБКА" | ||||
| @@ -299,27 +307,27 @@ msgstr "" | ||||
| msgid "Error while running app" | ||||
| msgstr "Ошибка при запуске приложения" | ||||
|  | ||||
| #: pkg/build/build.go:105 | ||||
| #: pkg/build/build.go:107 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" | ||||
|  | ||||
| #: pkg/build/build.go:109 | ||||
| #: pkg/build/build.go:111 | ||||
| msgid "Building package" | ||||
| msgstr "Сборка пакета" | ||||
|  | ||||
| #: pkg/build/build.go:153 | ||||
| #: pkg/build/build.go:155 | ||||
| msgid "Downloading sources" | ||||
| msgstr "Скачивание источников" | ||||
|  | ||||
| #: pkg/build/build.go:165 | ||||
| #: pkg/build/build.go:167 | ||||
| msgid "Building package metadata" | ||||
| msgstr "Сборка метаданных пакета" | ||||
|  | ||||
| #: pkg/build/build.go:187 | ||||
| #: pkg/build/build.go:189 | ||||
| msgid "Compressing package" | ||||
| msgstr "Сжатие пакета" | ||||
|  | ||||
| #: pkg/build/build.go:313 | ||||
| #: pkg/build/build.go:315 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| @@ -327,57 +335,57 @@ msgstr "" | ||||
| "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||
| "равно хотите выполнить сборку?" | ||||
|  | ||||
| #: pkg/build/build.go:324 | ||||
| #: pkg/build/build.go:326 | ||||
| msgid "This package is already installed" | ||||
| msgstr "Этот пакет уже установлен" | ||||
|  | ||||
| #: pkg/build/build.go:352 | ||||
| #: pkg/build/build.go:354 | ||||
| msgid "Installing build dependencies" | ||||
| msgstr "Установка зависимостей сборки" | ||||
|  | ||||
| #: pkg/build/build.go:394 | ||||
| #: pkg/build/build.go:396 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "Установка зависимостей" | ||||
|  | ||||
| #: pkg/build/build.go:440 | ||||
| #: pkg/build/build.go:442 | ||||
| msgid "Executing version()" | ||||
| msgstr "Исполнение версия()" | ||||
|  | ||||
| #: pkg/build/build.go:460 | ||||
| #: pkg/build/build.go:462 | ||||
| msgid "Updating version" | ||||
| msgstr "Обновление версии" | ||||
|  | ||||
| #: pkg/build/build.go:465 | ||||
| #: pkg/build/build.go:467 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "Исполнение prepare()" | ||||
|  | ||||
| #: pkg/build/build.go:475 | ||||
| #: pkg/build/build.go:477 | ||||
| msgid "Executing build()" | ||||
| msgstr "Исполнение build()" | ||||
|  | ||||
| #: pkg/build/build.go:487 | ||||
| #: pkg/build/build.go:489 | ||||
| msgid "Executing package()" | ||||
| msgstr "Исполнение package()" | ||||
|  | ||||
| #: pkg/build/build.go:525 | ||||
| #: pkg/build/build.go:527 | ||||
| msgid "Executing files()" | ||||
| msgstr "Исполнение files()" | ||||
|  | ||||
| #: pkg/build/build.go:600 | ||||
| #: pkg/build/build.go:605 | ||||
| msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/build.go:611 | ||||
| #: pkg/build/build.go:616 | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/build.go:718 | ||||
| #: pkg/build/build.go:723 | ||||
| msgid "Would you like to remove the build dependencies?" | ||||
| msgstr "Хотели бы вы удалить зависимости сборки?" | ||||
|  | ||||
| #: pkg/build/build.go:824 | ||||
| #: pkg/build/build.go:829 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user