feat: migrate to system cache with changing core logic #66
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @@ -37,6 +37,7 @@ install: \ | |||||||
|  |  | ||||||
| $(INSTALED_BIN): $(BIN) | $(INSTALED_BIN): $(BIN) | ||||||
| 	install -Dm755 $< $@ | 	install -Dm755 $< $@ | ||||||
|  | 	setcap cap_setuid,cap_setgid+ep $(INSTALED_BIN) | ||||||
|  |  | ||||||
| $(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION) | $(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION) | ||||||
| 	install -Dm755 $< $@ | 	install -Dm755 $< $@ | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> |     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> | ||||||
|         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> |         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> | ||||||
|         <text x="33.5" y="14">coverage</text> |         <text x="33.5" y="14">coverage</text> | ||||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">19.0%</text> |         <text x="86" y="15" fill="#010101" fill-opacity=".3">16.3%</text> | ||||||
|         <text x="86" y="14">19.0%</text> |         <text x="86" y="14">16.3%</text> | ||||||
|     </g> |     </g> | ||||||
| </svg> | </svg> | ||||||
|   | |||||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
							
								
								
									
										182
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								build.go
									
									
									
									
									
								
							| @@ -28,14 +28,12 @@ import ( | |||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func BuildCmd() *cli.Command { | func BuildCmd() *cli.Command { | ||||||
| @@ -66,32 +64,67 @@ func BuildCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.EnuseIsPrivilegedGroupMember(); err != nil { | ||||||
| 			cfg := config.New() | 				return err | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			wd, err := os.Getwd() | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			wd, wdCleanup, err := Mount(wd) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer wdCleanup() | ||||||
|  |  | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposNoPull(). | ||||||
|  | 				WithDistroInfo(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			var script string | 			var script string | ||||||
| 			var packages []string | 			var packages []string | ||||||
| 			repository := "default" |  | ||||||
|  |  | ||||||
| 			repoDir := cfg.GetPaths().RepoDir | 			var res *build.BuildResult | ||||||
|  |  | ||||||
|  | 			var scriptArgs *build.BuildPackageFromScriptArgs | ||||||
|  | 			var dbArgs *build.BuildPackageFromDbArgs | ||||||
|  |  | ||||||
|  | 			buildArgs := &build.BuildArgs{ | ||||||
|  | 				Opts: &types.BuildOpts{ | ||||||
|  | 					Clean:       c.Bool("clean"), | ||||||
|  | 					Interactive: c.Bool("interactive"), | ||||||
|  | 				}, | ||||||
|  | 				PkgFormat_: build.GetPkgFormat(deps.Manager), | ||||||
|  | 				Info:       deps.Info, | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			switch { | 			switch { | ||||||
| 			case c.IsSet("script"): | 			case c.IsSet("script"): | ||||||
| 				script = c.String("script") | 				script, err = filepath.Abs(c.String("script")) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Cannot get absolute script path"), err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				packages = append(packages, c.String("script-package")) | 				packages = append(packages, c.String("script-package")) | ||||||
|  |  | ||||||
|  | 				scriptArgs = &build.BuildPackageFromScriptArgs{ | ||||||
|  | 					Script:    script, | ||||||
|  | 					Packages:  packages, | ||||||
|  | 					BuildArgs: *buildArgs, | ||||||
|  | 				} | ||||||
| 			case c.IsSet("package"): | 			case c.IsSet("package"): | ||||||
| 				// TODO: handle multiple packages | 				// TODO: handle multiple packages | ||||||
| 				packageInput := c.String("package") | 				packageInput := c.String("package") | ||||||
| @@ -104,86 +137,97 @@ func BuildCmd() *cli.Command { | |||||||
| 					packageSearch = arr[0] | 					packageSearch = arr[0] | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				pkgs, _, _ := rs.FindPkgs(ctx, []string{packageSearch}) | 				pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageSearch}) | ||||||
| 				pkg, ok := pkgs[packageSearch] | 				if err != nil { | ||||||
| 				if len(pkg) < 1 || !ok { | 					return cliutils.FormatCliExit("failed to find pkgs", err) | ||||||
| 					slog.Error(gotext.Get("Package not found")) |  | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				repository = pkg[0].Repository | 				pkg := cliutils.FlattenPkgs(ctx, pkgs, "build", c.Bool("interactive")) | ||||||
|  |  | ||||||
|  | 				if len(pkg) < 1 { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Package not found"), nil) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				if pkg[0].BasePkgName != "" { | 				if pkg[0].BasePkgName != "" { | ||||||
| 					script = filepath.Join(repoDir, repository, pkg[0].BasePkgName, "alr.sh") |  | ||||||
| 					packages = append(packages, pkg[0].Name) | 					packages = append(packages, pkg[0].Name) | ||||||
| 				} else { | 				} | ||||||
| 					script = filepath.Join(repoDir, repository, pkg[0].Name, "alr.sh") |  | ||||||
|  | 				dbArgs = &build.BuildPackageFromDbArgs{ | ||||||
|  | 					Package:   &pkg[0], | ||||||
|  | 					Packages:  packages, | ||||||
|  | 					BuildArgs: *buildArgs, | ||||||
| 				} | 				} | ||||||
| 			default: | 			default: | ||||||
| 				script = filepath.Join(repoDir, "alr.sh") | 				return cliutils.FormatCliExit(gotext.Get("Nothing to build"), nil) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Проверка автоматического пулла репозиториев | 			if scriptArgs != nil { | ||||||
| 			if cfg.AutoPull() { | 				scriptFile := filepath.Base(scriptArgs.Script) | ||||||
| 				err := rs.Pull(ctx, cfg.Repos()) | 				newScriptDir, scriptDirCleanup, err := Mount(filepath.Dir(scriptArgs.Script)) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | 					return err | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  | 				defer scriptDirCleanup() | ||||||
|  | 				scriptArgs.Script = filepath.Join(newScriptDir, scriptFile) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Обнаружение менеджера пакетов | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 			mgr := manager.Detect() | 				return err | ||||||
| 			if mgr == nil { |  | ||||||
| 				slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			info, err := distro.ParseOSRelease(ctx) | 			installer, installerClose, err := build.GetSafeInstaller() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error parsing os release"), "err", err) | 				return err | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer installerClose() | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			builder := build.NewBuilder( | 			scripter, scripterClose, err := build.GetSafeScriptExecutor() | ||||||
| 				ctx, | 			if err != nil { | ||||||
| 				types.BuildOpts{ | 				return err | ||||||
| 					Packages:    packages, | 			} | ||||||
| 					Repository:  repository, | 			defer scripterClose() | ||||||
| 					Script:      script, |  | ||||||
| 					Manager:     mgr, | 			builder, err := build.NewMainBuilder( | ||||||
| 					Clean:       c.Bool("clean"), | 				deps.Cfg, | ||||||
| 					Interactive: c.Bool("interactive"), | 				deps.Manager, | ||||||
| 				}, | 				deps.Repos, | ||||||
| 				rs, | 				scripter, | ||||||
| 				info, | 				installer, | ||||||
| 				cfg, |  | ||||||
| 			) | 			) | ||||||
|  |  | ||||||
| 			// Сборка пакета |  | ||||||
| 			pkgPaths, _, err := builder.BuildPackage(ctx) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error building package"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Получение текущей рабочей директории | 			if scriptArgs != nil { | ||||||
| 			wd, err := os.Getwd() | 				res, err = builder.BuildPackageFromScript( | ||||||
| 			if err != nil { | 					ctx, | ||||||
| 				slog.Error(gotext.Get("Error getting working directory"), "err", err) | 					scriptArgs, | ||||||
| 				os.Exit(1) | 				) | ||||||
|  | 			} else if dbArgs != nil { | ||||||
|  | 				res, err = builder.BuildPackageFromDb( | ||||||
|  | 					ctx, | ||||||
|  | 					dbArgs, | ||||||
|  | 				) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Перемещение собранных пакетов в рабочую директорию | 			if err != nil { | ||||||
| 			for _, pkgPath := range pkgPaths { | 				return cliutils.FormatCliExit(gotext.Get("Error building package"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, pkgPath := range res.PackagePaths { | ||||||
| 				name := filepath.Base(pkgPath) | 				name := filepath.Base(pkgPath) | ||||||
| 				err = osutils.Move(pkgPath, filepath.Join(wd, name)) | 				err = osutils.Move(pkgPath, filepath.Join(wd, name)) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error moving the package"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			slog.Info(gotext.Get("Done")) | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ func TestE2EAlrAddRepo(t *testing.T) { | |||||||
| 		COMMON_SYSTEMS, | 		COMMON_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"addrepo", | 				"addrepo", | ||||||
| 				"--name", | 				"--name", | ||||||
| @@ -45,11 +46,12 @@ func TestE2EAlrAddRepo(t *testing.T) { | |||||||
| 			err = r.Exec(e2e.NewCommand( | 			err = r.Exec(e2e.NewCommand( | ||||||
| 				"bash", | 				"bash", | ||||||
| 				"-c", | 				"-c", | ||||||
| 				"cat $HOME/.config/alr/alr.toml", | 				"cat /etc/alr/alr.toml", | ||||||
| 			)) | 			)) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
| 			err = r.Exec(e2e.NewCommand( | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"removerepo", | 				"removerepo", | ||||||
| 				"--name", | 				"--name", | ||||||
| @@ -61,7 +63,7 @@ func TestE2EAlrAddRepo(t *testing.T) { | |||||||
| 			err = r.Exec(e2e.NewCommand( | 			err = r.Exec(e2e.NewCommand( | ||||||
| 				"bash", | 				"bash", | ||||||
| 				"-c", | 				"-c", | ||||||
| 				"cat $HOME/.config/alr/alr.toml", | 				"cat /etc/alr/alr.toml", | ||||||
| 			), e2e.WithExecOptionStdout(&buf)) | 			), e2e.WithExecOptionStdout(&buf)) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 			assert.Contains(t, buf.String(), "rootCmd") | 			assert.Contains(t, buf.String(), "rootCmd") | ||||||
|   | |||||||
| @@ -109,7 +109,7 @@ var ALL_SYSTEMS []string = []string{ | |||||||
| } | } | ||||||
|  |  | ||||||
| var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{ | var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{ | ||||||
| 	"alt-sisyphus", | 	// "alt-sisyphus", | ||||||
| 	"fedora-41", | 	"fedora-41", | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -159,7 +159,7 @@ func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testin | |||||||
| 					e2e.StartOptions{ | 					e2e.StartOptions{ | ||||||
| 						Image:   imageId, | 						Image:   imageId, | ||||||
| 						Volumes: []string{ | 						Volumes: []string{ | ||||||
| 							"./alr:/usr/bin/alr", | 							// "./alr:/usr/bin/alr", | ||||||
| 						}, | 						}, | ||||||
| 						Privileged: true, | 						Privileged: true, | ||||||
| 					}, | 					}, | ||||||
|   | |||||||
| @@ -1,8 +1,18 @@ | |||||||
| FROM fedora:41 | FROM fedora:41 | ||||||
| RUN dnf install -y ca-certificates sudo rpm-build | RUN dnf install -y ca-certificates sudo rpm-build bindfs | ||||||
| RUN useradd -m -s /bin/bash alr-user && \ | RUN <<EOF | ||||||
|     echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \ |     useradd -m -s /bin/bash -G wheel user | ||||||
|     chmod 0440 /etc/sudoers.d/alr-user |     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user | ||||||
| USER alr-user |     chmod 0440 /etc/sudoers.d/user | ||||||
| WORKDIR /home/alr-user |  | ||||||
|  |     useradd -m -s /bin/bash alr | ||||||
|  |     mkdir -p /var/cache/alr /etc/alr | ||||||
|  |     chown alr:alr /var/cache/alr /etc/alr | ||||||
|  | EOF | ||||||
|  | COPY ./alr /usr/bin | ||||||
|  | RUN <<EOF | ||||||
|  |     setcap cap_setuid,cap_setgid+ep /usr/bin/alr | ||||||
|  | EOF | ||||||
|  | USER user | ||||||
|  | WORKDIR /home/user | ||||||
| ENTRYPOINT ["tail", "-f", "/dev/null"] | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
| @@ -1,7 +1,17 @@ | |||||||
| FROM ubuntu:24.10 | FROM ubuntu:24.10 | ||||||
| RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo | RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo libcap2-bin | ||||||
| RUN useradd -m -s /bin/bash alr-user && \ | RUN <<EOF | ||||||
|     echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \ |     useradd -m -s /bin/bash user | ||||||
|     chmod 0440 /etc/sudoers.d/alr-user |     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user | ||||||
| USER alr-user |     chmod 0440 /etc/sudoers.d/user  | ||||||
|  |      | ||||||
|  |     useradd -m -s /bin/bash alr | ||||||
|  |     mkdir -p /var/cache/alr /etc/alr | ||||||
|  |     chown alr:alr /var/cache/alr /etc/alr | ||||||
|  | EOF | ||||||
|  | COPY ./alr /usr/bin | ||||||
|  | RUN <<EOF | ||||||
|  |     setcap cap_setuid,cap_setgid+ep /usr/bin/alr | ||||||
|  | EOF | ||||||
|  | USER user | ||||||
| ENTRYPOINT ["tail", "-f", "/dev/null"] | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
| @@ -32,7 +32,7 @@ func TestE2EIssue32Interactive(t *testing.T) { | |||||||
| 		COMMON_SYSTEMS, | 		COMMON_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
| 				"alr", "--interactive=false", "remove", "ca-certificates", | 				"sudo", "alr", "--interactive=false", "remove", "ca-certificates", | ||||||
| 			)) | 			)) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 		}, | 		}, | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ func TestE2EIssue41AutoreqSkiplist(t *testing.T) { | |||||||
| 		AUTOREQ_AUTOPROV_SYSTEMS, | 		AUTOREQ_AUTOPROV_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"addrepo", | 				"addrepo", | ||||||
| 				"--name", | 				"--name", | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ func TestE2EIssue50InstallMultiple(t *testing.T) { | |||||||
| 		COMMON_SYSTEMS, | 		COMMON_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"addrepo", | 				"addrepo", | ||||||
| 				"--name", | 				"--name", | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ func TestE2EIssue53LcAllCInfo(t *testing.T) { | |||||||
| 		COMMON_SYSTEMS, | 		COMMON_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"addrepo", | 				"addrepo", | ||||||
| 				"--name", | 				"--name", | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ func TestE2EIssue59RmCompletion(t *testing.T) { | |||||||
| 		COMMON_SYSTEMS, | 		COMMON_SYSTEMS, | ||||||
| 		func(t *testing.T, r e2e.Runnable) { | 		func(t *testing.T, r e2e.Runnable) { | ||||||
| 			err := r.Exec(e2e.NewCommand( | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
| 				"alr", | 				"alr", | ||||||
| 				"addrepo", | 				"addrepo", | ||||||
| 				"--name", | 				"--name", | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								fix.go
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								fix.go
									
									
									
									
									
								
							| @@ -22,13 +22,14 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func FixCmd() *cli.Command { | func FixCmd() *cli.Command { | ||||||
| @@ -36,51 +37,63 @@ func FixCmd() *cli.Command { | |||||||
| 		Name:  "fix", | 		Name:  "fix", | ||||||
| 		Usage: gotext.Get("Attempt to fix problems with ALR"), | 		Usage: gotext.Get("Attempt to fix problems with ALR"), | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
| 			cfg := config.New() | 				return err | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			cfg := deps.Cfg | ||||||
|  |  | ||||||
| 			paths := cfg.GetPaths() | 			paths := cfg.GetPaths() | ||||||
|  |  | ||||||
| 			slog.Info(gotext.Get("Removing cache directory")) | 			slog.Info(gotext.Get("Clearing cache directory")) | ||||||
|  | 			// Remove all nested directories of paths.CacheDir | ||||||
|  |  | ||||||
| 			err = os.RemoveAll(paths.CacheDir) | 			dir, err := os.Open(paths.CacheDir) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Unable to remove cache directory"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Unable to open cache directory"), err) | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer dir.Close() | ||||||
|  |  | ||||||
|  | 			entries, err := dir.Readdirnames(-1) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Unable to read cache directory contents"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, entry := range entries { | ||||||
|  | 				err = os.RemoveAll(filepath.Join(paths.CacheDir, entry)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Unable to remove cache item (%s)", entry), err) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			slog.Info(gotext.Get("Rebuilding cache")) | 			slog.Info(gotext.Get("Rebuilding cache")) | ||||||
|  |  | ||||||
| 			err = os.MkdirAll(paths.CacheDir, 0o755) | 			err = os.MkdirAll(paths.CacheDir, 0o755) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Unable to create new cache directory"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			cfg = config.New() | 			deps, err = appbuilder. | ||||||
| 			err = cfg.Load() | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposForcePull(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return cli.Exit(err, 1) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			db := database.New(cfg) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
| 			err = rs.Pull(ctx, cfg.Repos()) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			slog.Info(gotext.Get("Done")) | 			slog.Info(gotext.Get("Done")) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								go.mod
									
									
									
									
									
								
							| @@ -5,7 +5,6 @@ go 1.22 | |||||||
| toolchain go1.23.5 | toolchain go1.23.5 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1 |  | ||||||
| 	github.com/AlecAivazis/survey/v2 v2.3.7 | 	github.com/AlecAivazis/survey/v2 v2.3.7 | ||||||
| 	github.com/PuerkitoBio/purell v1.2.0 | 	github.com/PuerkitoBio/purell v1.2.0 | ||||||
| 	github.com/alecthomas/assert/v2 v2.2.1 | 	github.com/alecthomas/assert/v2 v2.2.1 | ||||||
| @@ -20,6 +19,8 @@ require ( | |||||||
| 	github.com/go-git/go-git/v5 v5.12.0 | 	github.com/go-git/go-git/v5 v5.12.0 | ||||||
| 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | ||||||
| 	github.com/goreleaser/nfpm/v2 v2.41.0 | 	github.com/goreleaser/nfpm/v2 v2.41.0 | ||||||
|  | 	github.com/hashicorp/go-hclog v0.14.1 | ||||||
|  | 	github.com/hashicorp/go-plugin v1.6.3 | ||||||
| 	github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 | 	github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 | ||||||
| 	github.com/jmoiron/sqlx v1.3.5 | 	github.com/jmoiron/sqlx v1.3.5 | ||||||
| 	github.com/leonelquinteros/gotext v1.7.0 | 	github.com/leonelquinteros/gotext v1.7.0 | ||||||
| @@ -33,7 +34,7 @@ require ( | |||||||
| 	github.com/urfave/cli/v2 v2.25.7 | 	github.com/urfave/cli/v2 v2.25.7 | ||||||
| 	github.com/vmihailenco/msgpack/v5 v5.3.5 | 	github.com/vmihailenco/msgpack/v5 v5.3.5 | ||||||
| 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | ||||||
| 	golang.org/x/crypto v0.27.0 | 	golang.org/x/crypto v0.32.0 | ||||||
| 	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb | 	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb | ||||||
| 	golang.org/x/sys v0.29.0 | 	golang.org/x/sys v0.29.0 | ||||||
| 	golang.org/x/text v0.21.0 | 	golang.org/x/text v0.21.0 | ||||||
| @@ -64,6 +65,7 @@ require ( | |||||||
| 	github.com/cloudflare/circl v1.3.8 // indirect | 	github.com/cloudflare/circl v1.3.8 // indirect | ||||||
| 	github.com/connesc/cipherio v0.2.1 // indirect | 	github.com/connesc/cipherio v0.2.1 // indirect | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect | 	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect | ||||||
|  | 	github.com/creack/pty v1.1.24 // indirect | ||||||
| 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect | 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect | ||||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||||
| 	github.com/dlclark/regexp2 v1.10.0 // indirect | 	github.com/dlclark/regexp2 v1.10.0 // indirect | ||||||
| @@ -72,10 +74,12 @@ require ( | |||||||
| 	github.com/efficientgo/core v1.0.0-rc.0 // indirect | 	github.com/efficientgo/core v1.0.0-rc.0 // indirect | ||||||
| 	github.com/emirpasic/gods v1.18.1 // indirect | 	github.com/emirpasic/gods v1.18.1 // indirect | ||||||
| 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | ||||||
|  | 	github.com/fatih/color v1.7.0 // indirect | ||||||
| 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect | 	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect | ||||||
| 	github.com/go-logfmt/logfmt v0.6.0 // indirect | 	github.com/go-logfmt/logfmt v0.6.0 // indirect | ||||||
| 	github.com/gobwas/glob v0.2.3 // indirect | 	github.com/gobwas/glob v0.2.3 // indirect | ||||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | ||||||
|  | 	github.com/golang/protobuf v1.5.3 // indirect | ||||||
| 	github.com/golang/snappy v0.0.4 // indirect | 	github.com/golang/snappy v0.0.4 // indirect | ||||||
| 	github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect | 	github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect | ||||||
| 	github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect | 	github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect | ||||||
| @@ -84,6 +88,7 @@ require ( | |||||||
| 	github.com/goreleaser/fileglob v1.3.0 // indirect | 	github.com/goreleaser/fileglob v1.3.0 // indirect | ||||||
| 	github.com/hashicorp/errwrap v1.0.0 // indirect | 	github.com/hashicorp/errwrap v1.0.0 // indirect | ||||||
| 	github.com/hashicorp/go-multierror v1.1.1 // indirect | 	github.com/hashicorp/go-multierror v1.1.1 // indirect | ||||||
|  | 	github.com/hashicorp/yamux v0.1.1 // indirect | ||||||
| 	github.com/hexops/gotextdiff v1.0.3 // indirect | 	github.com/hexops/gotextdiff v1.0.3 // indirect | ||||||
| 	github.com/huandu/xstrings v1.3.3 // indirect | 	github.com/huandu/xstrings v1.3.3 // indirect | ||||||
| 	github.com/imdario/mergo v0.3.16 // indirect | 	github.com/imdario/mergo v0.3.16 // indirect | ||||||
| @@ -103,6 +108,7 @@ require ( | |||||||
| 	github.com/muesli/cancelreader v0.2.2 // indirect | 	github.com/muesli/cancelreader v0.2.2 // indirect | ||||||
| 	github.com/muesli/termenv v0.15.2 // indirect | 	github.com/muesli/termenv v0.15.2 // indirect | ||||||
| 	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect | 	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect | ||||||
|  | 	github.com/oklog/run v1.0.0 // indirect | ||||||
| 	github.com/pierrec/lz4/v4 v4.1.15 // indirect | 	github.com/pierrec/lz4/v4 v4.1.15 // indirect | ||||||
| 	github.com/pjbgf/sha1cd v0.3.0 // indirect | 	github.com/pjbgf/sha1cd v0.3.0 // indirect | ||||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||||
| @@ -121,10 +127,13 @@ require ( | |||||||
| 	gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect | 	gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect | ||||||
| 	go4.org v0.0.0-20200411211856-f5505b9728dd // indirect | 	go4.org v0.0.0-20200411211856-f5505b9728dd // indirect | ||||||
| 	golang.org/x/mod v0.18.0 // indirect | 	golang.org/x/mod v0.18.0 // indirect | ||||||
| 	golang.org/x/net v0.26.0 // indirect | 	golang.org/x/net v0.34.0 // indirect | ||||||
| 	golang.org/x/sync v0.10.0 // indirect | 	golang.org/x/sync v0.10.0 // indirect | ||||||
| 	golang.org/x/term v0.28.0 // indirect | 	golang.org/x/term v0.28.0 // indirect | ||||||
| 	golang.org/x/tools v0.22.0 // indirect | 	golang.org/x/tools v0.22.0 // indirect | ||||||
|  | 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect | ||||||
|  | 	google.golang.org/grpc v1.58.3 // indirect | ||||||
|  | 	google.golang.org/protobuf v1.36.1 // indirect | ||||||
| 	gopkg.in/warnings.v0 v0.1.2 // indirect | 	gopkg.in/warnings.v0 v0.1.2 // indirect | ||||||
| 	lukechampine.com/uint128 v1.2.0 // indirect | 	lukechampine.com/uint128 v1.2.0 // indirect | ||||||
| 	modernc.org/cc/v3 v3.40.0 // indirect | 	modernc.org/cc/v3 v3.40.0 // indirect | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								go.sum
									
									
									
									
									
								
							| @@ -17,8 +17,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo | |||||||
| dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= | dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= | ||||||
| dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= | dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= | ||||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||||
| gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1 h1:c7F4OsyQbiVpSOrYGMrNsRL37BwoOfrgoKxAwULBKZo= |  | ||||||
| gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw= |  | ||||||
| github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= | ||||||
| github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | ||||||
| github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= | github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= | ||||||
| @@ -71,6 +69,8 @@ github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY | |||||||
| github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM= | github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM= | ||||||
| github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= | github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= | ||||||
| github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= | github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= | ||||||
|  | github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= | ||||||
|  | github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= | ||||||
| github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= | github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= | ||||||
| github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= | github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= | ||||||
| github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= | github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= | ||||||
| @@ -79,8 +79,8 @@ github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTli | |||||||
| github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= | github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= | ||||||
| github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= | github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= | ||||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||||
| github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||||||
| github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||||
| github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= | 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/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= | ||||||
| github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= | github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= | ||||||
| @@ -107,8 +107,8 @@ github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoV | |||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||||
| github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | ||||||
| github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= | github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= | ||||||
| github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= | github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= | ||||||
| github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= | github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= | ||||||
| github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= | github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| @@ -133,6 +133,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m | |||||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | 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 h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | ||||||
| github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | ||||||
|  | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= | ||||||
|  | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | ||||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | 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/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||||
| github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= | github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= | ||||||
| @@ -171,8 +173,9 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y | |||||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||||
| github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||||
| github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||||||
|  | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||||
| github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | ||||||
| github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||||
| @@ -181,6 +184,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a | |||||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
|  | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
| github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= | github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= | ||||||
| @@ -211,10 +215,16 @@ github.com/goreleaser/nfpm/v2 v2.41.0 h1:JyMzS/EwqaWbFs+7Z9oZ4Hkk4or00gUTqwm9Dgr | |||||||
| github.com/goreleaser/nfpm/v2 v2.41.0/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0= | github.com/goreleaser/nfpm/v2 v2.41.0/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0= | ||||||
| github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= | ||||||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | ||||||
|  | github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= | ||||||
|  | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= | ||||||
| github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= | ||||||
| github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= | ||||||
|  | github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= | ||||||
|  | github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= | ||||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||||
|  | github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= | ||||||
|  | github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= | ||||||
| github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= | ||||||
| github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= | ||||||
| github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= | ||||||
| @@ -229,6 +239,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl | |||||||
| github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= | ||||||
| github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc= | github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc= | ||||||
| github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= | github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= | ||||||
|  | github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= | ||||||
|  | github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= | ||||||
| github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | ||||||
| github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | ||||||
| github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||||
| @@ -264,9 +276,11 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i | |||||||
| github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= | ||||||
| github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= | ||||||
| github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||||
|  | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | ||||||
| github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||||||
| github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | 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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||||||
|  | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= | ||||||
| github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | 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-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||||
| @@ -304,6 +318,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+ | |||||||
| github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||||||
| github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= | github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= | ||||||
| github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= | github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= | ||||||
|  | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= | ||||||
|  | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= | ||||||
| github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= | github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= | ||||||
| github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= | github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= | ||||||
| github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | ||||||
| @@ -405,8 +421,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 | |||||||
| golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= | ||||||
| golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= | golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= | ||||||
| golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= | ||||||
| golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||||
| golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||||
| @@ -458,15 +474,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug | |||||||
| golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= | ||||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | ||||||
| golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||||
| golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= | golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= | ||||||
| golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= | ||||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| @@ -487,6 +503,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| @@ -572,8 +589,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 | |||||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||||
| google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
| google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||||
| google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||||
| google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||||
| google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||||
| @@ -587,6 +604,8 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx | |||||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||||
| google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||||
| google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||||
|  | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= | ||||||
|  | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= | ||||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||||
| @@ -594,8 +613,12 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac | |||||||
| google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||||
| google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||||
| google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= | google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= | ||||||
|  | google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= | ||||||
|  | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||||
|  | google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= | ||||||
|  | google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ import ( | |||||||
| 	"mvdan.cc/sh/v3/expand" | 	"mvdan.cc/sh/v3/expand" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| @@ -71,19 +72,17 @@ func HelperCmd() *cli.Command { | |||||||
| 			helper, ok := helpers.Helpers[c.Args().First()] | 			helper, ok := helpers.Helpers[c.Args().First()] | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				slog.Error(gotext.Get("No such helper command"), "name", c.Args().First()) | 				slog.Error(gotext.Get("No such helper command"), "name", c.Args().First()) | ||||||
| 				os.Exit(1) | 				return cli.Exit(gotext.Get("No such helper command"), 1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			wd, err := os.Getwd() | 			wd, err := os.Getwd() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error getting working directory"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			info, err := distro.ParseOSRelease(ctx) | 			info, err := distro.ParseOSRelease(ctx) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error getting working directory"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			hc := interp.HandlerContext{ | 			hc := interp.HandlerContext{ | ||||||
|   | |||||||
							
								
								
									
										97
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								info.go
									
									
									
									
									
								
							| @@ -21,7 +21,6 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" |  | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"github.com/jeandeaual/go-locale" | 	"github.com/jeandeaual/go-locale" | ||||||
| @@ -30,11 +29,11 @@ import ( | |||||||
| 	"gopkg.in/yaml.v3" | 	"gopkg.in/yaml.v3" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func InfoCmd() *cli.Command { | func InfoCmd() *cli.Command { | ||||||
| @@ -48,26 +47,25 @@ func InfoCmd() *cli.Command { | |||||||
| 				Usage:   gotext.Get("Show all information, not just for the current distro"), | 				Usage:   gotext.Get("Show all information, not just for the current distro"), | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		BashComplete: func(c *cli.Context) { | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
| 			cfg := config.New() | 			deps, err := appbuilder. | ||||||
| 			err := cfg.Load() | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			result, err := deps.DB.GetPkgs(c.Context, "true") | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			result, err := db.GetPkgs(c.Context, "true") |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error getting packages"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			defer result.Close() | 			defer result.Close() | ||||||
|  |  | ||||||
| @@ -75,53 +73,45 @@ func InfoCmd() *cli.Command { | |||||||
| 				var pkg database.Package | 				var pkg database.Package | ||||||
| 				err = result.StructScan(&pkg) | 				err = result.StructScan(&pkg) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error iterating over packages"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				fmt.Println(pkg.Name) | 				fmt.Println(pkg.Name) | ||||||
| 			} | 			} | ||||||
| 		}, | 			return nil | ||||||
|  | 		}), | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
| 			cfg := config.New() |  | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
|  |  | ||||||
| 			args := c.Args() | 			args := c.Args() | ||||||
| 			if args.Len() < 1 { | 			if args.Len() < 1 { | ||||||
| 				slog.Error(gotext.Get("Command info expected at least 1 argument, got %d", args.Len())) | 				return cli.Exit(gotext.Get("Command info expected at least 1 argument, got %d", args.Len()), 1) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if cfg.AutoPull() { | 			ctx := c.Context | ||||||
| 				err := rs.Pull(ctx, cfg.Repos()) |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithRepos(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error pulling repos"), "err", err) | 				return cli.Exit(err, 1) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			rs := deps.Repos | ||||||
|  |  | ||||||
| 			found, _, err := rs.FindPkgs(ctx, args.Slice()) | 			found, _, err := rs.FindPkgs(ctx, args.Slice()) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error finding packages"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error finding packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if len(found) == 0 { | 			if len(found) == 0 { | ||||||
| 				os.Exit(1) | 				return cliutils.FormatCliExit(gotext.Get("Package not found"), err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive")) | 			pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive")) | ||||||
| @@ -131,8 +121,7 @@ func InfoCmd() *cli.Command { | |||||||
|  |  | ||||||
| 			systemLang, err := locale.GetLanguage() | 			systemLang, err := locale.GetLanguage() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error("Can't detect system language", "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Can't detect system language"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			if systemLang == "" { | 			if systemLang == "" { | ||||||
| 				systemLang = "en" | 				systemLang = "en" | ||||||
| @@ -141,8 +130,7 @@ func InfoCmd() *cli.Command { | |||||||
| 			if !all { | 			if !all { | ||||||
| 				info, err := distro.ParseOSRelease(ctx) | 				info, err := distro.ParseOSRelease(ctx) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error parsing os-release file"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
| 				names, err = overrides.Resolve( | 				names, err = overrides.Resolve( | ||||||
| 					info, | 					info, | ||||||
| @@ -150,8 +138,7 @@ func InfoCmd() *cli.Command { | |||||||
| 						WithLanguages([]string{systemLang}), | 						WithLanguages([]string{systemLang}), | ||||||
| 				) | 				) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error resolving overrides"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -159,14 +146,12 @@ func InfoCmd() *cli.Command { | |||||||
| 				if !all { | 				if !all { | ||||||
| 					err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) | 					err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						slog.Error(gotext.Get("Error encoding script variables"), "err", err) | 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | ||||||
| 						os.Exit(1) |  | ||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					err = yaml.NewEncoder(os.Stdout).Encode(pkg) | 					err = yaml.NewEncoder(os.Stdout).Encode(pkg) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						slog.Error(gotext.Get("Error encoding script variables"), "err", err) | 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | ||||||
| 						os.Exit(1) |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										216
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								install.go
									
									
									
									
									
								
							| @@ -21,20 +21,17 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" |  | ||||||
| 	"os" |  | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func InstallCmd() *cli.Command { | func InstallCmd() *cli.Command { | ||||||
| @@ -50,96 +47,98 @@ func InstallCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.ExitIfNotRoot(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			args := c.Args() | 			args := c.Args() | ||||||
| 			if args.Len() < 1 { | 			if args.Len() < 1 { | ||||||
| 				slog.Error(gotext.Get("Command install expected at least 1 argument, got %d", args.Len())) | 				return cliutils.FormatCliExit(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()), nil) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			mgr := manager.Detect() | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 			if mgr == nil { | 				return err | ||||||
| 				slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			cfg := config.New() | 			installer, installerClose, err := build.GetSafeInstaller() | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return err | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer installerClose() | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			scripter, scripterClose, err := build.GetSafeScriptExecutor() | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer scripterClose() | ||||||
|  |  | ||||||
| 			if cfg.AutoPull() { | 			ctx := c.Context | ||||||
| 				err := rs.Pull(ctx, cfg.Repos()) |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithRepos(). | ||||||
|  | 				WithDistroInfo(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | 				return err | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			found, notFound, err := rs.FindPkgs(ctx, args.Slice()) | 			builder, err := build.NewMainBuilder( | ||||||
| 			if err != nil { | 				deps.Cfg, | ||||||
| 				slog.Error(gotext.Get("Error finding packages"), "err", err) | 				deps.Manager, | ||||||
| 				os.Exit(1) | 				deps.Repos, | ||||||
| 			} | 				scripter, | ||||||
|  | 				installer, | ||||||
| 			pkgs := cliutils.FlattenPkgs(ctx, found, "install", c.Bool("interactive")) |  | ||||||
|  |  | ||||||
| 			opts := types.BuildOpts{ |  | ||||||
| 				Manager:     mgr, |  | ||||||
| 				Clean:       c.Bool("clean"), |  | ||||||
| 				Interactive: c.Bool("interactive"), |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			info, err := distro.ParseOSRelease(ctx) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error parsing os release"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			builder := build.NewBuilder( |  | ||||||
| 				ctx, |  | ||||||
| 				opts, |  | ||||||
| 				rs, |  | ||||||
| 				info, |  | ||||||
| 				cfg, |  | ||||||
| 			) | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			builder.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{ | 			err = builder.InstallPkgs( | ||||||
| 				Manager:     mgr, | 				ctx, | ||||||
|  | 				&build.BuildArgs{ | ||||||
|  | 					Opts: &types.BuildOpts{ | ||||||
| 						Clean:       c.Bool("clean"), | 						Clean:       c.Bool("clean"), | ||||||
| 						Interactive: c.Bool("interactive"), | 						Interactive: c.Bool("interactive"), | ||||||
| 			}) | 					}, | ||||||
|  | 					Info:       deps.Info, | ||||||
|  | 					PkgFormat_: build.GetPkgFormat(deps.Manager), | ||||||
|  | 				}, | ||||||
|  | 				args.Slice(), | ||||||
|  | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error parsing os release"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 		BashComplete: func(c *cli.Context) { | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
| 			cfg := config.New() | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 			err := cfg.Load() | 				return err | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			ctx := c.Context | ||||||
| 			err = db.Init(c.Context) | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			result, err := db.GetPkgs(c.Context, "true") | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			result, err := deps.DB.GetPkgs(c.Context, "true") | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error getting packages"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			defer result.Close() | 			defer result.Close() | ||||||
|  |  | ||||||
| @@ -147,13 +146,14 @@ func InstallCmd() *cli.Command { | |||||||
| 				var pkg database.Package | 				var pkg database.Package | ||||||
| 				err = result.StructScan(&pkg) | 				err = result.StructScan(&pkg) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error iterating over packages"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				fmt.Println(pkg.Name) | 				fmt.Println(pkg.Name) | ||||||
| 			} | 			} | ||||||
| 		}, |  | ||||||
|  | 			return nil | ||||||
|  | 		}), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -162,31 +162,24 @@ func RemoveCmd() *cli.Command { | |||||||
| 		Name:    "remove", | 		Name:    "remove", | ||||||
| 		Usage:   gotext.Get("Remove an installed package"), | 		Usage:   gotext.Get("Remove an installed package"), | ||||||
| 		Aliases: []string{"rm"}, | 		Aliases: []string{"rm"}, | ||||||
| 		BashComplete: func(c *cli.Context) { | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
| 			cfg := config.New() | 			ctx := c.Context | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			deps, err := appbuilder. | ||||||
| 			err = db.Init(c.Context) | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return cli.Exit(err, 1) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			installedAlrPackages := map[string]string{} | 			installedAlrPackages := map[string]string{} | ||||||
| 			mgr := manager.Detect() | 			installed, err := deps.Manager.ListInstalled(&manager.Opts{}) | ||||||
| 			if mgr == nil { |  | ||||||
| 				slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
| 			installed, err := mgr.ListInstalled(&manager.Opts{AsRoot: false}) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error listing installed packages"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error listing installed packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			for pkgName, version := range installed { | 			for pkgName, version := range installed { | ||||||
| 				matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName) | 				matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName) | ||||||
| @@ -197,10 +190,9 @@ func RemoveCmd() *cli.Command { | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			result, err := db.GetPkgs(c.Context, "true") | 			result, err := deps.DB.GetPkgs(c.Context, "true") | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error getting packages"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			defer result.Close() | 			defer result.Close() | ||||||
|  |  | ||||||
| @@ -208,8 +200,7 @@ func RemoveCmd() *cli.Command { | |||||||
| 				var pkg database.Package | 				var pkg database.Package | ||||||
| 				err = result.StructScan(&pkg) | 				err = result.StructScan(&pkg) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error iterating over packages"), "err", err) | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				_, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] | 				_, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] | ||||||
| @@ -219,27 +210,32 @@ func RemoveCmd() *cli.Command { | |||||||
|  |  | ||||||
| 				fmt.Println(pkg.Name) | 				fmt.Println(pkg.Name) | ||||||
| 			} | 			} | ||||||
| 		}, |  | ||||||
|  | 			return nil | ||||||
|  | 		}), | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfNotRoot(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			args := c.Args() | 			args := c.Args() | ||||||
| 			if args.Len() < 1 { | 			if args.Len() < 1 { | ||||||
| 				slog.Error(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len())) | 				return cliutils.FormatCliExit(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len()), nil) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			mgr := manager.Detect() | 			deps, err := appbuilder. | ||||||
| 			if mgr == nil { | 				New(c.Context). | ||||||
| 				slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) | 				WithManager(). | ||||||
| 				os.Exit(1) | 				Build() | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			err := mgr.Remove(&manager.Opts{ |  | ||||||
| 				AsRoot:    true, |  | ||||||
| 				NoConfirm: !c.Bool("interactive"), |  | ||||||
| 			}, c.Args().Slice()...) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error removing packages"), "err", err) | 				return err | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			if err := deps.Manager.Remove(&manager.Opts{ | ||||||
|  | 				NoConfirm: !c.Bool("interactive"), | ||||||
|  | 			}, c.Args().Slice()...); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error removing packages"), err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
|   | |||||||
							
								
								
									
										280
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | // 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 main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"os/user" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/go-hclog" | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func InternalBuildCmd() *cli.Command { | ||||||
|  | 	return &cli.Command{ | ||||||
|  | 		Name:     "_internal-safe-script-executor", | ||||||
|  | 		HideHelp: true, | ||||||
|  | 		Hidden:   true, | ||||||
|  | 		Action: func(c *cli.Context) error { | ||||||
|  | 			logger.SetupForGoPlugin() | ||||||
|  |  | ||||||
|  | 			slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid()) | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			cfg := config.New() | ||||||
|  | 			err := cfg.Load() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error loading config"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			logger := hclog.New(&hclog.LoggerOptions{ | ||||||
|  | 				Name:        "plugin", | ||||||
|  | 				Output:      os.Stderr, | ||||||
|  | 				Level:       hclog.Debug, | ||||||
|  | 				JSONFormat:  false, | ||||||
|  | 				DisableTime: true, | ||||||
|  | 			}) | ||||||
|  |  | ||||||
|  | 			plugin.Serve(&plugin.ServeConfig{ | ||||||
|  | 				HandshakeConfig: build.HandshakeConfig, | ||||||
|  | 				Plugins: map[string]plugin.Plugin{ | ||||||
|  | 					"script-executor": &build.ScriptExecutorPlugin{ | ||||||
|  | 						Impl: build.NewLocalScriptExecutor(cfg), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Logger: logger, | ||||||
|  | 			}) | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func InternalInstallCmd() *cli.Command { | ||||||
|  | 	return &cli.Command{ | ||||||
|  | 		Name:     "_internal-installer", | ||||||
|  | 		HideHelp: true, | ||||||
|  | 		Hidden:   true, | ||||||
|  | 		Action: func(c *cli.Context) error { | ||||||
|  | 			logger.SetupForGoPlugin() | ||||||
|  |  | ||||||
|  | 			if err := utils.EnsureIsAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Before escalating the rights, we made sure that | ||||||
|  | 			// this is an ALR user, so it looks safe. | ||||||
|  | 			err := utils.EscalateToRootUid() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("cannot escalate to root", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(c.Context). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposNoPull(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			logger := hclog.New(&hclog.LoggerOptions{ | ||||||
|  | 				Name:        "plugin", | ||||||
|  | 				Output:      os.Stderr, | ||||||
|  | 				Level:       hclog.Trace, | ||||||
|  | 				JSONFormat:  true, | ||||||
|  | 				DisableTime: true, | ||||||
|  | 			}) | ||||||
|  |  | ||||||
|  | 			plugin.Serve(&plugin.ServeConfig{ | ||||||
|  | 				HandshakeConfig: build.HandshakeConfig, | ||||||
|  | 				Plugins: map[string]plugin.Plugin{ | ||||||
|  | 					"installer": &build.InstallerPlugin{ | ||||||
|  | 						Impl: build.NewInstaller( | ||||||
|  | 							manager.Detect(), | ||||||
|  | 						), | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Logger: logger, | ||||||
|  | 			}) | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Mount(target string) (string, func(), error) { | ||||||
|  | 	exe, err := os.Executable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, fmt.Errorf("failed to get executable path: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd := exec.Command(exe, "_internal-temporary-mount", target) | ||||||
|  |  | ||||||
|  | 	stdoutPipe, err := cmd.StdoutPipe() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, fmt.Errorf("failed to get stdout pipe: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	stdinPipe, err := cmd.StdinPipe() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, fmt.Errorf("failed to get stdin pipe: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd.Stderr = os.Stderr | ||||||
|  |  | ||||||
|  | 	if err := cmd.Start(); err != nil { | ||||||
|  | 		return "", nil, fmt.Errorf("failed to start mount: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	scanner := bufio.NewScanner(stdoutPipe) | ||||||
|  | 	var mountPath string | ||||||
|  | 	if scanner.Scan() { | ||||||
|  | 		mountPath = scanner.Text() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := scanner.Err(); err != nil { | ||||||
|  | 		_ = cmd.Process.Kill() | ||||||
|  | 		return "", nil, fmt.Errorf("failed to read mount output: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if mountPath == "" { | ||||||
|  | 		_ = cmd.Process.Kill() | ||||||
|  | 		return "", nil, errors.New("mount failed: no target path returned") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cleanup := func() { | ||||||
|  | 		slog.Debug("cleanup triggered") | ||||||
|  | 		_, _ = fmt.Fprintln(stdinPipe, "") | ||||||
|  | 		_ = cmd.Wait() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return mountPath, cleanup, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func InternalMountCmd() *cli.Command { | ||||||
|  | 	return &cli.Command{ | ||||||
|  | 		Name:     "_internal-temporary-mount", | ||||||
|  | 		HideHelp: true, | ||||||
|  | 		Hidden:   true, | ||||||
|  | 		Action: func(c *cli.Context) error { | ||||||
|  | 			logger.SetupForGoPlugin() | ||||||
|  |  | ||||||
|  | 			sourceDir := c.Args().First() | ||||||
|  |  | ||||||
|  | 			u, err := user.Current() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("cannot get current user", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			_, alrGid, err := utils.GetUidGidAlrUser() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("cannot get alr user", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if _, err := os.Stat(sourceDir); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(fmt.Sprintf("cannot read %s", sourceDir), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := utils.EnuseIsPrivilegedGroupMember(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Before escalating the rights, we made sure that | ||||||
|  | 			// 1. user in wheel group | ||||||
|  | 			// 2. user can access sourceDir | ||||||
|  | 			if err := utils.EscalateToRootUid(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			if err := syscall.Setgid(alrGid); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := os.MkdirAll(constants.AlrRunDir, 0o770); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(fmt.Sprintf("failed to create %s", constants.AlrRunDir), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := os.Chown(constants.AlrRunDir, 0, alrGid); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(fmt.Sprintf("failed to chown %s", constants.AlrRunDir), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			targetDir := filepath.Join(constants.AlrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid())) | ||||||
|  | 			// 0750: owner (root) and group (alr) | ||||||
|  | 			if err := os.MkdirAll(targetDir, 0o750); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("error creating bindfs target directory", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			//  chown AlrRunDir/mounts/bindfs-* to (root:alr), | ||||||
|  | 			//  so alr user can access dir | ||||||
|  | 			if err := os.Chown(targetDir, 0, alrGid); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("failed to chown bindfs directory", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			bindfsCmd := exec.Command( | ||||||
|  | 				"bindfs", | ||||||
|  | 				fmt.Sprintf("--map=%s/alr:@%s/@alr", u.Uid, u.Gid), | ||||||
|  | 				sourceDir, | ||||||
|  | 				targetDir, | ||||||
|  | 			) | ||||||
|  |  | ||||||
|  | 			bindfsCmd.Stderr = os.Stderr | ||||||
|  |  | ||||||
|  | 			if err := bindfsCmd.Run(); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("failed to strart bindfs", err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			fmt.Println(targetDir) | ||||||
|  |  | ||||||
|  | 			_, _ = bufio.NewReader(os.Stdin).ReadString('\n') | ||||||
|  |  | ||||||
|  | 			slog.Debug("start unmount", "dir", targetDir) | ||||||
|  |  | ||||||
|  | 			umountCmd := exec.Command("umount", targetDir) | ||||||
|  | 			umountCmd.Stderr = os.Stderr | ||||||
|  | 			if err := umountCmd.Run(); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(fmt.Sprintf("failed to unmount %s", targetDir), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := os.Remove(targetDir); err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(fmt.Sprintf("error removing directory %s", targetDir), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										176
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | |||||||
|  | // 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 appbuilder | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"log/slog" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type AppDeps struct { | ||||||
|  | 	Cfg     *config.ALRConfig | ||||||
|  | 	DB      *db.Database | ||||||
|  | 	Repos   *repos.Repos | ||||||
|  | 	Info    *distro.OSRelease | ||||||
|  | 	Manager manager.Manager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *AppDeps) Defer() { | ||||||
|  | 	if d.DB != nil { | ||||||
|  | 		if err := d.DB.Close(); err != nil { | ||||||
|  | 			slog.Warn("failed to close db", "err", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type AppBuilder struct { | ||||||
|  | 	deps AppDeps | ||||||
|  | 	err  error | ||||||
|  | 	ctx  context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func New(ctx context.Context) *AppBuilder { | ||||||
|  | 	return &AppBuilder{ctx: ctx} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) UseConfig(cfg *config.ALRConfig) *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  | 	b.deps.Cfg = cfg | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithConfig() *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cfg := config.New() | ||||||
|  | 	if err := cfg.Load(); err != nil { | ||||||
|  | 		b.err = cliutils.FormatCliExit(gotext.Get("Error loading config"), err) | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.deps.Cfg = cfg | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithDB() *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cfg := b.deps.Cfg | ||||||
|  | 	if cfg == nil { | ||||||
|  | 		b.err = errors.New("config is required before initializing DB") | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	db := db.New(cfg) | ||||||
|  | 	if err := db.Init(b.ctx); err != nil { | ||||||
|  | 		b.err = cliutils.FormatCliExit(gotext.Get("Error initialization database"), err) | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.deps.DB = db | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithRepos() *AppBuilder { | ||||||
|  | 	b.withRepos(true, false) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithReposForcePull() *AppBuilder { | ||||||
|  | 	b.withRepos(true, true) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithReposNoPull() *AppBuilder { | ||||||
|  | 	b.withRepos(false, false) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) withRepos(enablePull, forcePull bool) *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cfg := b.deps.Cfg | ||||||
|  | 	db := b.deps.DB | ||||||
|  | 	if cfg == nil || db == nil { | ||||||
|  | 		b.err = errors.New("config and db are required before initializing repos") | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rs := repos.New(cfg, db) | ||||||
|  |  | ||||||
|  | 	if enablePull && (forcePull || cfg.AutoPull()) { | ||||||
|  | 		if err := rs.Pull(b.ctx, cfg.Repos()); err != nil { | ||||||
|  | 			b.err = cliutils.FormatCliExit(gotext.Get("Error pulling repositories"), err) | ||||||
|  | 			return b | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.deps.Repos = rs | ||||||
|  |  | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithDistroInfo() *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.deps.Info, b.err = distro.ParseOSRelease(b.ctx) | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		b.err = cliutils.FormatCliExit(gotext.Get("Error parsing os release"), b.err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) WithManager() *AppBuilder { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.deps.Manager = manager.Detect() | ||||||
|  | 	if b.deps.Manager == nil { | ||||||
|  | 		b.err = cliutils.FormatCliExit(gotext.Get("Unable to detect a supported package manager on the system"), nil) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *AppBuilder) Build() (*AppDeps, error) { | ||||||
|  | 	if b.err != nil { | ||||||
|  | 		return nil, b.err | ||||||
|  | 	} | ||||||
|  | 	return &b.deps, nil | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								internal/cliutils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/cliutils/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  | // 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 cliutils | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  |  | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type BashCompleteWithErrorFunc func(c *cli.Context) error | ||||||
|  |  | ||||||
|  | func BashCompleteWithError(f BashCompleteWithErrorFunc) cli.BashCompleteFunc { | ||||||
|  | 	return func(c *cli.Context) { HandleExitCoder(f(c)) } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func HandleExitCoder(err error) { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if exitErr, ok := err.(cli.ExitCoder); ok { | ||||||
|  | 		if err.Error() != "" { | ||||||
|  | 			if _, ok := exitErr.(cli.ErrorFormatter); ok { | ||||||
|  | 				slog.Error(fmt.Sprintf("%+v\n", err)) | ||||||
|  | 			} else { | ||||||
|  | 				slog.Error(err.Error()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		cli.OsExiter(exitErr.ExitCode()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func FormatCliExit(msg string, err error) cli.ExitCoder { | ||||||
|  | 	return FormatCliExitWithCode(msg, err, 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func FormatCliExitWithCode(msg string, err error, exitCode int) cli.ExitCoder { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return cli.Exit(errors.New(msg), exitCode) | ||||||
|  | 	} | ||||||
|  | 	return cli.Exit(fmt.Errorf("%s: %w", msg, err), exitCode) | ||||||
|  | } | ||||||
| @@ -26,9 +26,9 @@ import ( | |||||||
| 	"reflect" | 	"reflect" | ||||||
|  |  | ||||||
| 	"github.com/caarlos0/env" | 	"github.com/caarlos0/env" | ||||||
| 	"github.com/leonelquinteros/gotext" |  | ||||||
| 	"github.com/pelletier/go-toml/v2" | 	"github.com/pelletier/go-toml/v2" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -84,34 +84,18 @@ func mergeStructs(dst, src interface{}) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| const systemConfigPath = "/etc/alr/alr.toml" |  | ||||||
|  |  | ||||||
| func (c *ALRConfig) Load() error { | func (c *ALRConfig) Load() error { | ||||||
| 	systemConfig, err := readConfig( | 	systemConfig, err := readConfig( | ||||||
| 		systemConfigPath, | 		constants.SystemConfigPath, | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		slog.Debug("Cannot read system config", "err", err) | 		slog.Debug("Cannot read system config", "err", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cfgDir, err := os.UserConfigDir() |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Debug("Cannot read user config directory") |  | ||||||
| 	} |  | ||||||
| 	userConfigPath := filepath.Join(cfgDir, "alr", "alr.toml") |  | ||||||
|  |  | ||||||
| 	userConfig, err := readConfig( |  | ||||||
| 		userConfigPath, |  | ||||||
| 	) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Debug("Cannot read user config") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	config := &types.Config{} | 	config := &types.Config{} | ||||||
|  |  | ||||||
| 	mergeStructs(config, defaultConfig) | 	mergeStructs(config, defaultConfig) | ||||||
| 	mergeStructs(config, systemConfig) | 	mergeStructs(config, systemConfig) | ||||||
| 	mergeStructs(config, userConfig) |  | ||||||
| 	err = env.Parse(config) | 	err = env.Parse(config) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -119,17 +103,13 @@ func (c *ALRConfig) Load() error { | |||||||
|  |  | ||||||
| 	c.cfg = config | 	c.cfg = config | ||||||
|  |  | ||||||
| 	cacheDir, err := os.UserCacheDir() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	c.paths = &Paths{} | 	c.paths = &Paths{} | ||||||
| 	c.paths.UserConfigPath = userConfigPath | 	c.paths.UserConfigPath = constants.SystemConfigPath | ||||||
| 	c.paths.CacheDir = filepath.Join(cacheDir, "alr") | 	c.paths.CacheDir = constants.SystemCachePath | ||||||
| 	c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") | 	c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") | ||||||
| 	c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") | 	c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") | ||||||
| 	c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") | 	c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") | ||||||
| 	c.initPaths() | 	// c.initPaths() | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -146,10 +126,6 @@ func (c *ALRConfig) AutoPull() bool { | |||||||
| 	return c.cfg.AutoPull | 	return c.cfg.AutoPull | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *ALRConfig) AllowRunAsRoot() bool { |  | ||||||
| 	return c.cfg.Unsafe.AllowRunAsRoot |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *ALRConfig) Repos() []types.Repo { | func (c *ALRConfig) Repos() []types.Repo { | ||||||
| 	return c.cfg.Repos | 	return c.cfg.Repos | ||||||
| } | } | ||||||
| @@ -170,26 +146,6 @@ func (c *ALRConfig) GetPaths() *Paths { | |||||||
| 	return c.paths | 	return c.paths | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *ALRConfig) initPaths() { |  | ||||||
| 	err := os.MkdirAll(filepath.Dir(c.paths.UserConfigPath), 0o755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Error(gotext.Get("Unable to create config directory"), "err", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = os.MkdirAll(c.paths.RepoDir, 0o755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = os.MkdirAll(c.paths.PkgsDir, 0o755) |  | ||||||
| 	if err != nil { |  | ||||||
| 		slog.Error(gotext.Get("Unable to create package cache directory"), "err", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *ALRConfig) SaveUserConfig() error { | func (c *ALRConfig) SaveUserConfig() error { | ||||||
| 	f, err := os.Create(c.paths.UserConfigPath) | 	f, err := os.Create(c.paths.UserConfigPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | // 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 constants | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SystemConfigPath = "/etc/alr/alr.toml" | ||||||
|  | 	SystemCachePath  = "/var/cache/alr" | ||||||
|  | 	AlrRunDir        = "/var/run/alr" | ||||||
|  | 	PrivilegedGroup  = "wheel" | ||||||
|  | ) | ||||||
							
								
								
									
										152
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | // 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 logger | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	chLog "github.com/charmbracelet/log" | ||||||
|  | 	"github.com/hashicorp/go-hclog" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type HCLoggerAdapter struct { | ||||||
|  | 	logger *Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func hclogLevelTochLog(level hclog.Level) chLog.Level { | ||||||
|  | 	switch level { | ||||||
|  | 	case hclog.Debug: | ||||||
|  | 		return chLog.DebugLevel | ||||||
|  | 	case hclog.Info: | ||||||
|  | 		return chLog.InfoLevel | ||||||
|  | 	case hclog.Warn: | ||||||
|  | 		return chLog.WarnLevel | ||||||
|  | 	case hclog.Error: | ||||||
|  | 		return chLog.ErrorLevel | ||||||
|  | 	} | ||||||
|  | 	return chLog.FatalLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Log(level hclog.Level, msg string, args ...interface{}) { | ||||||
|  | 	filteredArgs := make([]interface{}, 0, len(args)) | ||||||
|  | 	for i := 0; i < len(args); i += 2 { | ||||||
|  | 		if i+1 >= len(args) { | ||||||
|  | 			filteredArgs = append(filteredArgs, args[i]) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		key, ok := args[i].(string) | ||||||
|  | 		if !ok || key != "timestamp" { | ||||||
|  | 			filteredArgs = append(filteredArgs, args[i], args[i+1]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Start ugly hacks | ||||||
|  | 	// Ignore exit messages | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/331 | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/203 | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/192 | ||||||
|  | 	var chLogLevel chLog.Level | ||||||
|  | 	if msg == "plugin process exited" || | ||||||
|  | 		strings.HasPrefix(msg, "[ERR] plugin: stream copy 'stderr' error") || | ||||||
|  | 		strings.HasPrefix(msg, "[DEBUG] plugin") { | ||||||
|  | 		chLogLevel = chLog.DebugLevel | ||||||
|  | 	} else { | ||||||
|  | 		chLogLevel = hclogLevelTochLog(level) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	a.logger.l.Log(chLogLevel, msg, filteredArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Trace(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Trace, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Debug(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Debug, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Info(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Info, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Warn(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Warn, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Error(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Error, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsTrace() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.DebugLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsDebug() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.DebugLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsInfo() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.InfoLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsWarn() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.WarnLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsError() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.ErrorLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) ImpliedArgs() []interface{} { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) With(args ...interface{}) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Name() string { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Named(name string) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) ResetNamed(name string) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) SetLevel(level hclog.Level) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetHCLoggerAdapter() *HCLoggerAdapter { | ||||||
|  | 	return &HCLoggerAdapter{ | ||||||
|  | 		logger: logger, | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -22,96 +22,90 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"github.com/charmbracelet/lipgloss" | 	"github.com/charmbracelet/lipgloss" | ||||||
| 	"github.com/charmbracelet/log" |  | ||||||
|  | 	chLog "github.com/charmbracelet/log" | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Logger struct { | type Logger struct { | ||||||
| 	lOut slog.Handler | 	l *chLog.Logger | ||||||
| 	lErr slog.Handler |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func setupOutLogger() *log.Logger { | func setupLogger() *chLog.Logger { | ||||||
| 	styles := log.DefaultStyles() | 	styles := chLog.DefaultStyles() | ||||||
| 	logger := log.New(os.Stdout) | 	logger := chLog.New(os.Stderr) | ||||||
| 	styles.Levels[log.InfoLevel] = lipgloss.NewStyle(). | 	styles.Levels[chLog.InfoLevel] = lipgloss.NewStyle(). | ||||||
| 		SetString("-->"). | 		SetString("-->"). | ||||||
| 		Foreground(lipgloss.Color("35")) | 		Foreground(lipgloss.Color("35")) | ||||||
| 	logger.SetStyles(styles) | 	styles.Levels[chLog.ErrorLevel] = lipgloss.NewStyle(). | ||||||
| 	return logger |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func setupErrorLogger() *log.Logger { |  | ||||||
| 	styles := log.DefaultStyles() |  | ||||||
| 	styles.Levels[log.ErrorLevel] = lipgloss.NewStyle(). |  | ||||||
| 		SetString(gotext.Get("ERROR")). | 		SetString(gotext.Get("ERROR")). | ||||||
| 		Padding(0, 1, 0, 1). | 		Padding(0, 1, 0, 1). | ||||||
| 		Background(lipgloss.Color("204")). | 		Background(lipgloss.Color("204")). | ||||||
| 		Foreground(lipgloss.Color("0")) | 		Foreground(lipgloss.Color("0")) | ||||||
| 	logger := log.New(os.Stderr) |  | ||||||
| 	logger.SetStyles(styles) | 	logger.SetStyles(styles) | ||||||
| 	return logger | 	return logger | ||||||
| } | } | ||||||
|  |  | ||||||
| func New() *Logger { | func New() *Logger { | ||||||
| 	standardLogger := setupOutLogger() |  | ||||||
| 	errLogger := setupErrorLogger() |  | ||||||
| 	return &Logger{ | 	return &Logger{ | ||||||
| 		lOut: standardLogger, | 		l: setupLogger(), | ||||||
| 		lErr: errLogger, |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func slogLevelToLog(level slog.Level) log.Level { | func slogLevelToLog(level slog.Level) chLog.Level { | ||||||
| 	switch level { | 	switch level { | ||||||
| 	case slog.LevelDebug: | 	case slog.LevelDebug: | ||||||
| 		return log.DebugLevel | 		return chLog.DebugLevel | ||||||
| 	case slog.LevelInfo: | 	case slog.LevelInfo: | ||||||
| 		return log.InfoLevel | 		return chLog.InfoLevel | ||||||
| 	case slog.LevelWarn: | 	case slog.LevelWarn: | ||||||
| 		return log.WarnLevel | 		return chLog.WarnLevel | ||||||
| 	case slog.LevelError: | 	case slog.LevelError: | ||||||
| 		return log.ErrorLevel | 		return chLog.ErrorLevel | ||||||
| 	} | 	} | ||||||
| 	return log.FatalLevel | 	return chLog.FatalLevel | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Logger) SetLevel(level slog.Level) { | func (l *Logger) SetLevel(level slog.Level) { | ||||||
| 	l.lOut.(*log.Logger).SetLevel(slogLevelToLog(level)) | 	l.l.SetLevel(slogLevelToLog(level)) | ||||||
| 	l.lErr.(*log.Logger).SetLevel(slogLevelToLog(level)) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool { | func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool { | ||||||
| 	if level <= slog.LevelInfo { | 	return l.l.Enabled(ctx, level) | ||||||
| 		return l.lOut.Enabled(ctx, level) |  | ||||||
| 	} |  | ||||||
| 	return l.lErr.Enabled(ctx, level) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Logger) Handle(ctx context.Context, rec slog.Record) error { | func (l *Logger) Handle(ctx context.Context, rec slog.Record) error { | ||||||
| 	if rec.Level <= slog.LevelInfo { | 	return l.l.Handle(ctx, rec) | ||||||
| 		return l.lOut.Handle(ctx, rec) |  | ||||||
| 	} |  | ||||||
| 	return l.lErr.Handle(ctx, rec) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler { | func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler { | ||||||
| 	sl := *l | 	sl := *l | ||||||
| 	sl.lOut = l.lOut.WithAttrs(attrs) | 	sl.l = l.l.WithAttrs(attrs).(*chLog.Logger) | ||||||
| 	sl.lErr = l.lErr.WithAttrs(attrs) |  | ||||||
| 	return &sl | 	return &sl | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *Logger) WithGroup(name string) slog.Handler { | func (l *Logger) WithGroup(name string) slog.Handler { | ||||||
| 	sl := *l | 	sl := *l | ||||||
| 	sl.lOut = l.lOut.WithGroup(name) | 	sl.l = l.l.WithGroup(name).(*chLog.Logger) | ||||||
| 	sl.lErr = l.lErr.WithGroup(name) |  | ||||||
| 	return &sl | 	return &sl | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var logger *Logger | ||||||
|  |  | ||||||
| func SetupDefault() *Logger { | func SetupDefault() *Logger { | ||||||
| 	l := New() | 	logger = New() | ||||||
| 	logger := slog.New(l) | 	slogLogger := slog.New(logger) | ||||||
| 	slog.SetDefault(logger) | 	slog.SetDefault(slogLogger) | ||||||
| 	return l | 	return logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SetupForGoPlugin() { | ||||||
|  | 	logger.l.SetFormatter(chLog.JSONFormatter) | ||||||
|  | 	chLog.TimestampKey = "@timestamp" | ||||||
|  | 	chLog.MessageKey = "@message" | ||||||
|  | 	chLog.LevelKey = "@level" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetLogger() *Logger { | ||||||
|  | 	return logger | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,11 +25,11 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"runtime" | 	"runtime" | ||||||
|  | 	"slices" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/fakeroot" |  | ||||||
| 	"mvdan.cc/sh/v3/expand" | 	"mvdan.cc/sh/v3/expand" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| ) | ) | ||||||
| @@ -54,7 +54,7 @@ func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc { | |||||||
| 			Stderr: hc.Stderr, | 			Stderr: hc.Stderr, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		err = fakeroot.Apply(cmd) | 		err = Apply(cmd) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -108,6 +108,52 @@ func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func rootMap(m syscall.SysProcIDMap) bool { | ||||||
|  | 	return m.ContainerID == 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Apply(cmd *exec.Cmd) error { | ||||||
|  | 	uid := os.Getuid() | ||||||
|  | 	gid := os.Getgid() | ||||||
|  |  | ||||||
|  | 	// If the user is already root, there's no need for fakeroot | ||||||
|  | 	if uid == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Ensure SysProcAttr isn't nil | ||||||
|  | 	if cmd.SysProcAttr == nil { | ||||||
|  | 		cmd.SysProcAttr = &syscall.SysProcAttr{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create a new user namespace | ||||||
|  | 	cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWUSER | ||||||
|  |  | ||||||
|  | 	// If the command already contains a mapping for the root user, return an error | ||||||
|  | 	if slices.ContainsFunc(cmd.SysProcAttr.UidMappings, rootMap) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If the command already contains a mapping for the root group, return an error | ||||||
|  | 	if slices.ContainsFunc(cmd.SysProcAttr.GidMappings, rootMap) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd.SysProcAttr.UidMappings = append(cmd.SysProcAttr.UidMappings, syscall.SysProcIDMap{ | ||||||
|  | 		ContainerID: 0, | ||||||
|  | 		HostID:      uid, | ||||||
|  | 		Size:        1, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	cmd.SysProcAttr.GidMappings = append(cmd.SysProcAttr.GidMappings, syscall.SysProcIDMap{ | ||||||
|  | 		ContainerID: 0, | ||||||
|  | 		HostID:      gid, | ||||||
|  | 		Size:        1, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // execEnv was extracted from github.com/mvdan/sh/interp/vars.go | // execEnv was extracted from github.com/mvdan/sh/interp/vars.go | ||||||
| func execEnv(env expand.Environ) []string { | func execEnv(env expand.Environ) []string { | ||||||
| 	list := make([]string, 0, 64) | 	list := make([]string, 0, 64) | ||||||
|   | |||||||
| @@ -9,91 +9,83 @@ msgstr "" | |||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
|  |  | ||||||
| #: build.go:44 | #: build.go:42 | ||||||
| msgid "Build a local package" | msgid "Build a local package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:50 | #: build.go:48 | ||||||
| msgid "Path to the build script" | msgid "Path to the build script" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:55 | #: build.go:53 | ||||||
| msgid "Specify subpackage in script (for multi package script only)" | msgid "Specify subpackage in script (for multi package script only)" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:60 | #: build.go:58 | ||||||
| msgid "Name of the package to build and its repo (example: default/go-bin)" | msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:65 | #: build.go:63 | ||||||
| msgid "" | msgid "" | ||||||
| "Build package from scratch even if there's an already built package available" | "Build package from scratch even if there's an already built package available" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:73 | #: build.go:73 | ||||||
| msgid "Error loading config" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:81 |  | ||||||
| msgid "Error initialization database" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:110 |  | ||||||
| msgid "Package not found" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:130 |  | ||||||
| msgid "Error pulling repositories" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:138 |  | ||||||
| msgid "Unable to detect a supported package manager on the system" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:144 |  | ||||||
| msgid "Error parsing os release" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:166 |  | ||||||
| msgid "Error building package" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: build.go:173 |  | ||||||
| msgid "Error getting working directory" | msgid "Error getting working directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:182 | #: build.go:118 | ||||||
|  | msgid "Cannot get absolute script path" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:148 | ||||||
|  | msgid "Package not found" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:161 | ||||||
|  | msgid "Nothing to build" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:218 | ||||||
|  | msgid "Error building package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:225 | ||||||
| msgid "Error moving the package" | msgid "Error moving the package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:37 | #: build.go:229 | ||||||
|  | msgid "Done" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:38 | ||||||
| msgid "Attempt to fix problems with ALR" | msgid "Attempt to fix problems with ALR" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:49 | #: fix.go:59 | ||||||
| msgid "Removing cache directory" | msgid "Clearing cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:53 | #: fix.go:64 | ||||||
| msgid "Unable to remove cache directory" | msgid "Unable to open cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:57 | #: fix.go:70 | ||||||
|  | msgid "Unable to read cache directory contents" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:76 | ||||||
|  | msgid "Unable to remove cache item (%s)" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:80 | ||||||
| msgid "Rebuilding cache" | msgid "Rebuilding cache" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:61 | #: fix.go:84 | ||||||
| msgid "Unable to create new cache directory" | msgid "Unable to create new cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:81 |  | ||||||
| msgid "Error pulling repos" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: fix.go:85 |  | ||||||
| msgid "Done" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: gen.go:34 | #: gen.go:34 | ||||||
| msgid "Generate a ALR script from a template" | msgid "Generate a ALR script from a template" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -102,82 +94,106 @@ msgstr "" | |||||||
| msgid "Generate a ALR script for a pip module" | msgid "Generate a ALR script for a pip module" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: helper.go:41 | #: helper.go:42 | ||||||
| msgid "List all the available helper commands" | msgid "List all the available helper commands" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: helper.go:53 | #: helper.go:54 | ||||||
| msgid "Run a ALR helper command" | msgid "Run a ALR helper command" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: helper.go:60 | #: helper.go:61 | ||||||
| msgid "The directory that the install commands will install to" | msgid "The directory that the install commands will install to" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: helper.go:73 | #: helper.go:74 helper.go:75 | ||||||
| msgid "No such helper command" | msgid "No such helper command" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:43 | #: helper.go:85 | ||||||
| msgid "Print information about a package" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:48 |  | ||||||
| msgid "Show all information, not just for the current distro" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:69 |  | ||||||
| msgid "Error getting packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:78 |  | ||||||
| msgid "Error iterating over packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:105 |  | ||||||
| msgid "Command info expected at least 1 argument, got %d" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:119 |  | ||||||
| msgid "Error finding packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: info.go:144 |  | ||||||
| msgid "Error parsing os-release file" | msgid "Error parsing os-release file" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:153 | #: info.go:42 | ||||||
|  | msgid "Print information about a package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:47 | ||||||
|  | msgid "Show all information, not just for the current distro" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:68 | ||||||
|  | msgid "Error getting packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:76 | ||||||
|  | msgid "Error iterating over packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:90 | ||||||
|  | msgid "Command info expected at least 1 argument, got %d" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:110 | ||||||
|  | msgid "Error finding packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:124 | ||||||
|  | msgid "Can't detect system language" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:141 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:162 info.go:168 | #: info.go:149 info.go:154 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:43 | #: install.go:40 | ||||||
| msgid "Install a new package" | msgid "Install a new package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:57 | #: install.go:56 | ||||||
| msgid "Command install expected at least 1 argument, got %d" | msgid "Command install expected at least 1 argument, got %d" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:118 | ||||||
|  | msgid "Error parsing os release" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:163 | #: install.go:163 | ||||||
| msgid "Remove an installed package" | msgid "Remove an installed package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:188 | #: install.go:182 | ||||||
| msgid "Error listing installed packages" | msgid "Error listing installed packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:226 | #: install.go:223 | ||||||
| msgid "Command remove expected at least 1 argument, got %d" | msgid "Command remove expected at least 1 argument, got %d" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:241 | #: install.go:238 | ||||||
| msgid "Error removing packages" | msgid "Error removing packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:75 | ||||||
|  | msgid "Error loading config" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:96 | ||||||
|  | msgid "Error initialization database" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:135 | ||||||
|  | msgid "Error pulling repositories" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:165 | ||||||
|  | msgid "Unable to detect a supported package manager on the system" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: internal/cliutils/prompt.go:60 | #: internal/cliutils/prompt.go:60 | ||||||
| msgid "Would you like to view the build script for %s" | msgid "Would you like to view the build script for %s" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -258,18 +274,6 @@ msgstr "" | |||||||
| msgid "OPTIONS" | msgid "OPTIONS" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/config/config.go:176 |  | ||||||
| msgid "Unable to create config directory" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: internal/config/config.go:182 |  | ||||||
| msgid "Unable to create repo cache directory" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: internal/config/config.go:188 |  | ||||||
| msgid "Unable to create package cache directory" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: internal/db/db.go:133 | #: internal/db/db.go:133 | ||||||
| msgid "Database version mismatch; resetting" | msgid "Database version mismatch; resetting" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -303,10 +307,22 @@ msgstr "" | |||||||
| msgid "%s %s downloading at %s/s\n" | msgid "%s %s downloading at %s/s\n" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/logger/log.go:47 | #: internal/logger/log.go:41 | ||||||
| msgid "ERROR" | msgid "ERROR" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:95 | ||||||
|  | msgid "Error dropping capabilities" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:123 | ||||||
|  | msgid "You need to be root to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:165 | ||||||
|  | msgid "You need to be a %s member to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: list.go:41 | #: list.go:41 | ||||||
| msgid "List ALR repo packages" | msgid "List ALR repo packages" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -323,86 +339,40 @@ msgstr "" | |||||||
| msgid "Enable interactive questions and prompts" | msgid "Enable interactive questions and prompts" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:96 | #: main.go:145 | ||||||
| msgid "" |  | ||||||
| "Running ALR as root is forbidden as it may cause catastrophic damage to your " |  | ||||||
| "system" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: main.go:154 |  | ||||||
| msgid "Show help" | msgid "Show help" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:158 | #: main.go:149 | ||||||
| msgid "Error while running app" | msgid "Error while running app" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:157 | #: pkg/build/build.go:394 | ||||||
| msgid "Failed to prompt user to view build script" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:161 |  | ||||||
| msgid "Building package" | msgid "Building package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:209 | #: pkg/build/build.go:423 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:238 | #: pkg/build/build.go:454 | ||||||
| msgid "Downloading sources" | msgid "Downloading sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:260 | #: pkg/build/build.go:543 | ||||||
| msgid "Building package metadata" | msgid "Installing dependencies" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:282 | #: pkg/build/checker.go:43 | ||||||
| msgid "Compressing package" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:441 |  | ||||||
| msgid "" | msgid "" | ||||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
| "build anyway?" | "build anyway?" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:455 | #: pkg/build/checker.go:67 | ||||||
| msgid "This package is already installed" | msgid "This package is already installed" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:479 |  | ||||||
| msgid "Installing build dependencies" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:524 |  | ||||||
| msgid "Installing dependencies" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:605 |  | ||||||
| msgid "Would you like to remove the build dependencies?" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:668 |  | ||||||
| msgid "Executing prepare()" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:678 |  | ||||||
| msgid "Executing build()" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:708 pkg/build/build.go:728 |  | ||||||
| msgid "Executing %s()" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:787 |  | ||||||
| msgid "Error installing native packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:811 |  | ||||||
| msgid "Error installing package" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: pkg/build/find_deps/alt_linux.go:35 | #: pkg/build/find_deps/alt_linux.go:35 | ||||||
| msgid "Command not found on the system" | msgid "Command not found on the system" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -423,6 +393,22 @@ msgstr "" | |||||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:237 | ||||||
|  | msgid "Building package metadata" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:356 | ||||||
|  | msgid "Executing prepare()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:365 | ||||||
|  | msgid "Executing build()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:394 pkg/build/script_executor.go:414 | ||||||
|  | msgid "Executing %s()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/repos/pull.go:79 | #: pkg/repos/pull.go:79 | ||||||
| msgid "Pulling repository" | msgid "Pulling repository" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -441,43 +427,47 @@ msgid "" | |||||||
| "updating ALR if something doesn't work." | "updating ALR if something doesn't work." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:40 | #: repo.go:39 | ||||||
| msgid "Add a new repository" | msgid "Add a new repository" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:47 | #: repo.go:46 | ||||||
| msgid "Name of the new repo" | msgid "Name of the new repo" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:53 | #: repo.go:52 | ||||||
| msgid "URL of the new repo" | msgid "URL of the new repo" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:86 repo.go:156 | #: repo.go:79 | ||||||
|  | msgid "Repo %s already exists" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:90 repo.go:167 | ||||||
| msgid "Error saving config" | msgid "Error saving config" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:111 | #: repo.go:116 | ||||||
| msgid "Remove an existing repository" | msgid "Remove an existing repository" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:118 | #: repo.go:123 | ||||||
| msgid "Name of the repo to be deleted" | msgid "Name of the repo to be deleted" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:142 | #: repo.go:156 | ||||||
| msgid "Repo does not exist" | msgid "Repo \"%s\" does not exist" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:150 | #: repo.go:163 | ||||||
| msgid "Error removing repo directory" | msgid "Error removing repo directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:167 | #: repo.go:186 | ||||||
| msgid "Error removing packages from database" | msgid "Error removing packages from database" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:179 | #: repo.go:197 | ||||||
| msgid "Pull all repositories that have changed" | msgid "Pull all repositories that have changed" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -505,11 +495,15 @@ msgstr "" | |||||||
| msgid "Format output using a Go template" | msgid "Format output using a Go template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: search.go:88 search.go:105 | #: search.go:96 | ||||||
|  | msgid "Error while executing search" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:104 | ||||||
| msgid "Error parsing format template" | msgid "Error parsing format template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: search.go:113 | #: search.go:112 | ||||||
| msgid "Error executing template" | msgid "Error executing template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -517,10 +511,10 @@ msgstr "" | |||||||
| msgid "Upgrade all installed packages" | msgid "Upgrade all installed packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: upgrade.go:96 | #: upgrade.go:109 upgrade.go:126 | ||||||
| msgid "Error checking for updates" | msgid "Error checking for updates" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: upgrade.go:118 | #: upgrade.go:129 | ||||||
| msgid "There is nothing to do." | msgid "There is nothing to do." | ||||||
| msgstr "" | msgstr "" | ||||||
|   | |||||||
| @@ -16,92 +16,88 @@ msgstr "" | |||||||
| "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" | ||||||
| "X-Generator: Gtranslator 47.1\n" | "X-Generator: Gtranslator 47.1\n" | ||||||
|  |  | ||||||
| #: build.go:44 | #: build.go:42 | ||||||
| msgid "Build a local package" | msgid "Build a local package" | ||||||
| msgstr "Сборка локального пакета" | msgstr "Сборка локального пакета" | ||||||
|  |  | ||||||
| #: build.go:50 | #: build.go:48 | ||||||
| msgid "Path to the build script" | msgid "Path to the build script" | ||||||
| msgstr "Путь к скрипту сборки" | msgstr "Путь к скрипту сборки" | ||||||
|  |  | ||||||
| #: build.go:55 | #: build.go:53 | ||||||
| msgid "Specify subpackage in script (for multi package script only)" | msgid "Specify subpackage in script (for multi package script only)" | ||||||
| msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)" | msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)" | ||||||
|  |  | ||||||
| #: build.go:60 | #: build.go:58 | ||||||
| msgid "Name of the package to build and its repo (example: default/go-bin)" | msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||||
| msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)" | msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)" | ||||||
|  |  | ||||||
| #: build.go:65 | #: build.go:63 | ||||||
| msgid "" | msgid "" | ||||||
| "Build package from scratch even if there's an already built package available" | "Build package from scratch even if there's an already built package available" | ||||||
| msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | ||||||
|  |  | ||||||
| #: build.go:73 | #: build.go:73 | ||||||
| #, fuzzy |  | ||||||
| msgid "Error loading config" |  | ||||||
| msgstr "Ошибка при кодировании конфигурации" |  | ||||||
|  |  | ||||||
| #: build.go:81 |  | ||||||
| msgid "Error initialization database" |  | ||||||
| msgstr "Ошибка инициализации базы данных" |  | ||||||
|  |  | ||||||
| #: build.go:110 |  | ||||||
| msgid "Package not found" |  | ||||||
| msgstr "Пакет не найден" |  | ||||||
|  |  | ||||||
| #: build.go:130 |  | ||||||
| msgid "Error pulling repositories" |  | ||||||
| msgstr "Ошибка при извлечении репозиториев" |  | ||||||
|  |  | ||||||
| #: build.go:138 |  | ||||||
| msgid "Unable to detect a supported package manager on the system" |  | ||||||
| msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" |  | ||||||
|  |  | ||||||
| #: build.go:144 |  | ||||||
| msgid "Error parsing os release" |  | ||||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" |  | ||||||
|  |  | ||||||
| #: build.go:166 |  | ||||||
| msgid "Error building package" |  | ||||||
| msgstr "Ошибка при сборке пакета" |  | ||||||
|  |  | ||||||
| #: build.go:173 |  | ||||||
| msgid "Error getting working directory" | msgid "Error getting working directory" | ||||||
| msgstr "Ошибка при получении рабочего каталога" | msgstr "Ошибка при получении рабочего каталога" | ||||||
|  |  | ||||||
| #: build.go:182 | #: build.go:118 | ||||||
|  | msgid "Cannot get absolute script path" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:148 | ||||||
|  | msgid "Package not found" | ||||||
|  | msgstr "Пакет не найден" | ||||||
|  |  | ||||||
|  | #: build.go:161 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Nothing to build" | ||||||
|  | msgstr "Исполнение build()" | ||||||
|  |  | ||||||
|  | #: build.go:218 | ||||||
|  | msgid "Error building package" | ||||||
|  | msgstr "Ошибка при сборке пакета" | ||||||
|  |  | ||||||
|  | #: build.go:225 | ||||||
| msgid "Error moving the package" | msgid "Error moving the package" | ||||||
| msgstr "Ошибка при перемещении пакета" | msgstr "Ошибка при перемещении пакета" | ||||||
|  |  | ||||||
| #: fix.go:37 | #: build.go:229 | ||||||
|  | msgid "Done" | ||||||
|  | msgstr "Сделано" | ||||||
|  |  | ||||||
|  | #: fix.go:38 | ||||||
| msgid "Attempt to fix problems with ALR" | msgid "Attempt to fix problems with ALR" | ||||||
| msgstr "Попытка устранить проблемы с ALR" | msgstr "Попытка устранить проблемы с ALR" | ||||||
|  |  | ||||||
| #: fix.go:49 | #: fix.go:59 | ||||||
| msgid "Removing cache directory" | #, fuzzy | ||||||
|  | msgid "Clearing cache directory" | ||||||
| msgstr "Удаление каталога кэша" | msgstr "Удаление каталога кэша" | ||||||
|  |  | ||||||
| #: fix.go:53 | #: fix.go:64 | ||||||
| msgid "Unable to remove cache directory" | #, fuzzy | ||||||
|  | msgid "Unable to open cache directory" | ||||||
| msgstr "Не удалось удалить каталог кэша" | msgstr "Не удалось удалить каталог кэша" | ||||||
|  |  | ||||||
| #: fix.go:57 | #: fix.go:70 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Unable to read cache directory contents" | ||||||
|  | msgstr "Не удалось удалить каталог кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:76 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Unable to remove cache item (%s)" | ||||||
|  | msgstr "Не удалось удалить каталог кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:80 | ||||||
| msgid "Rebuilding cache" | msgid "Rebuilding cache" | ||||||
| msgstr "Восстановление кэша" | msgstr "Восстановление кэша" | ||||||
|  |  | ||||||
| #: fix.go:61 | #: fix.go:84 | ||||||
| msgid "Unable to create new cache directory" | msgid "Unable to create new cache directory" | ||||||
| msgstr "Не удалось создать новый каталог кэша" | msgstr "Не удалось создать новый каталог кэша" | ||||||
|  |  | ||||||
| #: fix.go:81 |  | ||||||
| msgid "Error pulling repos" |  | ||||||
| msgstr "Ошибка при извлечении репозиториев" |  | ||||||
|  |  | ||||||
| #: fix.go:85 |  | ||||||
| msgid "Done" |  | ||||||
| msgstr "Сделано" |  | ||||||
|  |  | ||||||
| #: gen.go:34 | #: gen.go:34 | ||||||
| msgid "Generate a ALR script from a template" | msgid "Generate a ALR script from a template" | ||||||
| msgstr "Генерация скрипта ALR из шаблона" | msgstr "Генерация скрипта ALR из шаблона" | ||||||
| @@ -110,82 +106,108 @@ msgstr "Генерация скрипта ALR из шаблона" | |||||||
| msgid "Generate a ALR script for a pip module" | msgid "Generate a ALR script for a pip module" | ||||||
| msgstr "Генерация скрипта ALR для модуля pip" | msgstr "Генерация скрипта ALR для модуля pip" | ||||||
|  |  | ||||||
| #: helper.go:41 | #: helper.go:42 | ||||||
| msgid "List all the available helper commands" | msgid "List all the available helper commands" | ||||||
| msgstr "Список всех доступных вспомогательных команды" | msgstr "Список всех доступных вспомогательных команды" | ||||||
|  |  | ||||||
| #: helper.go:53 | #: helper.go:54 | ||||||
| msgid "Run a ALR helper command" | msgid "Run a ALR helper command" | ||||||
| msgstr "Запустить вспомогательную команду ALR" | msgstr "Запустить вспомогательную команду ALR" | ||||||
|  |  | ||||||
| #: helper.go:60 | #: helper.go:61 | ||||||
| msgid "The directory that the install commands will install to" | msgid "The directory that the install commands will install to" | ||||||
| msgstr "Каталог, в который будут устанавливать команды установки" | msgstr "Каталог, в который будут устанавливать команды установки" | ||||||
|  |  | ||||||
| #: helper.go:73 | #: helper.go:74 helper.go:75 | ||||||
| msgid "No such helper command" | msgid "No such helper command" | ||||||
| msgstr "Такой вспомогательной команды нет" | msgstr "Такой вспомогательной команды нет" | ||||||
|  |  | ||||||
| #: info.go:43 | #: helper.go:85 | ||||||
| msgid "Print information about a package" |  | ||||||
| msgstr "Отобразить информацию о пакете" |  | ||||||
|  |  | ||||||
| #: info.go:48 |  | ||||||
| msgid "Show all information, not just for the current distro" |  | ||||||
| msgstr "Показывать всю информацию, не только для текущего дистрибутива" |  | ||||||
|  |  | ||||||
| #: info.go:69 |  | ||||||
| msgid "Error getting packages" |  | ||||||
| msgstr "Ошибка при получении пакетов" |  | ||||||
|  |  | ||||||
| #: info.go:78 |  | ||||||
| msgid "Error iterating over packages" |  | ||||||
| msgstr "Ошибка при переборе пакетов" |  | ||||||
|  |  | ||||||
| #: info.go:105 |  | ||||||
| msgid "Command info expected at least 1 argument, got %d" |  | ||||||
| msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" |  | ||||||
|  |  | ||||||
| #: info.go:119 |  | ||||||
| msgid "Error finding packages" |  | ||||||
| msgstr "Ошибка при поиске пакетов" |  | ||||||
|  |  | ||||||
| #: info.go:144 |  | ||||||
| msgid "Error parsing os-release file" | msgid "Error parsing os-release file" | ||||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
| #: info.go:153 | #: info.go:42 | ||||||
|  | msgid "Print information about a package" | ||||||
|  | msgstr "Отобразить информацию о пакете" | ||||||
|  |  | ||||||
|  | #: info.go:47 | ||||||
|  | msgid "Show all information, not just for the current distro" | ||||||
|  | msgstr "Показывать всю информацию, не только для текущего дистрибутива" | ||||||
|  |  | ||||||
|  | #: info.go:68 | ||||||
|  | msgid "Error getting packages" | ||||||
|  | msgstr "Ошибка при получении пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:76 | ||||||
|  | msgid "Error iterating over packages" | ||||||
|  | msgstr "Ошибка при переборе пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:90 | ||||||
|  | msgid "Command info expected at least 1 argument, got %d" | ||||||
|  | msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
|  | #: info.go:110 | ||||||
|  | msgid "Error finding packages" | ||||||
|  | msgstr "Ошибка при поиске пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:124 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Can't detect system language" | ||||||
|  | msgstr "Ошибка при парсинге языка системы" | ||||||
|  |  | ||||||
|  | #: info.go:141 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "Ошибка устранения переорпеделений" | msgstr "Ошибка устранения переорпеделений" | ||||||
|  |  | ||||||
| #: info.go:162 info.go:168 | #: info.go:149 info.go:154 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "Ошибка кодирования переменных скрита" | msgstr "Ошибка кодирования переменных скрита" | ||||||
|  |  | ||||||
| #: install.go:43 | #: install.go:40 | ||||||
| msgid "Install a new package" | msgid "Install a new package" | ||||||
| msgstr "Установить новый пакет" | msgstr "Установить новый пакет" | ||||||
|  |  | ||||||
| #: install.go:57 | #: install.go:56 | ||||||
| msgid "Command install expected at least 1 argument, got %d" | msgid "Command install expected at least 1 argument, got %d" | ||||||
| msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" | msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
|  | #: install.go:118 | ||||||
|  | msgid "Error parsing os release" | ||||||
|  | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
| #: install.go:163 | #: install.go:163 | ||||||
| msgid "Remove an installed package" | msgid "Remove an installed package" | ||||||
| msgstr "Удалить установленный пакет" | msgstr "Удалить установленный пакет" | ||||||
|  |  | ||||||
| #: install.go:188 | #: install.go:182 | ||||||
| msgid "Error listing installed packages" | msgid "Error listing installed packages" | ||||||
| msgstr "Ошибка при составлении списка установленных пакетов" | msgstr "Ошибка при составлении списка установленных пакетов" | ||||||
|  |  | ||||||
| #: install.go:226 | #: install.go:223 | ||||||
| msgid "Command remove expected at least 1 argument, got %d" | msgid "Command remove expected at least 1 argument, got %d" | ||||||
| msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" | msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
| #: install.go:241 | #: install.go:238 | ||||||
| msgid "Error removing packages" | msgid "Error removing packages" | ||||||
| msgstr "Ошибка при удалении пакетов" | msgstr "Ошибка при удалении пакетов" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:75 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Error loading config" | ||||||
|  | msgstr "Ошибка при кодировании конфигурации" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:96 | ||||||
|  | msgid "Error initialization database" | ||||||
|  | msgstr "Ошибка инициализации базы данных" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:135 | ||||||
|  | msgid "Error pulling repositories" | ||||||
|  | msgstr "Ошибка при извлечении репозиториев" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:165 | ||||||
|  | msgid "Unable to detect a supported package manager on the system" | ||||||
|  | msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" | ||||||
|  |  | ||||||
| #: internal/cliutils/prompt.go:60 | #: internal/cliutils/prompt.go:60 | ||||||
| msgid "Would you like to view the build script for %s" | msgid "Would you like to view the build script for %s" | ||||||
| msgstr "Показать скрипт для пакета %s" | msgstr "Показать скрипт для пакета %s" | ||||||
| @@ -266,19 +288,6 @@ msgstr "КАТЕГОРИЯ" | |||||||
| msgid "OPTIONS" | msgid "OPTIONS" | ||||||
| msgstr "ПАРАМЕТРЫ" | msgstr "ПАРАМЕТРЫ" | ||||||
|  |  | ||||||
| #: internal/config/config.go:176 |  | ||||||
| #, fuzzy |  | ||||||
| msgid "Unable to create config directory" |  | ||||||
| msgstr "Не удалось создать каталог конфигурации ALR" |  | ||||||
|  |  | ||||||
| #: internal/config/config.go:182 |  | ||||||
| msgid "Unable to create repo cache directory" |  | ||||||
| msgstr "Не удалось создать каталог кэша репозитория" |  | ||||||
|  |  | ||||||
| #: internal/config/config.go:188 |  | ||||||
| msgid "Unable to create package cache directory" |  | ||||||
| msgstr "Не удалось создать каталог кэша пакетов" |  | ||||||
|  |  | ||||||
| #: internal/db/db.go:133 | #: internal/db/db.go:133 | ||||||
| msgid "Database version mismatch; resetting" | msgid "Database version mismatch; resetting" | ||||||
| msgstr "Несоответствие версий базы данных; сброс настроек" | msgstr "Несоответствие версий базы данных; сброс настроек" | ||||||
| @@ -313,10 +322,23 @@ msgstr "%s: выполнено!\n" | |||||||
| msgid "%s %s downloading at %s/s\n" | msgid "%s %s downloading at %s/s\n" | ||||||
| msgstr "%s %s загружается — %s/с\n" | msgstr "%s %s загружается — %s/с\n" | ||||||
|  |  | ||||||
| #: internal/logger/log.go:47 | #: internal/logger/log.go:41 | ||||||
| msgid "ERROR" | msgid "ERROR" | ||||||
| msgstr "ОШИБКА" | msgstr "ОШИБКА" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:95 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Error dropping capabilities" | ||||||
|  | msgstr "Ошибка при открытии базы данных" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:123 | ||||||
|  | msgid "You need to be root to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:165 | ||||||
|  | msgid "You need to be a %s member to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: list.go:41 | #: list.go:41 | ||||||
| msgid "List ALR repo packages" | msgid "List ALR repo packages" | ||||||
| msgstr "Список пакетов репозитория ALR" | msgstr "Список пакетов репозитория ALR" | ||||||
| @@ -333,47 +355,31 @@ msgstr "Аргументы, которые будут переданы мене | |||||||
| msgid "Enable interactive questions and prompts" | msgid "Enable interactive questions and prompts" | ||||||
| msgstr "Включение интерактивных вопросов и запросов" | msgstr "Включение интерактивных вопросов и запросов" | ||||||
|  |  | ||||||
| #: main.go:96 | #: main.go:145 | ||||||
| msgid "" |  | ||||||
| "Running ALR as root is forbidden as it may cause catastrophic damage to your " |  | ||||||
| "system" |  | ||||||
| msgstr "" |  | ||||||
| "Запуск ALR от имени root запрещён, так как это может привести к " |  | ||||||
| "катастрофическому повреждению вашей системы" |  | ||||||
|  |  | ||||||
| #: main.go:154 |  | ||||||
| msgid "Show help" | msgid "Show help" | ||||||
| msgstr "Показать справку" | msgstr "Показать справку" | ||||||
|  |  | ||||||
| #: main.go:158 | #: main.go:149 | ||||||
| msgid "Error while running app" | msgid "Error while running app" | ||||||
| msgstr "Ошибка при запуске приложения" | msgstr "Ошибка при запуске приложения" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:157 | #: pkg/build/build.go:394 | ||||||
| msgid "Failed to prompt user to view build script" |  | ||||||
| msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:161 |  | ||||||
| msgid "Building package" | msgid "Building package" | ||||||
| msgstr "Сборка пакета" | msgstr "Сборка пакета" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:209 | #: pkg/build/build.go:423 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:238 | #: pkg/build/build.go:454 | ||||||
| msgid "Downloading sources" | msgid "Downloading sources" | ||||||
| msgstr "Скачивание источников" | msgstr "Скачивание источников" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:260 | #: pkg/build/build.go:543 | ||||||
| msgid "Building package metadata" | msgid "Installing dependencies" | ||||||
| msgstr "Сборка метаданных пакета" | msgstr "Установка зависимостей" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:282 | #: pkg/build/checker.go:43 | ||||||
| msgid "Compressing package" |  | ||||||
| msgstr "Сжатие пакета" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:441 |  | ||||||
| msgid "" | msgid "" | ||||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
| "build anyway?" | "build anyway?" | ||||||
| @@ -381,42 +387,10 @@ msgstr "" | |||||||
| "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||||
| "равно хотите выполнить сборку?" | "равно хотите выполнить сборку?" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:455 | #: pkg/build/checker.go:67 | ||||||
| msgid "This package is already installed" | msgid "This package is already installed" | ||||||
| msgstr "Этот пакет уже установлен" | msgstr "Этот пакет уже установлен" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:479 |  | ||||||
| msgid "Installing build dependencies" |  | ||||||
| msgstr "Установка зависимостей сборки" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:524 |  | ||||||
| msgid "Installing dependencies" |  | ||||||
| msgstr "Установка зависимостей" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:605 |  | ||||||
| msgid "Would you like to remove the build dependencies?" |  | ||||||
| msgstr "Хотели бы вы удалить зависимости сборки?" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:668 |  | ||||||
| msgid "Executing prepare()" |  | ||||||
| msgstr "Исполнение prepare()" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:678 |  | ||||||
| msgid "Executing build()" |  | ||||||
| msgstr "Исполнение build()" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:708 pkg/build/build.go:728 |  | ||||||
| msgid "Executing %s()" |  | ||||||
| msgstr "Исполнение %s()" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:787 |  | ||||||
| msgid "Error installing native packages" |  | ||||||
| msgstr "Ошибка при установке нативных пакетов" |  | ||||||
|  |  | ||||||
| #: pkg/build/build.go:811 |  | ||||||
| msgid "Error installing package" |  | ||||||
| msgstr "Ошибка при установке пакета" |  | ||||||
|  |  | ||||||
| #: pkg/build/find_deps/alt_linux.go:35 | #: pkg/build/find_deps/alt_linux.go:35 | ||||||
| msgid "Command not found on the system" | msgid "Command not found on the system" | ||||||
| msgstr "Команда не найдена в системе" | msgstr "Команда не найдена в системе" | ||||||
| @@ -439,6 +413,22 @@ msgid "AutoReq is not implemented for this package format, so it's skipped" | |||||||
| msgstr "" | msgstr "" | ||||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:237 | ||||||
|  | msgid "Building package metadata" | ||||||
|  | msgstr "Сборка метаданных пакета" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:356 | ||||||
|  | msgid "Executing prepare()" | ||||||
|  | msgstr "Исполнение prepare()" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:365 | ||||||
|  | msgid "Executing build()" | ||||||
|  | msgstr "Исполнение build()" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:394 pkg/build/script_executor.go:414 | ||||||
|  | msgid "Executing %s()" | ||||||
|  | msgstr "Исполнение %s()" | ||||||
|  |  | ||||||
| #: pkg/repos/pull.go:79 | #: pkg/repos/pull.go:79 | ||||||
| msgid "Pulling repository" | msgid "Pulling repository" | ||||||
| msgstr "Скачивание репозитория" | msgstr "Скачивание репозитория" | ||||||
| @@ -459,44 +449,50 @@ msgstr "" | |||||||
| "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " | "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " | ||||||
| "обновить ALR, если что-то не работает." | "обновить ALR, если что-то не работает." | ||||||
|  |  | ||||||
| #: repo.go:40 | #: repo.go:39 | ||||||
| msgid "Add a new repository" | msgid "Add a new repository" | ||||||
| msgstr "Добавить новый репозиторий" | msgstr "Добавить новый репозиторий" | ||||||
|  |  | ||||||
| #: repo.go:47 | #: repo.go:46 | ||||||
| msgid "Name of the new repo" | msgid "Name of the new repo" | ||||||
| msgstr "Название нового репозитория" | msgstr "Название нового репозитория" | ||||||
|  |  | ||||||
| #: repo.go:53 | #: repo.go:52 | ||||||
| msgid "URL of the new repo" | msgid "URL of the new repo" | ||||||
| msgstr "URL-адрес нового репозитория" | msgstr "URL-адрес нового репозитория" | ||||||
|  |  | ||||||
| #: repo.go:86 repo.go:156 | #: repo.go:79 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Repo %s already exists" | ||||||
|  | msgstr "Репозитория не существует" | ||||||
|  |  | ||||||
|  | #: repo.go:90 repo.go:167 | ||||||
| #, fuzzy | #, fuzzy | ||||||
| msgid "Error saving config" | msgid "Error saving config" | ||||||
| msgstr "Ошибка при кодировании конфигурации" | msgstr "Ошибка при кодировании конфигурации" | ||||||
|  |  | ||||||
| #: repo.go:111 | #: repo.go:116 | ||||||
| msgid "Remove an existing repository" | msgid "Remove an existing repository" | ||||||
| msgstr "Удалить существующий репозиторий" | msgstr "Удалить существующий репозиторий" | ||||||
|  |  | ||||||
| #: repo.go:118 | #: repo.go:123 | ||||||
| msgid "Name of the repo to be deleted" | msgid "Name of the repo to be deleted" | ||||||
| msgstr "Название репозитория  удалён" | msgstr "Название репозитория  удалён" | ||||||
|  |  | ||||||
| #: repo.go:142 | #: repo.go:156 | ||||||
| msgid "Repo does not exist" | #, fuzzy | ||||||
|  | msgid "Repo \"%s\" does not exist" | ||||||
| msgstr "Репозитория не существует" | msgstr "Репозитория не существует" | ||||||
|  |  | ||||||
| #: repo.go:150 | #: repo.go:163 | ||||||
| msgid "Error removing repo directory" | msgid "Error removing repo directory" | ||||||
| msgstr "Ошибка при удалении каталога репозитория" | msgstr "Ошибка при удалении каталога репозитория" | ||||||
|  |  | ||||||
| #: repo.go:167 | #: repo.go:186 | ||||||
| msgid "Error removing packages from database" | msgid "Error removing packages from database" | ||||||
| msgstr "Ошибка при удалении пакетов из базы данных" | msgstr "Ошибка при удалении пакетов из базы данных" | ||||||
|  |  | ||||||
| #: repo.go:179 | #: repo.go:197 | ||||||
| msgid "Pull all repositories that have changed" | msgid "Pull all repositories that have changed" | ||||||
| msgstr "Скачать все изменённые репозитории" | msgstr "Скачать все изменённые репозитории" | ||||||
|  |  | ||||||
| @@ -524,11 +520,16 @@ msgstr "Иcкать по provides" | |||||||
| msgid "Format output using a Go template" | msgid "Format output using a Go template" | ||||||
| msgstr "Формат выходных данных с использованием шаблона Go" | msgstr "Формат выходных данных с использованием шаблона Go" | ||||||
|  |  | ||||||
| #: search.go:88 search.go:105 | #: search.go:96 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Error while executing search" | ||||||
|  | msgstr "Ошибка при запуске приложения" | ||||||
|  |  | ||||||
|  | #: search.go:104 | ||||||
| msgid "Error parsing format template" | msgid "Error parsing format template" | ||||||
| msgstr "Ошибка при разборе шаблона" | msgstr "Ошибка при разборе шаблона" | ||||||
|  |  | ||||||
| #: search.go:113 | #: search.go:112 | ||||||
| msgid "Error executing template" | msgid "Error executing template" | ||||||
| msgstr "Ошибка при выполнении шаблона" | msgstr "Ошибка при выполнении шаблона" | ||||||
|  |  | ||||||
| @@ -536,14 +537,60 @@ msgstr "Ошибка при выполнении шаблона" | |||||||
| msgid "Upgrade all installed packages" | msgid "Upgrade all installed packages" | ||||||
| msgstr "Обновить все установленные пакеты" | msgstr "Обновить все установленные пакеты" | ||||||
|  |  | ||||||
| #: upgrade.go:96 | #: upgrade.go:109 upgrade.go:126 | ||||||
| msgid "Error checking for updates" | msgid "Error checking for updates" | ||||||
| msgstr "Ошибка при проверке обновлений" | msgstr "Ошибка при проверке обновлений" | ||||||
|  |  | ||||||
| #: upgrade.go:118 | #: upgrade.go:129 | ||||||
| msgid "There is nothing to do." | msgid "There is nothing to do." | ||||||
| msgstr "Здесь нечего делать." | msgstr "Здесь нечего делать." | ||||||
|  |  | ||||||
|  | #~ msgid "Error pulling repos" | ||||||
|  | #~ msgstr "Ошибка при извлечении репозиториев" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Error getting current executable" | ||||||
|  | #~ msgstr "Ошибка при получении рабочего каталога" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Error mounting" | ||||||
|  | #~ msgstr "Ошибка при кодировании конфигурации" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Unable to create config directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог конфигурации ALR" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to create repo cache directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог кэша репозитория" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to create package cache directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог кэша пакетов" | ||||||
|  |  | ||||||
|  | #~ msgid "" | ||||||
|  | #~ "Running ALR as root is forbidden as it may cause catastrophic damage to " | ||||||
|  | #~ "your system" | ||||||
|  | #~ msgstr "" | ||||||
|  | #~ "Запуск ALR от имени root запрещён, так как это может привести к " | ||||||
|  | #~ "катастрофическому повреждению вашей системы" | ||||||
|  |  | ||||||
|  | #~ msgid "Failed to prompt user to view build script" | ||||||
|  | #~ msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" | ||||||
|  |  | ||||||
|  | #~ msgid "Compressing package" | ||||||
|  | #~ msgstr "Сжатие пакета" | ||||||
|  |  | ||||||
|  | #~ msgid "Installing build dependencies" | ||||||
|  | #~ msgstr "Установка зависимостей сборки" | ||||||
|  |  | ||||||
|  | #~ msgid "Would you like to remove the build dependencies?" | ||||||
|  | #~ msgstr "Хотели бы вы удалить зависимости сборки?" | ||||||
|  |  | ||||||
|  | #~ msgid "Error installing native packages" | ||||||
|  | #~ msgstr "Ошибка при установке нативных пакетов" | ||||||
|  |  | ||||||
|  | #~ msgid "Error installing package" | ||||||
|  | #~ msgstr "Ошибка при установке пакета" | ||||||
|  |  | ||||||
| #~ msgid "Error opening config file, using defaults" | #~ msgid "Error opening config file, using defaults" | ||||||
| #~ msgstr "" | #~ msgstr "" | ||||||
| #~ "Ошибка при открытии конфигурационного файла, используются значения по " | #~ "Ошибка при открытии конфигурационного файла, используются значения по " | ||||||
| @@ -569,12 +616,6 @@ msgstr "Здесь нечего делать." | |||||||
| #~ msgid "Error opening config file" | #~ msgid "Error opening config file" | ||||||
| #~ msgstr "Ошибка при открытии конфигурационного файла" | #~ msgstr "Ошибка при открытии конфигурационного файла" | ||||||
|  |  | ||||||
| #~ msgid "Error parsing system language" |  | ||||||
| #~ msgstr "Ошибка при парсинге языка системы" |  | ||||||
|  |  | ||||||
| #~ msgid "Error opening database" |  | ||||||
| #~ msgstr "Ошибка при открытии базы данных" |  | ||||||
|  |  | ||||||
| #~ msgid "Executing version()" | #~ msgid "Executing version()" | ||||||
| #~ msgstr "Исполнение версия()" | #~ msgstr "Исполнение версия()" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,13 +19,11 @@ | |||||||
|  |  | ||||||
| package types | package types | ||||||
|  |  | ||||||
| import "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" |  | ||||||
|  |  | ||||||
| type BuildOpts struct { | type BuildOpts struct { | ||||||
| 	Script      string | 	// Script      string | ||||||
| 	Repository  string | 	// Repository  string | ||||||
| 	Packages    []string | 	// Packages    []string | ||||||
| 	Manager     manager.Manager | 	// Manager     manager.Manager | ||||||
| 	Clean       bool | 	Clean       bool | ||||||
| 	Interactive bool | 	Interactive bool | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,7 +25,6 @@ type Config struct { | |||||||
| 	PagerStyle       string   `toml:"pagerStyle" env:"ALR_PAGER_STYLE"` | 	PagerStyle       string   `toml:"pagerStyle" env:"ALR_PAGER_STYLE"` | ||||||
| 	IgnorePkgUpdates []string `toml:"ignorePkgUpdates"` | 	IgnorePkgUpdates []string `toml:"ignorePkgUpdates"` | ||||||
| 	Repos            []Repo   `toml:"repo"` | 	Repos            []Repo   `toml:"repo"` | ||||||
| 	Unsafe           Unsafe   `toml:"unsafe"` |  | ||||||
| 	AutoPull         bool     `toml:"autoPull" env:"ALR_AUTOPULL"` | 	AutoPull         bool     `toml:"autoPull" env:"ALR_AUTOPULL"` | ||||||
| 	LogLevel         string   `toml:"logLevel" env:"ALR_LOG_LEVEL"` | 	LogLevel         string   `toml:"logLevel" env:"ALR_LOG_LEVEL"` | ||||||
| } | } | ||||||
| @@ -35,7 +34,3 @@ type Repo struct { | |||||||
| 	Name string `toml:"name"` | 	Name string `toml:"name"` | ||||||
| 	URL  string `toml:"url"` | 	URL  string `toml:"url"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type Unsafe struct { |  | ||||||
| 	AllowRunAsRoot bool `toml:"allowRunAsRoot" env:"ALR_UNSAFE_ALLOW_RUN_AS_ROOT"` |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										186
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | |||||||
|  | // 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 utils | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"os" | ||||||
|  | 	"os/user" | ||||||
|  | 	"strconv" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func GetUidGidAlrUserString() (string, string, error) { | ||||||
|  | 	u, err := user.Lookup("alr") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return u.Uid, u.Gid, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetUidGidAlrUser() (int, int, error) { | ||||||
|  | 	strUid, strGid, err := GetUidGidAlrUserString() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	uid, err := strconv.Atoi(strUid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  | 	gid, err := strconv.Atoi(strGid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return uid, gid, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func DropCapsToAlrUser() error { | ||||||
|  | 	uid, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setgid(gid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setuid(uid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return EnsureIsAlrUser() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExitIfCantDropGidToAlr() cli.ExitCoder { | ||||||
|  | 	_, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("cannot get gid alr", err) | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setgid(gid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("cannot get setgid alr", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExitIfCantDropCapsToAlrUser attempts to drop capabilities to the already | ||||||
|  | // running user. Returns a cli.ExitCoder with an error if the operation fails. | ||||||
|  | // See also [ExitIfCantDropCapsToAlrUserNoPrivs] for a version that also applies | ||||||
|  | // no-new-privs. | ||||||
|  | func ExitIfCantDropCapsToAlrUser() cli.ExitCoder { | ||||||
|  | 	err := DropCapsToAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit(gotext.Get("Error dropping capabilities"), err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExitIfCantSetNoNewPrivs() cli.ExitCoder { | ||||||
|  | 	if err := NoNewPrivs(); err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("error no new privs", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExitIfCantDropCapsToAlrUserNoPrivs combines [ExitIfCantDropCapsToAlrUser] with [ExitIfCantSetNoNewPrivs] | ||||||
|  | func ExitIfCantDropCapsToAlrUserNoPrivs() cli.ExitCoder { | ||||||
|  | 	if err := ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExitIfNotRoot() error { | ||||||
|  | 	if os.Getuid() != 0 { | ||||||
|  | 		return cli.Exit(gotext.Get("You need to be root to perform this action"), 1) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EnsureIsAlrUser() error { | ||||||
|  | 	uid, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	newUid := syscall.Getuid() | ||||||
|  | 	if newUid != uid { | ||||||
|  | 		return errors.New("new uid don't matches requested") | ||||||
|  | 	} | ||||||
|  | 	newGid := syscall.Getgid() | ||||||
|  | 	if newGid != gid { | ||||||
|  | 		return errors.New("new gid don't matches requested") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EnuseIsPrivilegedGroupMember() error { | ||||||
|  | 	currentUser, err := user.Current() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	group, err := user.LookupGroup(constants.PrivilegedGroup) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	groups, err := currentUser.GroupIds() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, gid := range groups { | ||||||
|  | 		if gid == group.Gid { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return cliutils.FormatCliExit(gotext.Get("You need to be a %s member to perform this action", constants.PrivilegedGroup), nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRootGid() error { | ||||||
|  | 	return syscall.Setgid(0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRootUid() error { | ||||||
|  | 	return syscall.Setuid(0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRoot() error { | ||||||
|  | 	err := EscalateToRootUid() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = EscalateToRootGid() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								internal/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // 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 utils | ||||||
|  |  | ||||||
|  | import "golang.org/x/sys/unix" | ||||||
|  |  | ||||||
|  | func NoNewPrivs() error { | ||||||
|  | 	return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								list.go
									
									
									
									
									
								
							| @@ -22,17 +22,17 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" |  | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func ListCmd() *cli.Command { | func ListCmd() *cli.Command { | ||||||
| @@ -47,29 +47,26 @@ func ListCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
| 			cfg := config.New() |  | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			deps, err := appbuilder. | ||||||
| 			err = db.Init(ctx) | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				// autoPull only | ||||||
|  | 				WithRepos(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			rs := repos.New(cfg, db) | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			if cfg.AutoPull() { | 			cfg := deps.Cfg | ||||||
| 				err = rs.Pull(ctx, cfg.Repos()) | 			db := deps.DB | ||||||
| 				if err != nil { |  | ||||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) |  | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			where := "true" | 			where := "true" | ||||||
| 			args := []any(nil) | 			args := []any(nil) | ||||||
| @@ -80,8 +77,7 @@ func ListCmd() *cli.Command { | |||||||
|  |  | ||||||
| 			result, err := db.GetPkgs(ctx, where, args...) | 			result, err := db.GetPkgs(ctx, where, args...) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error getting packages"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			defer result.Close() | 			defer result.Close() | ||||||
|  |  | ||||||
| @@ -89,14 +85,13 @@ func ListCmd() *cli.Command { | |||||||
| 			if c.Bool("installed") { | 			if c.Bool("installed") { | ||||||
| 				mgr := manager.Detect() | 				mgr := manager.Detect() | ||||||
| 				if mgr == nil { | 				if mgr == nil { | ||||||
| 					slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) | 					return cli.Exit(gotext.Get("Unable to detect a supported package manager on the system"), 1) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				installed, err := mgr.ListInstalled(&manager.Opts{AsRoot: false}) | 				installed, err := mgr.ListInstalled(&manager.Opts{}) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error listing installed packages"), "err", err) | 					slog.Error(gotext.Get("Error listing installed packages"), "err", err) | ||||||
| 					os.Exit(1) | 					return cli.Exit(err, 1) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				for pkgName, version := range installed { | 				for pkgName, version := range installed { | ||||||
| @@ -113,7 +108,7 @@ func ListCmd() *cli.Command { | |||||||
| 				var pkg database.Package | 				var pkg database.Package | ||||||
| 				err := result.StructScan(&pkg) | 				err := result.StructScan(&pkg) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return err | 					return cli.Exit(err, 1) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) { | 				if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) { | ||||||
| @@ -133,11 +128,6 @@ func ListCmd() *cli.Command { | |||||||
| 				fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) | 				fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error iterating over packages"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								main.go
									
									
									
									
									
								
							| @@ -82,29 +82,22 @@ func GetApp() *cli.App { | |||||||
| 			HelperCmd(), | 			HelperCmd(), | ||||||
| 			VersionCmd(), | 			VersionCmd(), | ||||||
| 			SearchCmd(), | 			SearchCmd(), | ||||||
|  | 			// Internal commands | ||||||
|  | 			InternalBuildCmd(), | ||||||
|  | 			InternalInstallCmd(), | ||||||
|  | 			InternalMountCmd(), | ||||||
| 		}, | 		}, | ||||||
| 		Before: func(c *cli.Context) error { | 		Before: func(c *cli.Context) error { | ||||||
| 			cfg := config.New() |  | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			cmd := c.Args().First() |  | ||||||
| 			if cmd != "helper" && !cfg.AllowRunAsRoot() && os.Geteuid() == 0 { |  | ||||||
| 				slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system")) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { | 			if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { | ||||||
| 				args := strings.Split(trimmed, " ") | 				args := strings.Split(trimmed, " ") | ||||||
| 				manager.Args = append(manager.Args, args...) | 				manager.Args = append(manager.Args, args...) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 		EnableBashCompletion: true, | 		EnableBashCompletion: true, | ||||||
|  | 		ExitErrHandler: func(cCtx *cli.Context, err error) { | ||||||
|  | 			cliutils.HandleExitCoder(err) | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -142,8 +135,6 @@ func main() { | |||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| 	setLogLevel(cfg.LogLevel()) | 	setLogLevel(cfg.LogLevel()) | ||||||
| 	// Set the root command to the one set in the ALR config |  | ||||||
| 	manager.DefaultRootCmd = cfg.RootCmd() |  | ||||||
|  |  | ||||||
| 	ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) | 	ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|   | |||||||
							
								
								
									
										1179
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
							
						
						
									
										1179
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -277,7 +277,7 @@ meta_bar() { | |||||||
| 			fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh") | 			fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh") | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 			_, allVars, err := b.executeFirstPass(fl) | 			_, allVars, err := b.scriptExecutor.ExecuteSecondPass(fl) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 			tc.Expected(t, allVars) | 			tc.Expected(t, allVars) | ||||||
							
								
								
									
										69
									
								
								pkg/build/cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								pkg/build/cache.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
|  | 	"github.com/goreleaser/nfpm/v2" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Cache struct { | ||||||
|  | 	cfg Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Cache) CheckForBuiltPackage( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	vars *types.BuildVars, | ||||||
|  | ) (string, bool, error) { | ||||||
|  | 	filename, err := pkgFileName(input, vars) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pkgPath := filepath.Join(getBaseDir(c.cfg, vars.Name), filename) | ||||||
|  |  | ||||||
|  | 	_, err = os.Stat(pkgPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", false, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pkgPath, true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func pkgFileName( | ||||||
|  | 	input interface { | ||||||
|  | 		OsInfoProvider | ||||||
|  | 		PkgFormatProvider | ||||||
|  | 		RepositoryProvider | ||||||
|  | 	}, | ||||||
|  | 	vars *types.BuildVars, | ||||||
|  | ) (string, error) { | ||||||
|  | 	pkgInfo := getBasePkgInfo(vars, input) | ||||||
|  |  | ||||||
|  | 	packager, err := nfpm.Get(input.PkgFormat()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return packager.ConventionalFileName(pkgInfo), nil | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								pkg/build/checker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								pkg/build/checker.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"log/slog" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Checker struct { | ||||||
|  | 	mgr manager.Manager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Checker) PerformChecks( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	vars *types.BuildVars, | ||||||
|  | ) (bool, error) { | ||||||
|  | 	if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { // Проверяем совместимость архитектуры | ||||||
|  | 		cont, err := cliutils.YesNoPrompt( | ||||||
|  | 			ctx, | ||||||
|  | 			gotext.Get("Your system's CPU architecture doesn't match this package. Do you want to build anyway?"), | ||||||
|  | 			input.opts.Interactive, | ||||||
|  | 			true, | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return false, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !cont { | ||||||
|  | 			return false, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	installed, err := c.mgr.ListInstalled(nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	filename, err := pkgFileName(input, vars) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if instVer, ok := installed[filename]; ok { // Если пакет уже установлен, выводим предупреждение | ||||||
|  | 		slog.Warn(gotext.Get("This package is already installed"), | ||||||
|  | 			"name", vars.Name, | ||||||
|  | 			"version", instVer, | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								pkg/build/dirs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								pkg/build/dirs.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type BaseDirProvider interface { | ||||||
|  | 	BaseDir() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SrcDirProvider interface { | ||||||
|  | 	SrcDir() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type PkgDirProvider interface { | ||||||
|  | 	PkgDir() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptDirProvider interface { | ||||||
|  | 	ScriptDir() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getDirs( | ||||||
|  | 	cfg Config, | ||||||
|  | 	scriptPath string, | ||||||
|  | 	basePkg string, | ||||||
|  | ) (types.Directories, error) { | ||||||
|  | 	pkgsDir := cfg.GetPaths().PkgsDir | ||||||
|  |  | ||||||
|  | 	scriptPath, err := filepath.Abs(scriptPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return types.Directories{}, err | ||||||
|  | 	} | ||||||
|  | 	baseDir := filepath.Join(pkgsDir, basePkg) | ||||||
|  | 	return types.Directories{ | ||||||
|  | 		BaseDir:   getBaseDir(cfg, basePkg), | ||||||
|  | 		SrcDir:    getSrcDir(cfg, basePkg), | ||||||
|  | 		PkgDir:    filepath.Join(baseDir, "pkg"), | ||||||
|  | 		ScriptDir: getScriptDir(scriptPath), | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getBaseDir(cfg Config, basePkg string) string { | ||||||
|  | 	return filepath.Join(cfg.GetPaths().PkgsDir, basePkg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getSrcDir(cfg Config, basePkg string) string { | ||||||
|  | 	return filepath.Join(getBaseDir(cfg, basePkg), "src") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getScriptDir(scriptPath string) string { | ||||||
|  | 	return filepath.Dir(scriptPath) | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								pkg/build/installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								pkg/build/installer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewInstaller(mgr manager.Manager) *Installer { | ||||||
|  | 	return &Installer{ | ||||||
|  | 		mgr: mgr, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Installer struct{ mgr manager.Manager } | ||||||
|  |  | ||||||
|  | func (i *Installer) InstallLocal(paths []string) error { | ||||||
|  | 	return i.mgr.InstallLocal(nil, paths...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *Installer) Install(pkgs []string) error { | ||||||
|  | 	return i.mgr.Install(nil, pkgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (i *Installer) RemoveAlreadyInstalled(pkgs []string) ([]string, error) { | ||||||
|  | 	filteredPackages := []string{} | ||||||
|  |  | ||||||
|  | 	for _, dep := range pkgs { | ||||||
|  | 		installed, err := i.mgr.IsInstalled(dep) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		if installed { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		filteredPackages = append(filteredPackages, dep) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return filteredPackages, nil | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								pkg/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								pkg/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewMainBuilder( | ||||||
|  | 	cfg Config, | ||||||
|  | 	mgr manager.Manager, | ||||||
|  | 	repos PackageFinder, | ||||||
|  | 	scriptExecutor ScriptExecutor, | ||||||
|  | 	installerExecutor InstallerExecutor, | ||||||
|  | ) (*Builder, error) { | ||||||
|  | 	builder := &Builder{ | ||||||
|  | 		scriptExecutor: scriptExecutor, | ||||||
|  | 		cacheExecutor: &Cache{ | ||||||
|  | 			cfg, | ||||||
|  | 		}, | ||||||
|  | 		scriptResolver: &ScriptResolver{ | ||||||
|  | 			cfg, | ||||||
|  | 		}, | ||||||
|  | 		scriptViewerExecutor: &ScriptViewer{ | ||||||
|  | 			config: cfg, | ||||||
|  | 		}, | ||||||
|  | 		checkerExecutor: &Checker{ | ||||||
|  | 			mgr, | ||||||
|  | 		}, | ||||||
|  | 		installerExecutor: installerExecutor, | ||||||
|  | 		sourceExecutor: &SourceDownloader{ | ||||||
|  | 			cfg, | ||||||
|  | 		}, | ||||||
|  | 		repos: repos, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return builder, nil | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								pkg/build/safe_installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								pkg/build/safe_installer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"net/rpc" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"sync" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type InstallerPlugin struct { | ||||||
|  | 	Impl InstallerExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerRPC struct { | ||||||
|  | 	client *rpc.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type InstallerRPCServer struct { | ||||||
|  | 	Impl InstallerExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *InstallerRPC) InstallLocal(paths []string) error { | ||||||
|  | 	return r.client.Call("Plugin.InstallLocal", paths, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerRPCServer) InstallLocal(paths []string, reply *struct{}) error { | ||||||
|  | 	return s.Impl.InstallLocal(paths) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *InstallerRPC) Install(pkgs []string) error { | ||||||
|  | 	return r.client.Call("Plugin.Install", pkgs, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerRPCServer) Install(pkgs []string, reply *struct{}) error { | ||||||
|  | 	slog.Debug("install", "pkgs", pkgs) | ||||||
|  | 	return s.Impl.Install(pkgs) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *InstallerRPC) RemoveAlreadyInstalled(paths []string) ([]string, error) { | ||||||
|  | 	var val []string | ||||||
|  | 	err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, &val) | ||||||
|  | 	return val, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *InstallerRPCServer) RemoveAlreadyInstalled(pkgs []string, res *[]string) error { | ||||||
|  | 	vars, err := s.Impl.RemoveAlreadyInstalled(pkgs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*res = vars | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *InstallerPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||||||
|  | 	return &InstallerRPC{client: c}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *InstallerPlugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||||||
|  | 	return &InstallerRPCServer{Impl: p.Impl}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetSafeInstaller() (InstallerExecutor, func(), error) { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	executable, err := os.Executable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	cmd := exec.Command(executable, "_internal-installer") | ||||||
|  | 	cmd.Env = []string{ | ||||||
|  | 		"HOME=/var/cache/alr", | ||||||
|  | 		"LOGNAME=alr", | ||||||
|  | 		"USER=alr", | ||||||
|  | 		"PATH=/usr/bin:/bin:/usr/local/bin", | ||||||
|  | 		"ALR_LOG_LEVEL=DEBUG", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 		uid, gid, err := utils.GetUidGidAlrUser() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			cmd.SysProcAttr = &syscall.SysProcAttr{ | ||||||
|  | 				Credential: &syscall.Credential{ | ||||||
|  | 					Uid: uint32(uid), | ||||||
|  | 					Gid: uint32(gid), | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 	*/ | ||||||
|  |  | ||||||
|  | 	slog.Debug("safe installer setup", "uid", syscall.Getuid(), "gid", syscall.Getgid()) | ||||||
|  |  | ||||||
|  | 	client := plugin.NewClient(&plugin.ClientConfig{ | ||||||
|  | 		HandshakeConfig: HandshakeConfig, | ||||||
|  | 		Plugins:         pluginMap, | ||||||
|  | 		Cmd:             cmd, | ||||||
|  | 		Logger:          logger.GetHCLoggerAdapter(), | ||||||
|  | 		SkipHostEnv:     true, | ||||||
|  | 		UnixSocketConfig: &plugin.UnixSocketConfig{ | ||||||
|  | 			Group: "alr", | ||||||
|  | 		}, | ||||||
|  | 		SyncStderr: os.Stderr, | ||||||
|  | 	}) | ||||||
|  | 	rpcClient, err := client.Client() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var cleanupOnce sync.Once | ||||||
|  | 	cleanup := func() { | ||||||
|  | 		cleanupOnce.Do(func() { | ||||||
|  | 			client.Kill() | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			slog.Debug("close installer") | ||||||
|  | 			cleanup() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	raw, err := rpcClient.Dispense("installer") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	executor, ok := raw.(InstallerExecutor) | ||||||
|  | 	if !ok { | ||||||
|  | 		err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw) | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return executor, cleanup, nil | ||||||
|  | } | ||||||
							
								
								
									
										291
									
								
								pkg/build/safe_script_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								pkg/build/safe_script_executor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,291 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"net/rpc" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/hashicorp/go-plugin" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var HandshakeConfig = plugin.HandshakeConfig{ | ||||||
|  | 	ProtocolVersion:  1, | ||||||
|  | 	MagicCookieKey:   "ALR_PLUGIN", | ||||||
|  | 	MagicCookieValue: "-", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorPlugin struct { | ||||||
|  | 	Impl ScriptExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorRPCServer struct { | ||||||
|  | 	Impl ScriptExecutor | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ============================= | ||||||
|  | // | ||||||
|  | // ReadScript | ||||||
|  | // | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*ScriptFile, error) { | ||||||
|  | 	var resp *ScriptFile | ||||||
|  | 	err := s.client.Call("Plugin.ReadScript", scriptPath, &resp) | ||||||
|  | 	return resp, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ReadScript(scriptPath string, resp *ScriptFile) error { | ||||||
|  | 	file, err := s.Impl.ReadScript(context.Background(), scriptPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = *file | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ============================= | ||||||
|  | // | ||||||
|  | // ExecuteFirstPass | ||||||
|  | // | ||||||
|  |  | ||||||
|  | type ExecuteFirstPassArgs struct { | ||||||
|  | 	Input *BuildInput | ||||||
|  | 	Sf    *ScriptFile | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ExecuteFirstPassResp struct { | ||||||
|  | 	BasePkg        string | ||||||
|  | 	VarsOfPackages []*types.BuildVars | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *ScriptFile) (string, []*types.BuildVars, error) { | ||||||
|  | 	var resp *ExecuteFirstPassResp | ||||||
|  | 	err := s.client.Call("Plugin.ExecuteFirstPass", &ExecuteFirstPassArgs{ | ||||||
|  | 		Input: input, | ||||||
|  | 		Sf:    sf, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp.BasePkg, resp.VarsOfPackages, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ExecuteFirstPassArgs, resp *ExecuteFirstPassResp) error { | ||||||
|  | 	basePkg, varsOfPackages, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = ExecuteFirstPassResp{ | ||||||
|  | 		BasePkg:        basePkg, | ||||||
|  | 		VarsOfPackages: varsOfPackages, | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ============================= | ||||||
|  | // | ||||||
|  | // PrepareDirs | ||||||
|  | // | ||||||
|  |  | ||||||
|  | type PrepareDirsArgs struct { | ||||||
|  | 	Input   *BuildInput | ||||||
|  | 	BasePkg string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) PrepareDirs( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	basePkg string, | ||||||
|  | ) error { | ||||||
|  | 	err := s.client.Call("Plugin.PrepareDirs", &PrepareDirsArgs{ | ||||||
|  | 		Input:   input, | ||||||
|  | 		BasePkg: basePkg, | ||||||
|  | 	}, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) PrepareDirs(args *PrepareDirsArgs, reply *struct{}) error { | ||||||
|  | 	err := s.Impl.PrepareDirs( | ||||||
|  | 		context.Background(), | ||||||
|  | 		args.Input, | ||||||
|  | 		args.BasePkg, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ============================= | ||||||
|  | // | ||||||
|  | // ExecuteSecondPass | ||||||
|  | // | ||||||
|  |  | ||||||
|  | type ExecuteSecondPassArgs struct { | ||||||
|  | 	Input          *BuildInput | ||||||
|  | 	Sf             *ScriptFile | ||||||
|  | 	VarsOfPackages []*types.BuildVars | ||||||
|  | 	RepoDeps       []string | ||||||
|  | 	BuiltNames     []string | ||||||
|  | 	BasePkg        string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPC) ExecuteSecondPass( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	sf *ScriptFile, | ||||||
|  | 	varsOfPackages []*types.BuildVars, | ||||||
|  | 	repoDeps []string, | ||||||
|  | 	builtNames []string, | ||||||
|  | 	basePkg string, | ||||||
|  | ) (*SecondPassResult, error) { | ||||||
|  | 	var resp *SecondPassResult | ||||||
|  | 	err := s.client.Call("Plugin.ExecuteSecondPass", &ExecuteSecondPassArgs{ | ||||||
|  | 		Input:          input, | ||||||
|  | 		Sf:             sf, | ||||||
|  | 		VarsOfPackages: varsOfPackages, | ||||||
|  | 		RepoDeps:       repoDeps, | ||||||
|  | 		BuiltNames:     builtNames, | ||||||
|  | 		BasePkg:        basePkg, | ||||||
|  | 	}, &resp) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return resp, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ExecuteSecondPassArgs, resp *SecondPassResult) error { | ||||||
|  | 	res, err := s.Impl.ExecuteSecondPass( | ||||||
|  | 		context.Background(), | ||||||
|  | 		args.Input, | ||||||
|  | 		args.Sf, | ||||||
|  | 		args.VarsOfPackages, | ||||||
|  | 		args.RepoDeps, | ||||||
|  | 		args.BuiltNames, | ||||||
|  | 		args.BasePkg, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*resp = *res | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  | // ============================ | ||||||
|  | // | ||||||
|  |  | ||||||
|  | func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { | ||||||
|  | 	return &ScriptExecutorRPCServer{Impl: p.Impl}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { | ||||||
|  | 	return &ScriptExecutorRPC{client: c}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptExecutorRPC struct { | ||||||
|  | 	client *rpc.Client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var pluginMap = map[string]plugin.Plugin{ | ||||||
|  | 	"script-executor": &ScriptExecutorPlugin{}, | ||||||
|  | 	"installer":       &InstallerPlugin{}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetSafeScriptExecutor() (ScriptExecutor, func(), error) { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	executable, err := os.Executable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd := exec.Command(executable, "_internal-safe-script-executor") | ||||||
|  | 	cmd.Env = []string{ | ||||||
|  | 		"HOME=/var/cache/alr", | ||||||
|  | 		"LOGNAME=alr", | ||||||
|  | 		"USER=alr", | ||||||
|  | 		"PATH=/usr/bin:/bin:/usr/local/bin", | ||||||
|  | 		"ALR_LOG_LEVEL=DEBUG", | ||||||
|  | 	} | ||||||
|  | 	/* | ||||||
|  | 		uid, gid, err := utils.GetUidGidAlrUser() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 			cmd.SysProcAttr = &syscall.SysProcAttr{ | ||||||
|  | 				Credential: &syscall.Credential{ | ||||||
|  | 					Uid: uint32(uid), | ||||||
|  | 					Gid: uint32(gid), | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 	*/ | ||||||
|  |  | ||||||
|  | 	client := plugin.NewClient(&plugin.ClientConfig{ | ||||||
|  | 		HandshakeConfig: HandshakeConfig, | ||||||
|  | 		Plugins:         pluginMap, | ||||||
|  | 		Cmd:             cmd, | ||||||
|  | 		Logger:          logger.GetHCLoggerAdapter(), | ||||||
|  | 		SkipHostEnv:     true, | ||||||
|  | 		UnixSocketConfig: &plugin.UnixSocketConfig{ | ||||||
|  | 			Group: "alr", | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	rpcClient, err := client.Client() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var cleanupOnce sync.Once | ||||||
|  | 	cleanup := func() { | ||||||
|  | 		cleanupOnce.Do(func() { | ||||||
|  | 			client.Kill() | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			slog.Debug("close script-executor") | ||||||
|  | 			cleanup() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	raw, err := rpcClient.Dispense("script-executor") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	executor, ok := raw.(ScriptExecutor) | ||||||
|  | 	if !ok { | ||||||
|  | 		err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw) | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return executor, cleanup, nil | ||||||
|  | } | ||||||
							
								
								
									
										435
									
								
								pkg/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								pkg/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,435 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"slices" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/google/shlex" | ||||||
|  | 	"github.com/goreleaser/nfpm/v2" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | 	"mvdan.cc/sh/v3/expand" | ||||||
|  | 	"mvdan.cc/sh/v3/interp" | ||||||
|  | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | 	finddeps "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build/find_deps" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type LocalScriptExecutor struct { | ||||||
|  | 	cfg Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewLocalScriptExecutor(cfg Config) *LocalScriptExecutor { | ||||||
|  | 	return &LocalScriptExecutor{ | ||||||
|  | 		cfg, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) ReadScript(ctx context.Context, scriptPath string) (*ScriptFile, error) { | ||||||
|  | 	fl, err := readScript(scriptPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return &ScriptFile{ | ||||||
|  | 		Path: scriptPath, | ||||||
|  | 		File: fl, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *ScriptFile) (string, []*types.BuildVars, error) { | ||||||
|  | 	varsOfPackages := []*types.BuildVars{} | ||||||
|  |  | ||||||
|  | 	scriptDir := filepath.Dir(sf.Path) | ||||||
|  | 	env := createBuildEnvVars(input.info, types.Directories{ScriptDir: scriptDir}) | ||||||
|  |  | ||||||
|  | 	runner, err := interp.New( | ||||||
|  | 		interp.Env(expand.ListEnviron(env...)),                               // Устанавливаем окружение | ||||||
|  | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr),                         // Устанавливаем стандартный ввод-вывод | ||||||
|  | 		interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение | ||||||
|  | 		interp.ReadDirHandler2(handlers.RestrictedReadDir(scriptDir)),        // Ограничиваем чтение директорий | ||||||
|  | 		interp.StatHandler(handlers.RestrictedStat(scriptDir)),               // Ограничиваем доступ к статистике файлов | ||||||
|  | 		interp.OpenHandler(handlers.RestrictedOpen(scriptDir)),               // Ограничиваем открытие файлов | ||||||
|  | 		interp.Dir(scriptDir), | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = runner.Run(ctx, sf.File) // Запускаем скрипт | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dec := decoder.New(input.info, runner) // Создаём новый декодер | ||||||
|  |  | ||||||
|  | 	type packages struct { | ||||||
|  | 		BasePkgName string   `sh:"basepkg_name"` | ||||||
|  | 		Names       []string `sh:"name"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var pkgs packages | ||||||
|  | 	err = dec.DecodeVars(&pkgs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(pkgs.Names) == 0 { | ||||||
|  | 		return "", nil, errors.New("package name is missing") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var vars types.BuildVars | ||||||
|  |  | ||||||
|  | 	if len(pkgs.Names) == 1 { | ||||||
|  | 		err = dec.DecodeVars(&vars) // Декодируем переменные | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", nil, err | ||||||
|  | 		} | ||||||
|  | 		varsOfPackages = append(varsOfPackages, &vars) | ||||||
|  |  | ||||||
|  | 		return vars.Name, varsOfPackages, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(input.packages) == 0 { | ||||||
|  | 		return "", nil, errors.New("script has multiple packages but package is not specified") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, pkgName := range input.packages { | ||||||
|  | 		var preVars types.BuildVarsPre | ||||||
|  | 		funcName := fmt.Sprintf("meta_%s", pkgName) | ||||||
|  | 		meta, ok := dec.GetFuncWithSubshell(funcName) | ||||||
|  | 		if !ok { | ||||||
|  | 			return "", nil, errors.New("func is missing") | ||||||
|  | 		} | ||||||
|  | 		r, err := meta(ctx) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", nil, err | ||||||
|  | 		} | ||||||
|  | 		d := decoder.New(&distro.OSRelease{}, r) | ||||||
|  | 		err = d.DecodeVars(&preVars) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", nil, err | ||||||
|  | 		} | ||||||
|  | 		vars := preVars.ToBuildVars() | ||||||
|  | 		vars.Name = pkgName | ||||||
|  | 		vars.Base = pkgs.BasePkgName | ||||||
|  |  | ||||||
|  | 		varsOfPackages = append(varsOfPackages, &vars) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pkgs.BasePkgName, varsOfPackages, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SecondPassResult struct { | ||||||
|  | 	BuiltPaths []string | ||||||
|  | 	BuiltNames []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) PrepareDirs( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	basePkg string, | ||||||
|  | ) error { | ||||||
|  | 	dirs, err := getDirs( | ||||||
|  | 		e.cfg, | ||||||
|  | 		input.script, | ||||||
|  | 		basePkg, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = prepareDirs(dirs) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) ExecuteSecondPass( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	sf *ScriptFile, | ||||||
|  | 	varsOfPackages []*types.BuildVars, | ||||||
|  | 	repoDeps []string, | ||||||
|  | 	builtNames []string, | ||||||
|  | 	basePkg string, | ||||||
|  | ) (*SecondPassResult, error) { | ||||||
|  | 	dirs, err := getDirs(e.cfg, sf.Path, basePkg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	env := createBuildEnvVars(input.info, dirs) | ||||||
|  |  | ||||||
|  | 	fakeroot := handlers.FakerootExecHandler(2 * time.Second) | ||||||
|  | 	runner, err := interp.New( | ||||||
|  | 		interp.Env(expand.ListEnviron(env...)),       // Устанавливаем окружение | ||||||
|  | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод | ||||||
|  | 		interp.ExecHandlers(func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc { | ||||||
|  | 			return helpers.Helpers.ExecHandler(fakeroot) | ||||||
|  | 		}), // Обрабатываем выполнение через fakeroot | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = runner.Run(ctx, sf.File) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dec := decoder.New(input.info, runner) | ||||||
|  |  | ||||||
|  | 	var builtPaths []string | ||||||
|  |  | ||||||
|  | 	err = e.ExecuteFunctions(ctx, dirs, dec) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, vars := range varsOfPackages { | ||||||
|  | 		packageName := "" | ||||||
|  | 		if vars.Base != "" { | ||||||
|  | 			packageName = vars.Name | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pkgFormat := input.pkgFormat | ||||||
|  |  | ||||||
|  | 		funcOut, err := e.ExecutePackageFunctions( | ||||||
|  | 			ctx, | ||||||
|  | 			dec, | ||||||
|  | 			dirs, | ||||||
|  | 			packageName, | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		slog.Info(gotext.Get("Building package metadata"), "name", basePkg) | ||||||
|  |  | ||||||
|  | 		pkgInfo, err := buildPkgMetadata( | ||||||
|  | 			ctx, | ||||||
|  | 			input, | ||||||
|  | 			vars, | ||||||
|  | 			dirs, | ||||||
|  | 			append( | ||||||
|  | 				repoDeps, | ||||||
|  | 				builtNames..., | ||||||
|  | 			), | ||||||
|  | 			funcOut.Contents, | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		packager, err := nfpm.Get(pkgFormat) // Получаем упаковщик для формата пакета | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета | ||||||
|  | 		pkgPath := filepath.Join(dirs.BaseDir, pkgName)   // Определяем путь к пакету | ||||||
|  |  | ||||||
|  | 		pkgFile, err := os.Create(pkgPath) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = packager.Package(pkgInfo, pkgFile) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		builtPaths = append(builtPaths, pkgPath) | ||||||
|  | 		builtNames = append(builtNames, vars.Name) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &SecondPassResult{ | ||||||
|  | 		BuiltPaths: builtPaths, | ||||||
|  | 		BuiltNames: builtNames, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func buildPkgMetadata( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input interface { | ||||||
|  | 		OsInfoProvider | ||||||
|  | 		BuildOptsProvider | ||||||
|  | 		PkgFormatProvider | ||||||
|  | 		RepositoryProvider | ||||||
|  | 	}, | ||||||
|  | 	vars *types.BuildVars, | ||||||
|  | 	dirs types.Directories, | ||||||
|  | 	deps []string, | ||||||
|  | 	preferedContents *[]string, | ||||||
|  | ) (*nfpm.Info, error) { | ||||||
|  | 	pkgInfo := getBasePkgInfo(vars, input) | ||||||
|  | 	pkgInfo.Description = vars.Description | ||||||
|  | 	pkgInfo.Platform = "linux" | ||||||
|  | 	pkgInfo.Homepage = vars.Homepage | ||||||
|  | 	pkgInfo.License = strings.Join(vars.Licenses, ", ") | ||||||
|  | 	pkgInfo.Maintainer = vars.Maintainer | ||||||
|  | 	pkgInfo.Overridables = nfpm.Overridables{ | ||||||
|  | 		Conflicts: append(vars.Conflicts, vars.Name), | ||||||
|  | 		Replaces:  vars.Replaces, | ||||||
|  | 		Provides:  append(vars.Provides, vars.Name), | ||||||
|  | 		Depends:   deps, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pkgFormat := input.PkgFormat() | ||||||
|  | 	info := input.OSRelease() | ||||||
|  |  | ||||||
|  | 	if pkgFormat == "apk" { | ||||||
|  | 		// Alpine отказывается устанавливать пакеты, которые предоставляют сами себя, поэтому удаляем такие элементы | ||||||
|  | 		pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool { | ||||||
|  | 			return s == pkgInfo.Name | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if vars.Epoch != 0 { | ||||||
|  | 		pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	setScripts(vars, pkgInfo, dirs.ScriptDir) | ||||||
|  |  | ||||||
|  | 	if slices.Contains(vars.Architectures, "all") { | ||||||
|  | 		pkgInfo.Arch = "all" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	contents, err := buildContents(vars, dirs, preferedContents) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	pkgInfo.Overridables.Contents = contents | ||||||
|  |  | ||||||
|  | 	if len(vars.AutoProv) == 1 && decoder.IsTruthy(vars.AutoProv[0]) { | ||||||
|  | 		f := finddeps.New(info, pkgFormat) | ||||||
|  | 		err = f.FindProvides(ctx, pkgInfo, dirs, vars.AutoProvSkipList) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(vars.AutoReq) == 1 && decoder.IsTruthy(vars.AutoReq[0]) { | ||||||
|  | 		f := finddeps.New(info, pkgFormat) | ||||||
|  | 		err = f.FindRequires(ctx, pkgInfo, dirs, vars.AutoReqSkipList) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pkgInfo, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) ExecuteFunctions(ctx context.Context, dirs types.Directories, dec *decoder.Decoder) error { | ||||||
|  | 	prepare, ok := dec.GetFunc("prepare") | ||||||
|  | 	if ok { | ||||||
|  | 		slog.Info(gotext.Get("Executing prepare()")) | ||||||
|  |  | ||||||
|  | 		err := prepare(ctx, interp.Dir(dirs.SrcDir)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	build, ok := dec.GetFunc("build") | ||||||
|  | 	if ok { | ||||||
|  | 		slog.Info(gotext.Get("Executing build()")) | ||||||
|  |  | ||||||
|  | 		err := build(ctx, interp.Dir(dirs.SrcDir)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *LocalScriptExecutor) ExecutePackageFunctions( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	dec *decoder.Decoder, | ||||||
|  | 	dirs types.Directories, | ||||||
|  | 	packageName string, | ||||||
|  | ) (*FunctionsOutput, error) { | ||||||
|  | 	output := &FunctionsOutput{} | ||||||
|  | 	var packageFuncName string | ||||||
|  | 	var filesFuncName string | ||||||
|  |  | ||||||
|  | 	if packageName == "" { | ||||||
|  | 		packageFuncName = "package" | ||||||
|  | 		filesFuncName = "files" | ||||||
|  | 	} else { | ||||||
|  | 		packageFuncName = fmt.Sprintf("package_%s", packageName) | ||||||
|  | 		filesFuncName = fmt.Sprintf("files_%s", packageName) | ||||||
|  | 	} | ||||||
|  | 	packageFn, ok := dec.GetFunc(packageFuncName) | ||||||
|  | 	if ok { | ||||||
|  | 		slog.Info(gotext.Get("Executing %s()", packageFuncName)) | ||||||
|  | 		err := packageFn(ctx, interp.Dir(dirs.SrcDir)) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	files, ok := dec.GetFuncP(filesFuncName, func(ctx context.Context, s *interp.Runner) error { | ||||||
|  | 		// It should be done via interp.RunnerOption, | ||||||
|  | 		// but due to the issues below, it cannot be done. | ||||||
|  | 		// - https://github.com/mvdan/sh/issues/962 | ||||||
|  | 		// - https://github.com/mvdan/sh/issues/1125 | ||||||
|  | 		script, err := syntax.NewParser().Parse(strings.NewReader("cd $pkgdir && shopt -s globstar"), "") | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		return s.Run(ctx, script) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	if ok { | ||||||
|  | 		slog.Info(gotext.Get("Executing %s()", filesFuncName)) | ||||||
|  |  | ||||||
|  | 		buf := &bytes.Buffer{} | ||||||
|  |  | ||||||
|  | 		err := files( | ||||||
|  | 			ctx, | ||||||
|  | 			interp.Dir(dirs.PkgDir), | ||||||
|  | 			interp.StdIO(os.Stdin, buf, os.Stderr), | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		contents, err := shlex.Split(buf.String()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		output.Contents = &contents | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return output, nil | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								pkg/build/script_resolver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								pkg/build/script_resolver.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ScriptResolver struct { | ||||||
|  | 	cfg Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptInfo struct { | ||||||
|  | 	Script     string | ||||||
|  | 	Repository string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptResolver) ResolveScript( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	pkg *db.Package, | ||||||
|  | ) *ScriptInfo { | ||||||
|  | 	var repository, script string | ||||||
|  |  | ||||||
|  | 	repodir := s.cfg.GetPaths().RepoDir | ||||||
|  | 	repository = pkg.Repository | ||||||
|  | 	if pkg.BasePkgName != "" { | ||||||
|  | 		script = filepath.Join(repodir, repository, pkg.BasePkgName, "alr.sh") | ||||||
|  | 	} else { | ||||||
|  | 		script = filepath.Join(repodir, repository, pkg.Name, "alr.sh") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &ScriptInfo{ | ||||||
|  | 		Repository: repository, | ||||||
|  | 		Script:     script, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								pkg/build/script_view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/build/script_view.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ScriptViewerConfig interface { | ||||||
|  | 	PagerStyle() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ScriptViewer struct { | ||||||
|  | 	config ScriptViewerConfig | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *ScriptViewer) ViewScript( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	sf *ScriptFile, | ||||||
|  | 	basePkg string, | ||||||
|  | ) error { | ||||||
|  | 	return cliutils.PromptViewScript( | ||||||
|  | 		ctx, | ||||||
|  | 		sf.Path, | ||||||
|  | 		basePkg, | ||||||
|  | 		s.config.PagerStyle(), | ||||||
|  | 		input.opts.Interactive, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								pkg/build/source_downloader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/build/source_downloader.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | // 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 build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/dl" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SourceDownloader struct { | ||||||
|  | 	cfg Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewSourceDownloader(cfg Config) *SourceDownloader { | ||||||
|  | 	return &SourceDownloader{ | ||||||
|  | 		cfg, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *SourceDownloader) DownloadSources( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	input *BuildInput, | ||||||
|  | 	basePkg string, | ||||||
|  | 	si SourcesInput, | ||||||
|  | ) error { | ||||||
|  | 	for i, src := range si.Sources { | ||||||
|  |  | ||||||
|  | 		opts := dl.Options{ | ||||||
|  | 			Name:        fmt.Sprintf("[%d]", i), | ||||||
|  | 			URL:         src, | ||||||
|  | 			Destination: getSrcDir(s.cfg, basePkg), | ||||||
|  | 			Progress:    os.Stderr, | ||||||
|  | 			LocalDir:    getScriptDir(input.script), | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !strings.EqualFold(si.Checksums[i], "SKIP") { | ||||||
|  | 			// Если контрольная сумма содержит двоеточие, используйте часть до двоеточия | ||||||
|  | 			// как алгоритм, а часть после как фактическую контрольную сумму. | ||||||
|  | 			// В противном случае используйте sha256 по умолчанию с целой строкой как контрольной суммой. | ||||||
|  | 			algo, hashData, ok := strings.Cut(si.Checksums[i], ":") | ||||||
|  | 			if ok { | ||||||
|  | 				checksum, err := hex.DecodeString(hashData) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 				opts.Hash = checksum | ||||||
|  | 				opts.HashAlgorithm = algo | ||||||
|  | 			} else { | ||||||
|  | 				checksum, err := hex.DecodeString(si.Checksums[i]) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 				opts.Hash = checksum | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		opts.DlCache = dlcache.New(s.cfg) | ||||||
|  |  | ||||||
|  | 		err := dl.Download(ctx, opts) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -39,7 +39,6 @@ import ( | |||||||
| 	"github.com/goreleaser/nfpm/v2/files" | 	"github.com/goreleaser/nfpm/v2/files" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| @@ -173,19 +172,23 @@ func buildContents(vars *types.BuildVars, dirs types.Directories, preferedConten | |||||||
|  |  | ||||||
| var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`) | var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`) | ||||||
|  |  | ||||||
| func getBasePkgInfo(vars *types.BuildVars, info *distro.OSRelease, opts *types.BuildOpts) *nfpm.Info { | func getBasePkgInfo(vars *types.BuildVars, input interface { | ||||||
|  | 	RepositoryProvider | ||||||
|  | 	OsInfoProvider | ||||||
|  | }, | ||||||
|  | ) *nfpm.Info { | ||||||
| 	return &nfpm.Info{ | 	return &nfpm.Info{ | ||||||
| 		Name:    fmt.Sprintf("%s+alr-%s", vars.Name, opts.Repository), | 		Name:    fmt.Sprintf("%s+alr-%s", vars.Name, input.Repository()), | ||||||
| 		Arch:    cpu.Arch(), | 		Arch:    cpu.Arch(), | ||||||
| 		Version: vars.Version, | 		Version: vars.Version, | ||||||
| 		Release: overrides.ReleasePlatformSpecific(vars.Release, info), | 		Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()), | ||||||
| 		Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10), | 		Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Функция getPkgFormat возвращает формат пакета из менеджера пакетов, | // Функция getPkgFormat возвращает формат пакета из менеджера пакетов, | ||||||
| // или ALR_PKG_FORMAT, если он установлен. | // или ALR_PKG_FORMAT, если он установлен. | ||||||
| func getPkgFormat(mgr manager.Manager) string { | func GetPkgFormat(mgr manager.Manager) string { | ||||||
| 	pkgFormat := mgr.Format() | 	pkgFormat := mgr.Format() | ||||||
| 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | ||||||
| 		pkgFormat = format | 		pkgFormat = format | ||||||
| @@ -272,25 +275,9 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error { | |||||||
| 	return r.Run(ctx, fl) | 	return r.Run(ctx, fl) | ||||||
| } | } | ||||||
| */ | */ | ||||||
| // Returns not installed dependencies |  | ||||||
| func removeAlreadyInstalled(opts types.BuildOpts, dependencies []string) ([]string, error) { |  | ||||||
| 	filteredPackages := []string{} |  | ||||||
|  |  | ||||||
| 	for _, dep := range dependencies { |  | ||||||
| 		installed, err := opts.Manager.IsInstalled(dep) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		if installed { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		filteredPackages = append(filteredPackages, dep) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return filteredPackages, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Функция packageNames возвращает имена всех предоставленных пакетов. | // Функция packageNames возвращает имена всех предоставленных пакетов. | ||||||
|  | /* | ||||||
| func packageNames(pkgs []db.Package) []string { | func packageNames(pkgs []db.Package) []string { | ||||||
| 	names := make([]string, len(pkgs)) | 	names := make([]string, len(pkgs)) | ||||||
| 	for i, p := range pkgs { | 	for i, p := range pkgs { | ||||||
| @@ -298,6 +285,7 @@ func packageNames(pkgs []db.Package) []string { | |||||||
| 	} | 	} | ||||||
| 	return names | 	return names | ||||||
| } | } | ||||||
|  | */ | ||||||
|  |  | ||||||
| // Функция removeDuplicates убирает любые дубликаты из предоставленного среза. | // Функция removeDuplicates убирает любые дубликаты из предоставленного среза. | ||||||
| func removeDuplicates(slice []string) []string { | func removeDuplicates(slice []string) []string { | ||||||
|   | |||||||
| @@ -82,6 +82,7 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) { | |||||||
| 		interp.ReadDirHandler2(handlers.NopReadDir), | 		interp.ReadDirHandler2(handlers.NopReadDir), | ||||||
| 		interp.StatHandler(handlers.NopStat), | 		interp.StatHandler(handlers.NopStat), | ||||||
| 		interp.Env(expand.ListEnviron()), | 		interp.Env(expand.ListEnviron()), | ||||||
|  | 		interp.Dir("/"), | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|   | |||||||
| @@ -28,7 +28,15 @@ import ( | |||||||
|  |  | ||||||
| // APK represents the APK package manager | // APK represents the APK package manager | ||||||
| type APK struct { | type APK struct { | ||||||
| 	rootCmd string | 	CommonPackageManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewAPK() *APK { | ||||||
|  | 	return &APK{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "-i", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*APK) Exists() bool { | func (*APK) Exists() bool { | ||||||
| @@ -44,10 +52,6 @@ func (*APK) Format() string { | |||||||
| 	return "apk" | 	return "apk" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APK) SetRootCmd(s string) { |  | ||||||
| 	a.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (a *APK) Sync(opts *Opts) error { | func (a *APK) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) | 	opts = ensureOpts(opts) | ||||||
| 	cmd := a.getCmd(opts, "apk", "update") | 	cmd := a.getCmd(opts, "apk", "update") | ||||||
| @@ -163,20 +167,3 @@ func (a *APK) IsInstalled(pkg string) (bool, error) { | |||||||
| 	} | 	} | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-i") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -28,7 +28,15 @@ import ( | |||||||
|  |  | ||||||
| // APT represents the APT package manager | // APT represents the APT package manager | ||||||
| type APT struct { | type APT struct { | ||||||
| 	rootCmd string | 	CommonPackageManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewAPT() *APT { | ||||||
|  | 	return &APT{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "-y", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*APT) Exists() bool { | func (*APT) Exists() bool { | ||||||
| @@ -44,10 +52,6 @@ func (*APT) Format() string { | |||||||
| 	return "deb" | 	return "deb" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APT) SetRootCmd(s string) { |  | ||||||
| 	a.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (a *APT) Sync(opts *Opts) error { | func (a *APT) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) | 	opts = ensureOpts(opts) | ||||||
| 	cmd := a.getCmd(opts, "apt", "update") | 	cmd := a.getCmd(opts, "apt", "update") | ||||||
| @@ -149,20 +153,3 @@ func (a *APT) IsInstalled(pkg string) (bool, error) { | |||||||
| 	} | 	} | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-y") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -24,18 +24,16 @@ import ( | |||||||
|  |  | ||||||
| // APTRpm represents the APT-RPM package manager | // APTRpm represents the APT-RPM package manager | ||||||
| type APTRpm struct { | type APTRpm struct { | ||||||
|  | 	CommonPackageManager | ||||||
| 	CommonRPM | 	CommonRPM | ||||||
| 	rootCmd string |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*APTRpm) Exists() bool { | func NewAPTRpm() *APTRpm { | ||||||
| 	cmd := exec.Command("apt-config", "dump") | 	return &APTRpm{ | ||||||
| 	output, err := cmd.Output() | 		CommonPackageManager: CommonPackageManager{ | ||||||
| 	if err != nil { | 			noConfirmArg: "-y", | ||||||
| 		return false | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return strings.Contains(string(output), "RPM") |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*APTRpm) Name() string { | func (*APTRpm) Name() string { | ||||||
| @@ -46,8 +44,14 @@ func (*APTRpm) Format() string { | |||||||
| 	return "rpm" | 	return "rpm" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APTRpm) SetRootCmd(s string) { | func (*APTRpm) Exists() bool { | ||||||
| 	a.rootCmd = s | 	cmd := exec.Command("apt-config", "dump") | ||||||
|  | 	output, err := cmd.Output() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return strings.Contains(string(output), "RPM") | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APTRpm) Sync(opts *Opts) error { | func (a *APTRpm) Sync(opts *Opts) error { | ||||||
| @@ -66,6 +70,7 @@ func (a *APTRpm) Install(opts *Opts, pkgs ...string) error { | |||||||
| 	cmd := a.getCmd(opts, "apt-get", "install") | 	cmd := a.getCmd(opts, "apt-get", "install") | ||||||
| 	cmd.Args = append(cmd.Args, pkgs...) | 	cmd.Args = append(cmd.Args, pkgs...) | ||||||
| 	setCmdEnv(cmd) | 	setCmdEnv(cmd) | ||||||
|  | 	cmd.Stdout = cmd.Stderr | ||||||
| 	err := cmd.Run() | 	err := cmd.Run() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("apt-get: install: %w", err) | 		return fmt.Errorf("apt-get: install: %w", err) | ||||||
| @@ -105,20 +110,3 @@ func (a *APTRpm) UpgradeAll(opts *Opts) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *APTRpm) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-y") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								pkg/manager/common.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/manager/common.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | // 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 manager | ||||||
|  |  | ||||||
|  | import "os/exec" | ||||||
|  |  | ||||||
|  | type CommonPackageManager struct { | ||||||
|  | 	noConfirmArg string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *CommonPackageManager) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { | ||||||
|  | 	cmd := exec.Command(mgrCmd) | ||||||
|  | 	cmd.Args = append(cmd.Args, opts.Args...) | ||||||
|  | 	cmd.Args = append(cmd.Args, args...) | ||||||
|  |  | ||||||
|  | 	if opts.NoConfirm { | ||||||
|  | 		cmd.Args = append(cmd.Args, m.noConfirmArg) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return cmd | ||||||
|  | } | ||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов. | ||||||
|  * ALR - Любой Linux Репозиторий | // | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // ALR - Any Linux Repository | ||||||
|  * | // Copyright (C) 2025 Евгений Храмов | ||||||
|  * This program является свободным: вы можете распространять его и/или изменять | // | ||||||
|  * на условиях GNU General Public License, опубликованной Free Software Foundation, | // This program is free software: you can redistribute it and/or modify | ||||||
|  * либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии. | // 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, | ||||||
|  * Подробности см. в GNU General Public License. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * Вы должны были получить копию GNU General Public License | // GNU General Public License for more details. | ||||||
|  * вместе с этой программой. Если нет, см. <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 manager | package manager | ||||||
|  |  | ||||||
| @@ -23,33 +24,32 @@ import ( | |||||||
| 	"os/exec" | 	"os/exec" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DNF представляет менеджер пакетов DNF |  | ||||||
| type DNF struct { | type DNF struct { | ||||||
|  | 	CommonPackageManager | ||||||
| 	CommonRPM | 	CommonRPM | ||||||
| 	rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Exists проверяет, доступен ли DNF в системе, возвращает true если да | func NewDNF() *DNF { | ||||||
|  | 	return &DNF{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "-y", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (*DNF) Exists() bool { | func (*DNF) Exists() bool { | ||||||
| 	_, err := exec.LookPath("dnf") | 	_, err := exec.LookPath("dnf") | ||||||
| 	return err == nil | 	return err == nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Name возвращает имя менеджера пакетов, в данном случае "dnf" |  | ||||||
| func (*DNF) Name() string { | func (*DNF) Name() string { | ||||||
| 	return "dnf" | 	return "dnf" | ||||||
| } | } | ||||||
|  |  | ||||||
| // Format возвращает формат пакетов "rpm", используемый DNF |  | ||||||
| func (*DNF) Format() string { | func (*DNF) Format() string { | ||||||
| 	return "rpm" | 	return "rpm" | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetRootCmd устанавливает команду, используемую для выполнения операций с правами root |  | ||||||
| func (d *DNF) SetRootCmd(s string) { |  | ||||||
| 	d.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий | // Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий | ||||||
| func (d *DNF) Sync(opts *Opts) error { | func (d *DNF) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения | 	opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения | ||||||
| @@ -118,21 +118,3 @@ func (d *DNF) UpgradeAll(opts *Opts) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF |  | ||||||
| func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-y") // Добавляет параметр автоматического подтверждения (-y) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -27,27 +27,22 @@ import ( | |||||||
| var Args []string | var Args []string | ||||||
|  |  | ||||||
| type Opts struct { | type Opts struct { | ||||||
| 	AsRoot    bool |  | ||||||
| 	NoConfirm bool | 	NoConfirm bool | ||||||
| 	Args      []string | 	Args      []string | ||||||
| } | } | ||||||
|  |  | ||||||
| var DefaultOpts = &Opts{ | var DefaultOpts = &Opts{ | ||||||
| 	AsRoot:    true, |  | ||||||
| 	NoConfirm: false, | 	NoConfirm: false, | ||||||
| } | } | ||||||
|  |  | ||||||
| // DefaultRootCmd is the command used for privilege elevation by default |  | ||||||
| var DefaultRootCmd = "sudo" |  | ||||||
|  |  | ||||||
| var managers = []Manager{ | var managers = []Manager{ | ||||||
| 	&Pacman{}, | 	NewPacman(), | ||||||
| 	&APT{}, | 	NewAPT(), | ||||||
| 	&DNF{}, | 	NewDNF(), | ||||||
| 	&YUM{}, | 	NewYUM(), | ||||||
| 	&APK{}, | 	NewAPK(), | ||||||
| 	&Zypper{}, | 	NewZypper(), | ||||||
| 	&APTRpm{}, | 	NewAPTRpm(), | ||||||
| } | } | ||||||
|  |  | ||||||
| // Register registers a new package manager | // Register registers a new package manager | ||||||
| @@ -64,8 +59,7 @@ type Manager interface { | |||||||
| 	Format() string | 	Format() string | ||||||
| 	// Returns true if the package manager exists on the system. | 	// Returns true if the package manager exists on the system. | ||||||
| 	Exists() bool | 	Exists() bool | ||||||
| 	// Sets the command used to elevate privileges. Defaults to DefaultRootCmd. |  | ||||||
| 	SetRootCmd(string) |  | ||||||
| 	// Sync fetches repositories without installing anything | 	// Sync fetches repositories without installing anything | ||||||
| 	Sync(*Opts) error | 	Sync(*Opts) error | ||||||
| 	// Install installs packages | 	// Install installs packages | ||||||
| @@ -104,18 +98,10 @@ func Get(name string) Manager { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // getRootCmd returns rootCmd if it's not empty, otherwise returns DefaultRootCmd |  | ||||||
| func getRootCmd(rootCmd string) string { |  | ||||||
| 	if rootCmd != "" { |  | ||||||
| 		return rootCmd |  | ||||||
| 	} |  | ||||||
| 	return DefaultRootCmd |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func setCmdEnv(cmd *exec.Cmd) { | func setCmdEnv(cmd *exec.Cmd) { | ||||||
| 	cmd.Env = os.Environ() | 	cmd.Env = os.Environ() | ||||||
| 	cmd.Stdin = os.Stdin | 	cmd.Stdin = os.Stdin | ||||||
| 	cmd.Stdout = os.Stdout | 	cmd.Stdout = os.Stderr | ||||||
| 	cmd.Stderr = os.Stderr | 	cmd.Stderr = os.Stderr | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,7 +28,15 @@ import ( | |||||||
|  |  | ||||||
| // Pacman represents the Pacman package manager | // Pacman represents the Pacman package manager | ||||||
| type Pacman struct { | type Pacman struct { | ||||||
| 	rootCmd string | 	CommonPackageManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPacman() *Pacman { | ||||||
|  | 	return &Pacman{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "--noconfirm", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*Pacman) Exists() bool { | func (*Pacman) Exists() bool { | ||||||
| @@ -44,10 +52,6 @@ func (*Pacman) Format() string { | |||||||
| 	return "archlinux" | 	return "archlinux" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Pacman) SetRootCmd(s string) { |  | ||||||
| 	p.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (p *Pacman) Sync(opts *Opts) error { | func (p *Pacman) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) | 	opts = ensureOpts(opts) | ||||||
| 	cmd := p.getCmd(opts, "pacman", "-Sy") | 	cmd := p.getCmd(opts, "pacman", "-Sy") | ||||||
| @@ -156,20 +160,3 @@ func (p *Pacman) IsInstalled(pkg string) (bool, error) { | |||||||
| 	} | 	} | ||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(p.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "--noconfirm") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -26,9 +26,16 @@ import ( | |||||||
|  |  | ||||||
| // YUM represents the YUM package manager | // YUM represents the YUM package manager | ||||||
| type YUM struct { | type YUM struct { | ||||||
|  | 	CommonPackageManager | ||||||
| 	CommonRPM | 	CommonRPM | ||||||
|  | } | ||||||
|  |  | ||||||
| 	rootCmd string | func NewYUM() *YUM { | ||||||
|  | 	return &YUM{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "-y", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*YUM) Exists() bool { | func (*YUM) Exists() bool { | ||||||
| @@ -44,10 +51,6 @@ func (*YUM) Format() string { | |||||||
| 	return "rpm" | 	return "rpm" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (y *YUM) SetRootCmd(s string) { |  | ||||||
| 	y.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (y *YUM) Sync(opts *Opts) error { | func (y *YUM) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) | 	opts = ensureOpts(opts) | ||||||
| 	cmd := y.getCmd(opts, "yum", "upgrade") | 	cmd := y.getCmd(opts, "yum", "upgrade") | ||||||
| @@ -110,20 +113,3 @@ func (y *YUM) UpgradeAll(opts *Opts) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(y.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-y") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -26,8 +26,16 @@ import ( | |||||||
|  |  | ||||||
| // Zypper represents the Zypper package manager | // Zypper represents the Zypper package manager | ||||||
| type Zypper struct { | type Zypper struct { | ||||||
|  | 	CommonPackageManager | ||||||
| 	CommonRPM | 	CommonRPM | ||||||
| 	rootCmd string | } | ||||||
|  |  | ||||||
|  | func NewZypper() *YUM { | ||||||
|  | 	return &YUM{ | ||||||
|  | 		CommonPackageManager: CommonPackageManager{ | ||||||
|  | 			noConfirmArg: "-y", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*Zypper) Exists() bool { | func (*Zypper) Exists() bool { | ||||||
| @@ -43,10 +51,6 @@ func (*Zypper) Format() string { | |||||||
| 	return "rpm" | 	return "rpm" | ||||||
| } | } | ||||||
|  |  | ||||||
| func (z *Zypper) SetRootCmd(s string) { |  | ||||||
| 	z.rootCmd = s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (z *Zypper) Sync(opts *Opts) error { | func (z *Zypper) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) | 	opts = ensureOpts(opts) | ||||||
| 	cmd := z.getCmd(opts, "zypper", "refresh") | 	cmd := z.getCmd(opts, "zypper", "refresh") | ||||||
| @@ -109,20 +113,3 @@ func (z *Zypper) UpgradeAll(opts *Opts) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	if opts.AsRoot { |  | ||||||
| 		cmd = exec.Command(getRootCmd(z.rootCmd), mgrCmd) |  | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |  | ||||||
| 		cmd.Args = append(cmd.Args, args...) |  | ||||||
| 	} else { |  | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |  | ||||||
| 		cmd.Args = append(cmd.Args, "-y") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return cmd |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -268,6 +268,7 @@ func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Ru | |||||||
| 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||||
| 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||||
| 		interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | 		interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | ||||||
|  | 		interp.Dir(scriptDir), | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										129
									
								
								repo.go
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								repo.go
									
									
									
									
									
								
							| @@ -20,7 +20,6 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"log/slog" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  |  | ||||||
| @@ -28,10 +27,10 @@ import ( | |||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func AddRepoCmd() *cli.Command { | func AddRepoCmd() *cli.Command { | ||||||
| @@ -54,27 +53,32 @@ func AddRepoCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.ExitIfNotRoot(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			name := c.String("name") | 			name := c.String("name") | ||||||
| 			repoURL := c.String("url") | 			repoURL := c.String("url") | ||||||
|  |  | ||||||
| 			cfg := config.New() | 			ctx := c.Context | ||||||
| 			err := cfg.Load() |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			cfg := deps.Cfg | ||||||
|  |  | ||||||
| 			reposSlice := cfg.Repos() | 			reposSlice := cfg.Repos() | ||||||
|  |  | ||||||
| 			for _, repo := range reposSlice { | 			for _, repo := range reposSlice { | ||||||
| 				if repo.URL == repoURL { | 				if repo.URL == repoURL || repo.Name == name { | ||||||
| 					slog.Error("Repo already exists", "name", repo.Name) | 					return cliutils.FormatCliExit(gotext.Get("Repo %s already exists", repo.Name), nil) | ||||||
| 					os.Exit(1) |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			reposSlice = append(reposSlice, types.Repo{ | 			reposSlice = append(reposSlice, types.Repo{ | ||||||
| 				Name: name, | 				Name: name, | ||||||
| 				URL:  repoURL, | 				URL:  repoURL, | ||||||
| @@ -83,22 +87,23 @@ func AddRepoCmd() *cli.Command { | |||||||
|  |  | ||||||
| 			err = cfg.SaveUserConfig() | 			err = cfg.SaveUserConfig() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error saving config"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error saving config"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
| 			err = db.Init(ctx) | 				return err | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			rs := repos.New(cfg, db) | 			deps, err = appbuilder. | ||||||
| 			err = rs.Pull(ctx, cfg.Repos()) | 				New(ctx). | ||||||
|  | 				UseConfig(cfg). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposForcePull(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| @@ -119,15 +124,24 @@ func RemoveRepoCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfNotRoot(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  |  | ||||||
| 			name := c.String("name") | 			name := c.String("name") | ||||||
| 			cfg := config.New() |  | ||||||
| 			err := cfg.Load() | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			cfg := deps.Cfg | ||||||
|  |  | ||||||
| 			found := false | 			found := false | ||||||
| 			index := 0 | 			index := 0 | ||||||
| @@ -139,33 +153,37 @@ func RemoveRepoCmd() *cli.Command { | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if !found { | 			if !found { | ||||||
| 				slog.Error(gotext.Get("Repo does not exist"), "name", name) | 				return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" does not exist", name), nil) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			cfg.SetRepos(slices.Delete(reposSlice, index, index+1)) | 			cfg.SetRepos(slices.Delete(reposSlice, index, index+1)) | ||||||
|  |  | ||||||
| 			err = os.RemoveAll(filepath.Join(cfg.GetPaths().RepoDir, name)) | 			err = os.RemoveAll(filepath.Join(cfg.GetPaths().RepoDir, name)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error removing repo directory"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error removing repo directory"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			err = cfg.SaveUserConfig() | 			err = cfg.SaveUserConfig() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error saving config"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error saving config"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 			err = db.Init(ctx) | 				return err | ||||||
| 			if err != nil { |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
| 			err = db.DeletePkgs(ctx, "repository = ?", name) |  | ||||||
|  | 			deps, err = appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				UseConfig(cfg). | ||||||
|  | 				WithDB(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error removing packages from database"), "err", err) | 				return err | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			err = deps.DB.DeletePkgs(ctx, "repository = ?", name) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error removing packages from database"), err) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| @@ -179,25 +197,22 @@ func RefreshCmd() *cli.Command { | |||||||
| 		Usage:   gotext.Get("Pull all repositories that have changed"), | 		Usage:   gotext.Get("Pull all repositories that have changed"), | ||||||
| 		Aliases: []string{"ref"}, | 		Aliases: []string{"ref"}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 			cfg := config.New() | 				return err | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			ctx := c.Context | ||||||
| 			err = db.Init(ctx) |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposForcePull(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				os.Exit(1) | 				return err | ||||||
| 			} |  | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
| 			err = rs.Pull(ctx, cfg.Repos()) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								search.go
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								search.go
									
									
									
									
									
								
							| @@ -18,15 +18,15 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"text/template" | 	"text/template" | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -63,32 +63,23 @@ func SearchCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
| 			cfg := config.New() |  | ||||||
| 			err := cfg.Load() |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			db := database.New(cfg) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			defer db.Close() |  | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return err | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 			format := c.String("format") | 			db := deps.DB | ||||||
| 			var tmpl *template.Template |  | ||||||
| 			if format != "" { |  | ||||||
| 				tmpl, err = template.New("format").Parse(format) |  | ||||||
| 				if err != nil { |  | ||||||
| 					slog.Error(gotext.Get("Error parsing format template"), "err", err) |  | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			s := search.New(db) | 			s := search.New(db) | ||||||
|  |  | ||||||
| @@ -102,16 +93,23 @@ func SearchCmd() *cli.Command { | |||||||
| 					Build(), | 					Build(), | ||||||
| 			) | 			) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error parsing format template"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error while executing search"), err) | ||||||
| 				os.Exit(1) | 			} | ||||||
|  |  | ||||||
|  | 			format := c.String("format") | ||||||
|  | 			var tmpl *template.Template | ||||||
|  | 			if format != "" { | ||||||
|  | 				tmpl, err = template.New("format").Parse(format) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			for _, dbPkg := range packages { | 			for _, dbPkg := range packages { | ||||||
| 				if tmpl != nil { | 				if tmpl != nil { | ||||||
| 					err = tmpl.Execute(os.Stdout, dbPkg) | 					err = tmpl.Execute(os.Stdout, dbPkg) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						slog.Error(gotext.Get("Error executing template"), "err", err) | 						return cliutils.FormatCliExit(gotext.Get("Error executing template"), err) | ||||||
| 						os.Exit(1) |  | ||||||
| 					} | 					} | ||||||
| 					fmt.Println() | 					fmt.Println() | ||||||
| 				} else { | 				} else { | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								upgrade.go
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								upgrade.go
									
									
									
									
									
								
							| @@ -23,21 +23,21 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" |  | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"go.elara.ws/vercmp" | 	"go.elara.ws/vercmp" | ||||||
| 	"golang.org/x/exp/maps" | 	"golang.org/x/exp/maps" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -54,66 +54,77 @@ func UpgradeCmd() *cli.Command { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfNotRoot(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			installer, installerClose, err := build.GetSafeInstaller() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer installerClose() | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			scripter, scripterClose, err := build.GetSafeScriptExecutor() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer scripterClose() | ||||||
|  |  | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  |  | ||||||
| 			cfg := config.New() | 			deps, err := appbuilder. | ||||||
| 			err := cfg.Load() | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithRepos(). | ||||||
|  | 				WithDistroInfo(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | 				return err | ||||||
| 				os.Exit(1) | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			builder, err := build.NewMainBuilder( | ||||||
|  | 				deps.Cfg, | ||||||
|  | 				deps.Manager, | ||||||
|  | 				deps.Repos, | ||||||
|  | 				scripter, | ||||||
|  | 				installer, | ||||||
|  | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			db := database.New(cfg) | 			updates, err := checkForUpdates(ctx, deps.Manager, deps.DB, deps.Info) | ||||||
| 			rs := repos.New(cfg, db) |  | ||||||
| 			err = db.Init(ctx) |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err) | ||||||
| 				os.Exit(1) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			info, err := distro.ParseOSRelease(ctx) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error parsing os-release file"), "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) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if cfg.AutoPull() { |  | ||||||
| 				err = rs.Pull(ctx, cfg.Repos()) |  | ||||||
| 				if err != nil { |  | ||||||
| 					slog.Error(gotext.Get("Error pulling repos"), "err", err) |  | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			updates, err := checkForUpdates(ctx, mgr, cfg, db, rs, info) |  | ||||||
| 			if err != nil { |  | ||||||
| 				slog.Error(gotext.Get("Error checking for updates"), "err", err) |  | ||||||
| 				os.Exit(1) |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if len(updates) > 0 { | 			if len(updates) > 0 { | ||||||
| 				builder := build.NewBuilder( | 				err = builder.InstallALRPackages( | ||||||
| 					ctx, | 					ctx, | ||||||
| 					types.BuildOpts{ | 					&build.BuildArgs{ | ||||||
| 						Manager:     mgr, | 						Opts: &types.BuildOpts{ | ||||||
| 							Clean:       c.Bool("clean"), | 							Clean:       c.Bool("clean"), | ||||||
| 							Interactive: c.Bool("interactive"), | 							Interactive: c.Bool("interactive"), | ||||||
| 						}, | 						}, | ||||||
| 					rs, | 						Info:       deps.Info, | ||||||
| 					info, | 						PkgFormat_: build.GetPkgFormat(deps.Manager), | ||||||
| 					cfg, | 					}, | ||||||
|  | 					updates, | ||||||
| 				) | 				) | ||||||
| 				builder.InstallPkgs(ctx, updates, nil, types.BuildOpts{ | 				if err != nil { | ||||||
| 					Manager:     mgr, | 					return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err) | ||||||
| 					Clean:       c.Bool("clean"), | 				} | ||||||
| 					Interactive: c.Bool("interactive"), |  | ||||||
| 				}) |  | ||||||
| 			} else { | 			} else { | ||||||
| 				slog.Info(gotext.Get("There is nothing to do.")) | 				slog.Info(gotext.Get("There is nothing to do.")) | ||||||
| 			} | 			} | ||||||
| @@ -126,9 +137,7 @@ func UpgradeCmd() *cli.Command { | |||||||
| func checkForUpdates( | func checkForUpdates( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	mgr manager.Manager, | 	mgr manager.Manager, | ||||||
| 	cfg *config.ALRConfig, |  | ||||||
| 	db *database.Database, | 	db *database.Database, | ||||||
| 	rs *repos.Repos, |  | ||||||
| 	info *distro.OSRelease, | 	info *distro.OSRelease, | ||||||
| ) ([]database.Package, error) { | ) ([]database.Package, error) { | ||||||
| 	installed, err := mgr.ListInstalled(nil) | 	installed, err := mgr.ListInstalled(nil) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user