forked from Plemya-x/ALR
		
	wip
This commit is contained in:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							| @@ -37,6 +37,7 @@ install: \ | ||||
|  | ||||
| $(INSTALED_BIN): $(BIN) | ||||
| 	install -Dm755 $< $@ | ||||
| 	setcap cap_setuid,cap_setgid+ep $(INSTALED_BIN) | ||||
|  | ||||
| $(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION) | ||||
| 	install -Dm755 $< $@ | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|     <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="14">coverage</text> | ||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">19.0%</text> | ||||
|         <text x="86" y="14">19.0%</text> | ||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">16.7%</text> | ||||
|         <text x="86" y="14">16.7%</text> | ||||
|     </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
| @@ -12,7 +12,7 @@ | ||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> | ||||
|         <text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text> | ||||
|         <text x="37" y="14">ru translate</text> | ||||
|         <text x="100" y="15" fill="#010101" fill-opacity=".3">97.00%</text> | ||||
|         <text x="100" y="14">97.00%</text> | ||||
|         <text x="100" y="15" fill="#010101" fill-opacity=".3">98.00%</text> | ||||
|         <text x="100" y="14">98.00%</text> | ||||
|     </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 940 B After Width: | Height: | Size: 940 B | 
							
								
								
									
										53
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								build.go
									
									
									
									
									
								
							| @@ -84,9 +84,9 @@ func BuildCmd() *cli.Command { | ||||
|  | ||||
| 			var script string | ||||
| 			var packages []string | ||||
| 			repository := "default" | ||||
| 			// repository := "default" | ||||
|  | ||||
| 			repoDir := cfg.GetPaths().RepoDir | ||||
| 			// repoDir := cfg.GetPaths().RepoDir | ||||
|  | ||||
| 			switch { | ||||
| 			case c.IsSet("script"): | ||||
| @@ -111,25 +111,14 @@ func BuildCmd() *cli.Command { | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
|  | ||||
| 				repository = pkg[0].Repository | ||||
| 				// repository = pkg[0].Repository | ||||
|  | ||||
| 				if pkg[0].BasePkgName != "" { | ||||
| 					script = filepath.Join(repoDir, repository, pkg[0].BasePkgName, "alr.sh") | ||||
| 					// script = filepath.Join(repoDir, repository, pkg[0].BasePkgName, "alr.sh") | ||||
| 					packages = append(packages, pkg[0].Name) | ||||
| 				} else { | ||||
| 					script = filepath.Join(repoDir, repository, pkg[0].Name, "alr.sh") | ||||
| 				} | ||||
| 			default: | ||||
| 				script = filepath.Join(repoDir, "alr.sh") | ||||
| 			} | ||||
|  | ||||
| 			// Проверка автоматического пулла репозиториев | ||||
| 			if cfg.AutoPull() { | ||||
| 				err := rs.Pull(ctx, cfg.Repos()) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Error pulling repositories"), "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 				// script = filepath.Join(repoDir, "alr.sh") | ||||
| 			} | ||||
|  | ||||
| 			// Обнаружение менеджера пакетов | ||||
| @@ -145,23 +134,27 @@ func BuildCmd() *cli.Command { | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			builder := build.NewBuilder( | ||||
| 				ctx, | ||||
| 				types.BuildOpts{ | ||||
| 					Packages:    packages, | ||||
| 					Repository:  repository, | ||||
| 					Script:      script, | ||||
| 					Manager:     mgr, | ||||
| 					Clean:       c.Bool("clean"), | ||||
| 					Interactive: c.Bool("interactive"), | ||||
| 				}, | ||||
| 				rs, | ||||
| 				info, | ||||
| 			builder := build.NewMainBuilder( | ||||
| 				cfg, | ||||
| 				rs, | ||||
| 			) | ||||
|  | ||||
| 			// Сборка пакета | ||||
| 			pkgPaths, _, err := builder.BuildPackage(ctx) | ||||
| 			res, err := builder.BuildPackageFromScript( | ||||
| 				ctx, | ||||
| 				&build.BuildPackageFromScriptArgs{ | ||||
| 					Script:   script, | ||||
| 					Packages: packages, | ||||
| 					BuildArgs: build.BuildArgs{ | ||||
| 						Opts: &types.BuildOpts{ | ||||
| 							Clean:       c.Bool("clean"), | ||||
| 							Interactive: c.Bool("interactive"), | ||||
| 						}, | ||||
| 						PkgFormat_: build.GetPkgFormat(mgr), | ||||
| 						Info:       info, | ||||
| 					}, | ||||
| 				}, | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error building package"), "err", err) | ||||
| 				os.Exit(1) | ||||
| @@ -175,7 +168,7 @@ func BuildCmd() *cli.Command { | ||||
| 			} | ||||
|  | ||||
| 			// Перемещение собранных пакетов в рабочую директорию | ||||
| 			for _, pkgPath := range pkgPaths { | ||||
| 			for _, pkgPath := range res.PackagePaths { | ||||
| 				name := filepath.Base(pkgPath) | ||||
| 				err = osutils.Move(pkgPath, filepath.Join(wd, name)) | ||||
| 				if err != nil { | ||||
|   | ||||
| @@ -33,6 +33,7 @@ func TestE2EAlrAddRepo(t *testing.T) { | ||||
| 		COMMON_SYSTEMS, | ||||
| 		func(t *testing.T, r e2e.Runnable) { | ||||
| 			err := r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"addrepo", | ||||
| 				"--name", | ||||
| @@ -45,11 +46,12 @@ func TestE2EAlrAddRepo(t *testing.T) { | ||||
| 			err = r.Exec(e2e.NewCommand( | ||||
| 				"bash", | ||||
| 				"-c", | ||||
| 				"cat $HOME/.config/alr/alr.toml", | ||||
| 				"cat /etc/alr/alr.toml", | ||||
| 			)) | ||||
| 			assert.NoError(t, err) | ||||
|  | ||||
| 			err = r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"removerepo", | ||||
| 				"--name", | ||||
| @@ -61,7 +63,7 @@ func TestE2EAlrAddRepo(t *testing.T) { | ||||
| 			err = r.Exec(e2e.NewCommand( | ||||
| 				"bash", | ||||
| 				"-c", | ||||
| 				"cat $HOME/.config/alr/alr.toml", | ||||
| 				"cat /etc/alr/alr.toml", | ||||
| 			), e2e.WithExecOptionStdout(&buf)) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Contains(t, buf.String(), "rootCmd") | ||||
|   | ||||
| @@ -109,7 +109,7 @@ var ALL_SYSTEMS []string = []string{ | ||||
| } | ||||
|  | ||||
| var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{ | ||||
| 	"alt-sisyphus", | ||||
| 	// "alt-sisyphus", | ||||
| 	"fedora-41", | ||||
| } | ||||
|  | ||||
| @@ -157,9 +157,9 @@ func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testin | ||||
| 				imageId := fmt.Sprintf("alr-testimage-%s", id) | ||||
| 				runnable := e.Runnable(dockerName).Init( | ||||
| 					e2e.StartOptions{ | ||||
| 						Image: imageId, | ||||
| 						Image:   imageId, | ||||
| 						Volumes: []string{ | ||||
| 							"./alr:/usr/bin/alr", | ||||
| 							// "./alr:/usr/bin/alr", | ||||
| 						}, | ||||
| 						Privileged: true, | ||||
| 					}, | ||||
|   | ||||
| @@ -1,8 +1,17 @@ | ||||
| FROM fedora:41 | ||||
| RUN dnf install -y ca-certificates sudo rpm-build | ||||
| RUN useradd -m -s /bin/bash alr-user && \ | ||||
|     echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \ | ||||
|     chmod 0440 /etc/sudoers.d/alr-user | ||||
| USER alr-user | ||||
| WORKDIR /home/alr-user | ||||
| RUN <<EOF | ||||
|     useradd -m -s /bin/bash user | ||||
|     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/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"] | ||||
| @@ -1,7 +1,17 @@ | ||||
| FROM ubuntu:24.10 | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo | ||||
| RUN useradd -m -s /bin/bash alr-user && \ | ||||
|     echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \ | ||||
|     chmod 0440 /etc/sudoers.d/alr-user | ||||
| USER alr-user | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo libcap2-bin | ||||
| RUN <<EOF | ||||
|     useradd -m -s /bin/bash user | ||||
|     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/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"] | ||||
| @@ -32,6 +32,7 @@ func TestE2EIssue41AutoreqSkiplist(t *testing.T) { | ||||
| 		AUTOREQ_AUTOPROV_SYSTEMS, | ||||
| 		func(t *testing.T, r e2e.Runnable) { | ||||
| 			err := r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"addrepo", | ||||
| 				"--name", | ||||
|   | ||||
| @@ -32,6 +32,7 @@ func TestE2EIssue50InstallMultiple(t *testing.T) { | ||||
| 		COMMON_SYSTEMS, | ||||
| 		func(t *testing.T, r e2e.Runnable) { | ||||
| 			err := r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"addrepo", | ||||
| 				"--name", | ||||
|   | ||||
| @@ -32,6 +32,7 @@ func TestE2EIssue53LcAllCInfo(t *testing.T) { | ||||
| 		COMMON_SYSTEMS, | ||||
| 		func(t *testing.T, r e2e.Runnable) { | ||||
| 			err := r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"addrepo", | ||||
| 				"--name", | ||||
|   | ||||
| @@ -32,6 +32,7 @@ func TestE2EIssue59RmCompletion(t *testing.T) { | ||||
| 		COMMON_SYSTEMS, | ||||
| 		func(t *testing.T, r e2e.Runnable) { | ||||
| 			err := r.Exec(e2e.NewCommand( | ||||
| 				"sudo", | ||||
| 				"alr", | ||||
| 				"addrepo", | ||||
| 				"--name", | ||||
|   | ||||
							
								
								
									
										26
									
								
								fix.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								fix.go
									
									
									
									
									
								
							| @@ -22,12 +22,14 @@ package main | ||||
| import ( | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"github.com/urfave/cli/v2" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	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/repos" | ||||
| ) | ||||
|  | ||||
| @@ -36,6 +38,11 @@ func FixCmd() *cli.Command { | ||||
| 		Name:  "fix", | ||||
| 		Usage: gotext.Get("Attempt to fix problems with ALR"), | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			if utils.DropCapsToAlrUser() != nil { | ||||
| 				slog.Error(gotext.Get("Can't drop privileges")) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			ctx := c.Context | ||||
| 			cfg := config.New() | ||||
| 			err := cfg.Load() | ||||
| @@ -48,11 +55,26 @@ func FixCmd() *cli.Command { | ||||
|  | ||||
| 			slog.Info(gotext.Get("Removing cache directory")) | ||||
|  | ||||
| 			err = os.RemoveAll(paths.CacheDir) | ||||
| 			dir, err := os.Open(paths.CacheDir) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Unable to remove cache directory"), "err", err) | ||||
| 				slog.Error(gotext.Get("Unable to open cache directory"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
| 			defer dir.Close() | ||||
|  | ||||
| 			entries, err := dir.Readdirnames(-1) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Unable to read cache directory contents"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			for _, entry := range entries { | ||||
| 				err = os.RemoveAll(filepath.Join(paths.CacheDir, entry)) | ||||
| 				if err != nil { | ||||
| 					slog.Error(gotext.Get("Unable to remove cache item"), "item", entry, "err", err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			slog.Info(gotext.Get("Rebuilding cache")) | ||||
|  | ||||
|   | ||||
							
								
								
									
										17
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go.mod
									
									
									
									
									
								
							| @@ -4,8 +4,9 @@ go 1.22 | ||||
|  | ||||
| toolchain go1.23.5 | ||||
|  | ||||
| replace github.com/creack/pty => github.com/creack/pty v1.1.19 | ||||
|  | ||||
| require ( | ||||
| 	gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1 | ||||
| 	github.com/AlecAivazis/survey/v2 v2.3.7 | ||||
| 	github.com/PuerkitoBio/purell v1.2.0 | ||||
| 	github.com/alecthomas/assert/v2 v2.2.1 | ||||
| @@ -15,11 +16,14 @@ require ( | ||||
| 	github.com/charmbracelet/bubbletea v1.2.4 | ||||
| 	github.com/charmbracelet/lipgloss v1.0.0 | ||||
| 	github.com/charmbracelet/log v0.4.0 | ||||
| 	github.com/creack/pty v1.1.24 | ||||
| 	github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 | ||||
| 	github.com/go-git/go-billy/v5 v5.5.0 | ||||
| 	github.com/go-git/go-git/v5 v5.12.0 | ||||
| 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | ||||
| 	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/jmoiron/sqlx v1.3.5 | ||||
| 	github.com/leonelquinteros/gotext v1.7.0 | ||||
| @@ -33,7 +37,7 @@ require ( | ||||
| 	github.com/urfave/cli/v2 v2.25.7 | ||||
| 	github.com/vmihailenco/msgpack/v5 v5.3.5 | ||||
| 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | ||||
| 	golang.org/x/crypto v0.27.0 | ||||
| 	golang.org/x/crypto v0.32.0 | ||||
| 	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb | ||||
| 	golang.org/x/sys v0.29.0 | ||||
| 	golang.org/x/text v0.21.0 | ||||
| @@ -72,10 +76,12 @@ require ( | ||||
| 	github.com/efficientgo/core v1.0.0-rc.0 // indirect | ||||
| 	github.com/emirpasic/gods v1.18.1 // 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-logfmt/logfmt v0.6.0 // indirect | ||||
| 	github.com/gobwas/glob v0.2.3 // 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/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect | ||||
| 	github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect | ||||
| @@ -84,6 +90,7 @@ require ( | ||||
| 	github.com/goreleaser/fileglob v1.3.0 // indirect | ||||
| 	github.com/hashicorp/errwrap v1.0.0 // 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/huandu/xstrings v1.3.3 // indirect | ||||
| 	github.com/imdario/mergo v0.3.16 // indirect | ||||
| @@ -103,6 +110,7 @@ require ( | ||||
| 	github.com/muesli/cancelreader v0.2.2 // indirect | ||||
| 	github.com/muesli/termenv v0.15.2 // indirect | ||||
| 	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect | ||||
| 	github.com/oklog/run v1.0.0 // indirect | ||||
| 	github.com/pierrec/lz4/v4 v4.1.15 // indirect | ||||
| 	github.com/pjbgf/sha1cd v0.3.0 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| @@ -121,10 +129,13 @@ require ( | ||||
| 	gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect | ||||
| 	go4.org v0.0.0-20200411211856-f5505b9728dd // 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/term v0.28.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 | ||||
| 	lukechampine.com/uint128 v1.2.0 // indirect | ||||
| 	modernc.org/cc/v3 v3.40.0 // indirect | ||||
|   | ||||
							
								
								
									
										58
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								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/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= | ||||
| 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/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= | ||||
| 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/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= | ||||
| 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/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= | ||||
| 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/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= | ||||
| 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.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
| github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||||
| 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/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= | ||||
| github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= | ||||
| @@ -106,9 +106,8 @@ github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEyc | ||||
| github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA= | ||||
| 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/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.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= | ||||
| github.com/creack/pty v1.1.19 h1:tUN6H7LWqNx4hQVxomd0CVsDwaDr9gaRQaI4GpSmrsA= | ||||
| github.com/creack/pty v1.1.19/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | ||||
| github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= | ||||
| 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= | ||||
| @@ -133,6 +132,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/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/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/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||
| github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= | ||||
| @@ -171,8 +172,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.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.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| 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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| @@ -181,6 +183,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.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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= | ||||
| @@ -211,10 +214,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/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/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/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.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/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= | ||||
| github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= | ||||
| @@ -229,6 +238,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/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/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/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | ||||
| github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||
| @@ -264,9 +275,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/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= | ||||
| 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/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.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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| @@ -304,6 +317,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/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/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/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= | ||||
| github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | ||||
| @@ -405,8 +420,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.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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | ||||
| golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | ||||
| golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||
| 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
| @@ -458,15 +473,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.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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= | ||||
| golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= | ||||
| golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||
| 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-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-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-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= | ||||
| golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= | ||||
| 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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| @@ -487,6 +502,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-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-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-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -572,8 +588,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.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.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= | ||||
| google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||
| 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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| @@ -587,6 +603,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-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| 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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
| @@ -594,8 +612,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.27.0/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.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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
|   | ||||
							
								
								
									
										51
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								install.go
									
									
									
									
									
								
							| @@ -27,10 +27,10 @@ import ( | ||||
| 	"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/config" | ||||
| 	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/utils" | ||||
| 	"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" | ||||
| @@ -79,6 +79,17 @@ func InstallCmd() *cli.Command { | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			err = utils.DropCapsToAlrUser() | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error dropping capabilities"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			builder := build.NewMainBuilder( | ||||
| 				cfg, | ||||
| 				rs, | ||||
| 			) | ||||
|  | ||||
| 			if cfg.AutoPull() { | ||||
| 				err := rs.Pull(ctx, cfg.Repos()) | ||||
| 				if err != nil { | ||||
| @@ -87,39 +98,29 @@ func InstallCmd() *cli.Command { | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			found, notFound, err := rs.FindPkgs(ctx, args.Slice()) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error finding packages"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			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( | ||||
| 			err = builder.InstallPkgs( | ||||
| 				ctx, | ||||
| 				opts, | ||||
| 				rs, | ||||
| 				info, | ||||
| 				cfg, | ||||
| 				&build.BuildArgs{ | ||||
| 					Opts: &types.BuildOpts{ | ||||
| 						Clean:       c.Bool("clean"), | ||||
| 						Interactive: c.Bool("interactive"), | ||||
| 					}, | ||||
| 					Info:       info, | ||||
| 					PkgFormat_: build.GetPkgFormat(mgr), | ||||
| 				}, | ||||
| 				args.Slice(), | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error parsing os release"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			builder.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{ | ||||
| 				Manager:     mgr, | ||||
| 				Clean:       c.Bool("clean"), | ||||
| 				Interactive: c.Bool("interactive"), | ||||
| 			}) | ||||
| 			return nil | ||||
| 		}, | ||||
| 		BashComplete: func(c *cli.Context) { | ||||
|   | ||||
							
								
								
									
										133
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| // 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 ( | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"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/config" | ||||
| 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"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" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||
| ) | ||||
|  | ||||
| func InternalBuildCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Name:     "_internal-safe-script-executor", | ||||
| 		HideHelp: true, | ||||
| 		Hidden:   true, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			logger.SetupForGoPlugin() | ||||
| 			err := utils.DropCapsToAlrUser() | ||||
| 			if err != nil { | ||||
| 				slog.Error("aa", "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
| 			slog.Info("", | ||||
| 				"uid", os.Getuid(), | ||||
| 				"gid", os.Getgid(), | ||||
| 			) | ||||
| 			cfg := config.New() | ||||
| 			err = cfg.Load() | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error loading config"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			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() | ||||
| 			err := syscall.Setuid(0) | ||||
| 			if err != nil { | ||||
| 				slog.Error("err") | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			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) | ||||
| 			rs := repos.New(cfg, db) | ||||
| 			err = db.Init(c.Context) | ||||
| 			if err != nil { | ||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			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( | ||||
| 							rs, | ||||
| 							manager.Detect(), | ||||
| 						), | ||||
| 					}, | ||||
| 				}, | ||||
| 				Logger: logger, | ||||
| 			}) | ||||
| 			return nil | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @@ -26,7 +26,6 @@ import ( | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/caarlos0/env" | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"github.com/pelletier/go-toml/v2" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| @@ -84,7 +83,10 @@ func mergeStructs(dst, src interface{}) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const systemConfigPath = "/etc/alr/alr.toml" | ||||
| const ( | ||||
| 	systemConfigPath = "/etc/alr/alr.toml" | ||||
| 	systemCachePath  = "/var/cache/alr" | ||||
| ) | ||||
|  | ||||
| func (c *ALRConfig) Load() error { | ||||
| 	systemConfig, err := readConfig( | ||||
| @@ -94,24 +96,10 @@ func (c *ALRConfig) Load() error { | ||||
| 		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{} | ||||
|  | ||||
| 	mergeStructs(config, defaultConfig) | ||||
| 	mergeStructs(config, systemConfig) | ||||
| 	mergeStructs(config, userConfig) | ||||
| 	err = env.Parse(config) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -119,17 +107,13 @@ func (c *ALRConfig) Load() error { | ||||
|  | ||||
| 	c.cfg = config | ||||
|  | ||||
| 	cacheDir, err := os.UserCacheDir() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.paths = &Paths{} | ||||
| 	c.paths.UserConfigPath = userConfigPath | ||||
| 	c.paths.CacheDir = filepath.Join(cacheDir, "alr") | ||||
| 	c.paths.UserConfigPath = systemConfigPath | ||||
| 	c.paths.CacheDir = systemCachePath | ||||
| 	c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") | ||||
| 	c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") | ||||
| 	c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") | ||||
| 	c.initPaths() | ||||
| 	// c.initPaths() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -170,26 +154,6 @@ func (c *ALRConfig) GetPaths() *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 { | ||||
| 	f, err := os.Create(c.paths.UserConfigPath) | ||||
| 	if err != nil { | ||||
|   | ||||
							
								
								
									
										136
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| // 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" | ||||
|  | ||||
| 	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]) | ||||
| 		} | ||||
| 	} | ||||
| 	a.logger.l.Log(hclogLevelTochLog(level), 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" | ||||
|  | ||||
| 	"github.com/charmbracelet/lipgloss" | ||||
| 	"github.com/charmbracelet/log" | ||||
|  | ||||
| 	chLog "github.com/charmbracelet/log" | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| ) | ||||
|  | ||||
| type Logger struct { | ||||
| 	lOut slog.Handler | ||||
| 	lErr slog.Handler | ||||
| 	l *chLog.Logger | ||||
| } | ||||
|  | ||||
| func setupOutLogger() *log.Logger { | ||||
| 	styles := log.DefaultStyles() | ||||
| 	logger := log.New(os.Stdout) | ||||
| 	styles.Levels[log.InfoLevel] = lipgloss.NewStyle(). | ||||
| func setupLogger() *chLog.Logger { | ||||
| 	styles := chLog.DefaultStyles() | ||||
| 	logger := chLog.New(os.Stderr) | ||||
| 	styles.Levels[chLog.InfoLevel] = lipgloss.NewStyle(). | ||||
| 		SetString("-->"). | ||||
| 		Foreground(lipgloss.Color("35")) | ||||
| 	logger.SetStyles(styles) | ||||
| 	return logger | ||||
| } | ||||
|  | ||||
| func setupErrorLogger() *log.Logger { | ||||
| 	styles := log.DefaultStyles() | ||||
| 	styles.Levels[log.ErrorLevel] = lipgloss.NewStyle(). | ||||
| 	styles.Levels[chLog.ErrorLevel] = lipgloss.NewStyle(). | ||||
| 		SetString(gotext.Get("ERROR")). | ||||
| 		Padding(0, 1, 0, 1). | ||||
| 		Background(lipgloss.Color("204")). | ||||
| 		Foreground(lipgloss.Color("0")) | ||||
| 	logger := log.New(os.Stderr) | ||||
| 	logger.SetStyles(styles) | ||||
| 	return logger | ||||
| } | ||||
|  | ||||
| func New() *Logger { | ||||
| 	standardLogger := setupOutLogger() | ||||
| 	errLogger := setupErrorLogger() | ||||
| 	return &Logger{ | ||||
| 		lOut: standardLogger, | ||||
| 		lErr: errLogger, | ||||
| 		l: setupLogger(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func slogLevelToLog(level slog.Level) log.Level { | ||||
| func slogLevelToLog(level slog.Level) chLog.Level { | ||||
| 	switch level { | ||||
| 	case slog.LevelDebug: | ||||
| 		return log.DebugLevel | ||||
| 		return chLog.DebugLevel | ||||
| 	case slog.LevelInfo: | ||||
| 		return log.InfoLevel | ||||
| 		return chLog.InfoLevel | ||||
| 	case slog.LevelWarn: | ||||
| 		return log.WarnLevel | ||||
| 		return chLog.WarnLevel | ||||
| 	case slog.LevelError: | ||||
| 		return log.ErrorLevel | ||||
| 		return chLog.ErrorLevel | ||||
| 	} | ||||
| 	return log.FatalLevel | ||||
| 	return chLog.FatalLevel | ||||
| } | ||||
|  | ||||
| func (l *Logger) SetLevel(level slog.Level) { | ||||
| 	l.lOut.(*log.Logger).SetLevel(slogLevelToLog(level)) | ||||
| 	l.lErr.(*log.Logger).SetLevel(slogLevelToLog(level)) | ||||
| 	l.l.SetLevel(slogLevelToLog(level)) | ||||
| } | ||||
|  | ||||
| func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool { | ||||
| 	if level <= slog.LevelInfo { | ||||
| 		return l.lOut.Enabled(ctx, level) | ||||
| 	} | ||||
| 	return l.lErr.Enabled(ctx, level) | ||||
| 	return l.l.Enabled(ctx, level) | ||||
| } | ||||
|  | ||||
| func (l *Logger) Handle(ctx context.Context, rec slog.Record) error { | ||||
| 	if rec.Level <= slog.LevelInfo { | ||||
| 		return l.lOut.Handle(ctx, rec) | ||||
| 	} | ||||
| 	return l.lErr.Handle(ctx, rec) | ||||
| 	return l.l.Handle(ctx, rec) | ||||
| } | ||||
|  | ||||
| func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler { | ||||
| 	sl := *l | ||||
| 	sl.lOut = l.lOut.WithAttrs(attrs) | ||||
| 	sl.lErr = l.lErr.WithAttrs(attrs) | ||||
| 	sl.l = l.l.WithAttrs(attrs).(*chLog.Logger) | ||||
| 	return &sl | ||||
| } | ||||
|  | ||||
| func (l *Logger) WithGroup(name string) slog.Handler { | ||||
| 	sl := *l | ||||
| 	sl.lOut = l.lOut.WithGroup(name) | ||||
| 	sl.lErr = l.lErr.WithGroup(name) | ||||
| 	sl.l = l.l.WithGroup(name).(*chLog.Logger) | ||||
| 	return &sl | ||||
| } | ||||
|  | ||||
| var logger *Logger | ||||
|  | ||||
| func SetupDefault() *Logger { | ||||
| 	l := New() | ||||
| 	logger := slog.New(l) | ||||
| 	slog.SetDefault(logger) | ||||
| 	return l | ||||
| 	logger = New() | ||||
| 	slogLogger := slog.New(logger) | ||||
| 	slog.SetDefault(slogLogger) | ||||
| 	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/exec" | ||||
| 	"runtime" | ||||
| 	"slices" | ||||
| 	"strings" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/fakeroot" | ||||
| 	"mvdan.cc/sh/v3/expand" | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| ) | ||||
| @@ -54,7 +54,7 @@ func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc { | ||||
| 			Stderr: hc.Stderr, | ||||
| 		} | ||||
|  | ||||
| 		err = fakeroot.Apply(cmd) | ||||
| 		err = Apply(cmd) | ||||
| 		if err != nil { | ||||
| 			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 | ||||
| func execEnv(env expand.Environ) []string { | ||||
| 	list := make([]string, 0, 64) | ||||
|   | ||||
| @@ -42,55 +42,63 @@ msgstr "" | ||||
| msgid "Package not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:130 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:138 | ||||
| #: build.go:127 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:144 | ||||
| #: build.go:133 | ||||
| msgid "Error parsing os release" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:166 | ||||
| #: build.go:159 | ||||
| msgid "Error building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:173 | ||||
| #: build.go:166 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:182 | ||||
| #: build.go:175 | ||||
| msgid "Error moving the package" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:37 | ||||
| #: fix.go:39 | ||||
| msgid "Attempt to fix problems with ALR" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:49 | ||||
| #: fix.go:42 | ||||
| msgid "Can't drop privileges" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:56 | ||||
| msgid "Removing cache directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:53 | ||||
| msgid "Unable to remove cache directory" | ||||
| #: fix.go:60 | ||||
| msgid "Unable to open cache directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:57 | ||||
| #: fix.go:67 | ||||
| msgid "Unable to read cache directory contents" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:74 | ||||
| msgid "Unable to remove cache item" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:79 | ||||
| msgid "Rebuilding cache" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:61 | ||||
| #: fix.go:83 | ||||
| msgid "Unable to create new cache directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:81 | ||||
| #: fix.go:103 | ||||
| msgid "Error pulling repos" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:85 | ||||
| #: fix.go:107 | ||||
| msgid "Done" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -162,19 +170,27 @@ msgstr "" | ||||
| msgid "Command install expected at least 1 argument, got %d" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:163 | ||||
| #: install.go:84 | ||||
| msgid "Error dropping capabilities" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:96 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:164 | ||||
| msgid "Remove an installed package" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:188 | ||||
| #: install.go:189 | ||||
| msgid "Error listing installed packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:226 | ||||
| #: install.go:227 | ||||
| msgid "Command remove expected at least 1 argument, got %d" | ||||
| msgstr "" | ||||
|  | ||||
| #: install.go:241 | ||||
| #: install.go:242 | ||||
| msgid "Error removing packages" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -258,18 +274,6 @@ msgstr "" | ||||
| msgid "OPTIONS" | ||||
| 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 | ||||
| msgid "Database version mismatch; resetting" | ||||
| msgstr "" | ||||
| @@ -303,10 +307,14 @@ msgstr "" | ||||
| msgid "%s %s downloading at %s/s\n" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/logger/log.go:47 | ||||
| #: internal/logger/log.go:41 | ||||
| msgid "ERROR" | ||||
| msgstr "" | ||||
|  | ||||
| #: internal/utils/cmd.go:65 | ||||
| msgid "You need to be root" | ||||
| msgstr "" | ||||
|  | ||||
| #: list.go:41 | ||||
| msgid "List ALR repo packages" | ||||
| msgstr "" | ||||
| @@ -315,94 +323,48 @@ msgstr "" | ||||
| msgid "Print the current ALR version and exit" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:61 | ||||
| #: main.go:79 | ||||
| msgid "Arguments to be passed on to the package manager" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:67 | ||||
| #: main.go:85 | ||||
| msgid "Enable interactive questions and prompts" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:96 | ||||
| msgid "" | ||||
| "Running ALR as root is forbidden as it may cause catastrophic damage to your " | ||||
| "system" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:154 | ||||
| #: main.go:183 | ||||
| msgid "Show help" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:158 | ||||
| #: main.go:187 | ||||
| msgid "Error while running app" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:157 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:161 | ||||
| #: pkg/build/build.go:392 | ||||
| msgid "Building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:209 | ||||
| #: pkg/build/build.go:421 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:238 | ||||
| #: pkg/build/build.go:448 | ||||
| msgid "Downloading sources" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:260 | ||||
| msgid "Building package metadata" | ||||
| #: pkg/build/build.go:507 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:282 | ||||
| msgid "Compressing package" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:441 | ||||
| #: pkg/build/checker.go:43 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/build.go:455 | ||||
| #: pkg/build/checker.go:67 | ||||
| msgid "This package is already installed" | ||||
| 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 | ||||
| msgid "Command not found on the system" | ||||
| msgstr "" | ||||
| @@ -423,6 +385,22 @@ msgstr "" | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:236 | ||||
| msgid "Building package metadata" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:355 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:364 | ||||
| msgid "Executing build()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:393 pkg/build/script_executor.go:413 | ||||
| msgid "Executing %s()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:79 | ||||
| msgid "Pulling repository" | ||||
| msgstr "" | ||||
| @@ -441,86 +419,74 @@ msgid "" | ||||
| "updating ALR if something doesn't work." | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:40 | ||||
| #: repo.go:41 | ||||
| msgid "Add a new repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:47 | ||||
| #: repo.go:48 | ||||
| msgid "Name of the new repo" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:53 | ||||
| #: repo.go:54 | ||||
| msgid "URL of the new repo" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:86 repo.go:156 | ||||
| #: repo.go:89 repo.go:166 | ||||
| msgid "Error saving config" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:111 | ||||
| #: repo.go:119 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:118 | ||||
| #: repo.go:126 | ||||
| msgid "Name of the repo to be deleted" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:142 | ||||
| #: repo.go:152 | ||||
| msgid "Repo does not exist" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:150 | ||||
| #: repo.go:160 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:167 | ||||
| #: repo.go:177 | ||||
| msgid "Error removing packages from database" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:179 | ||||
| #: repo.go:189 | ||||
| msgid "Pull all repositories that have changed" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:36 | ||||
| #: search.go:37 | ||||
| msgid "Search packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:42 | ||||
| #: search.go:43 | ||||
| msgid "Search by name" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:47 | ||||
| #: search.go:48 | ||||
| msgid "Search by description" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:52 | ||||
| #: search.go:53 | ||||
| msgid "Search by repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:57 | ||||
| #: search.go:58 | ||||
| msgid "Search by provides" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:62 | ||||
| #: search.go:63 | ||||
| msgid "Format output using a Go template" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:88 search.go:105 | ||||
| #: search.go:94 search.go:111 | ||||
| msgid "Error parsing format template" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:113 | ||||
| #: search.go:119 | ||||
| msgid "Error executing template" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:47 | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:96 | ||||
| msgid "Error checking for updates" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:118 | ||||
| msgid "There is nothing to do." | ||||
| msgstr "" | ||||
|   | ||||
| @@ -50,55 +50,66 @@ msgstr "Ошибка инициализации базы данных" | ||||
| msgid "Package not found" | ||||
| msgstr "Пакет не найден" | ||||
|  | ||||
| #: build.go:130 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "Ошибка при извлечении репозиториев" | ||||
|  | ||||
| #: build.go:138 | ||||
| #: build.go:127 | ||||
| msgid "Unable to detect a supported package manager on the system" | ||||
| msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" | ||||
|  | ||||
| #: build.go:144 | ||||
| #: build.go:133 | ||||
| msgid "Error parsing os release" | ||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||
|  | ||||
| #: build.go:166 | ||||
| #: build.go:159 | ||||
| msgid "Error building package" | ||||
| msgstr "Ошибка при сборке пакета" | ||||
|  | ||||
| #: build.go:173 | ||||
| #: build.go:166 | ||||
| msgid "Error getting working directory" | ||||
| msgstr "Ошибка при получении рабочего каталога" | ||||
|  | ||||
| #: build.go:182 | ||||
| #: build.go:175 | ||||
| msgid "Error moving the package" | ||||
| msgstr "Ошибка при перемещении пакета" | ||||
|  | ||||
| #: fix.go:37 | ||||
| #: fix.go:39 | ||||
| msgid "Attempt to fix problems with ALR" | ||||
| msgstr "Попытка устранить проблемы с ALR" | ||||
|  | ||||
| #: fix.go:49 | ||||
| #: fix.go:42 | ||||
| msgid "Can't drop privileges" | ||||
| msgstr "" | ||||
|  | ||||
| #: fix.go:56 | ||||
| msgid "Removing cache directory" | ||||
| msgstr "Удаление каталога кэша" | ||||
|  | ||||
| #: fix.go:53 | ||||
| msgid "Unable to remove cache directory" | ||||
| #: fix.go:60 | ||||
| #, fuzzy | ||||
| msgid "Unable to open cache directory" | ||||
| msgstr "Не удалось удалить каталог кэша" | ||||
|  | ||||
| #: fix.go:57 | ||||
| #: fix.go:67 | ||||
| #, fuzzy | ||||
| msgid "Unable to read cache directory contents" | ||||
| msgstr "Не удалось удалить каталог кэша" | ||||
|  | ||||
| #: fix.go:74 | ||||
| #, fuzzy | ||||
| msgid "Unable to remove cache item" | ||||
| msgstr "Не удалось удалить каталог кэша" | ||||
|  | ||||
| #: fix.go:79 | ||||
| msgid "Rebuilding cache" | ||||
| msgstr "Восстановление кэша" | ||||
|  | ||||
| #: fix.go:61 | ||||
| #: fix.go:83 | ||||
| msgid "Unable to create new cache directory" | ||||
| msgstr "Не удалось создать новый каталог кэша" | ||||
|  | ||||
| #: fix.go:81 | ||||
| #: fix.go:103 | ||||
| msgid "Error pulling repos" | ||||
| msgstr "Ошибка при извлечении репозиториев" | ||||
|  | ||||
| #: fix.go:85 | ||||
| #: fix.go:107 | ||||
| msgid "Done" | ||||
| msgstr "Сделано" | ||||
|  | ||||
| @@ -170,19 +181,28 @@ msgstr "Установить новый пакет" | ||||
| msgid "Command install expected at least 1 argument, got %d" | ||||
| msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" | ||||
|  | ||||
| #: install.go:163 | ||||
| #: install.go:84 | ||||
| #, fuzzy | ||||
| msgid "Error dropping capabilities" | ||||
| msgstr "Ошибка при открытии базы данных" | ||||
|  | ||||
| #: install.go:96 | ||||
| msgid "Error pulling repositories" | ||||
| msgstr "Ошибка при извлечении репозиториев" | ||||
|  | ||||
| #: install.go:164 | ||||
| msgid "Remove an installed package" | ||||
| msgstr "Удалить установленный пакет" | ||||
|  | ||||
| #: install.go:188 | ||||
| #: install.go:189 | ||||
| msgid "Error listing installed packages" | ||||
| msgstr "Ошибка при составлении списка установленных пакетов" | ||||
|  | ||||
| #: install.go:226 | ||||
| #: install.go:227 | ||||
| msgid "Command remove expected at least 1 argument, got %d" | ||||
| msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" | ||||
|  | ||||
| #: install.go:241 | ||||
| #: install.go:242 | ||||
| msgid "Error removing packages" | ||||
| msgstr "Ошибка при удалении пакетов" | ||||
|  | ||||
| @@ -266,19 +286,6 @@ msgstr "КАТЕГОРИЯ" | ||||
| msgid "OPTIONS" | ||||
| 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 | ||||
| msgid "Database version mismatch; resetting" | ||||
| msgstr "Несоответствие версий базы данных; сброс настроек" | ||||
| @@ -313,10 +320,14 @@ msgstr "%s: выполнено!\n" | ||||
| msgid "%s %s downloading at %s/s\n" | ||||
| msgstr "%s %s загружается — %s/с\n" | ||||
|  | ||||
| #: internal/logger/log.go:47 | ||||
| #: internal/logger/log.go:41 | ||||
| msgid "ERROR" | ||||
| msgstr "ОШИБКА" | ||||
|  | ||||
| #: internal/utils/cmd.go:65 | ||||
| msgid "You need to be root" | ||||
| msgstr "" | ||||
|  | ||||
| #: list.go:41 | ||||
| msgid "List ALR repo packages" | ||||
| msgstr "Список пакетов репозитория ALR" | ||||
| @@ -325,55 +336,39 @@ msgstr "Список пакетов репозитория ALR" | ||||
| msgid "Print the current ALR version and exit" | ||||
| msgstr "Показать текущую версию ALR и выйти" | ||||
|  | ||||
| #: main.go:61 | ||||
| #: main.go:79 | ||||
| msgid "Arguments to be passed on to the package manager" | ||||
| msgstr "Аргументы, которые будут переданы менеджеру пакетов" | ||||
|  | ||||
| #: main.go:67 | ||||
| #: main.go:85 | ||||
| msgid "Enable interactive questions and prompts" | ||||
| msgstr "Включение интерактивных вопросов и запросов" | ||||
|  | ||||
| #: main.go:96 | ||||
| msgid "" | ||||
| "Running ALR as root is forbidden as it may cause catastrophic damage to your " | ||||
| "system" | ||||
| msgstr "" | ||||
| "Запуск ALR от имени root запрещён, так как это может привести к " | ||||
| "катастрофическому повреждению вашей системы" | ||||
|  | ||||
| #: main.go:154 | ||||
| #: main.go:183 | ||||
| msgid "Show help" | ||||
| msgstr "Показать справку" | ||||
|  | ||||
| #: main.go:158 | ||||
| #: main.go:187 | ||||
| msgid "Error while running app" | ||||
| msgstr "Ошибка при запуске приложения" | ||||
|  | ||||
| #: pkg/build/build.go:157 | ||||
| msgid "Failed to prompt user to view build script" | ||||
| msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" | ||||
|  | ||||
| #: pkg/build/build.go:161 | ||||
| #: pkg/build/build.go:392 | ||||
| msgid "Building package" | ||||
| msgstr "Сборка пакета" | ||||
|  | ||||
| #: pkg/build/build.go:209 | ||||
| #: pkg/build/build.go:421 | ||||
| msgid "The checksums array must be the same length as sources" | ||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||
|  | ||||
| #: pkg/build/build.go:238 | ||||
| #: pkg/build/build.go:448 | ||||
| msgid "Downloading sources" | ||||
| msgstr "Скачивание источников" | ||||
|  | ||||
| #: pkg/build/build.go:260 | ||||
| msgid "Building package metadata" | ||||
| msgstr "Сборка метаданных пакета" | ||||
| #: pkg/build/build.go:507 | ||||
| msgid "Installing dependencies" | ||||
| msgstr "Установка зависимостей" | ||||
|  | ||||
| #: pkg/build/build.go:282 | ||||
| msgid "Compressing package" | ||||
| msgstr "Сжатие пакета" | ||||
|  | ||||
| #: pkg/build/build.go:441 | ||||
| #: pkg/build/checker.go:43 | ||||
| msgid "" | ||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | ||||
| "build anyway?" | ||||
| @@ -381,42 +376,10 @@ msgstr "" | ||||
| "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||
| "равно хотите выполнить сборку?" | ||||
|  | ||||
| #: pkg/build/build.go:455 | ||||
| #: pkg/build/checker.go:67 | ||||
| msgid "This package is already installed" | ||||
| 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 | ||||
| msgid "Command not found on the system" | ||||
| msgstr "Команда не найдена в системе" | ||||
| @@ -439,6 +402,22 @@ msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/script_executor.go:236 | ||||
| msgid "Building package metadata" | ||||
| msgstr "Сборка метаданных пакета" | ||||
|  | ||||
| #: pkg/build/script_executor.go:355 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "Исполнение prepare()" | ||||
|  | ||||
| #: pkg/build/script_executor.go:364 | ||||
| msgid "Executing build()" | ||||
| msgstr "Исполнение build()" | ||||
|  | ||||
| #: pkg/build/script_executor.go:393 pkg/build/script_executor.go:413 | ||||
| msgid "Executing %s()" | ||||
| msgstr "Исполнение %s()" | ||||
|  | ||||
| #: pkg/repos/pull.go:79 | ||||
| msgid "Pulling repository" | ||||
| msgstr "Скачивание репозитория" | ||||
| @@ -459,90 +438,122 @@ msgstr "" | ||||
| "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " | ||||
| "обновить ALR, если что-то не работает." | ||||
|  | ||||
| #: repo.go:40 | ||||
| #: repo.go:41 | ||||
| msgid "Add a new repository" | ||||
| msgstr "Добавить новый репозиторий" | ||||
|  | ||||
| #: repo.go:47 | ||||
| #: repo.go:48 | ||||
| msgid "Name of the new repo" | ||||
| msgstr "Название нового репозитория" | ||||
|  | ||||
| #: repo.go:53 | ||||
| #: repo.go:54 | ||||
| msgid "URL of the new repo" | ||||
| msgstr "URL-адрес нового репозитория" | ||||
|  | ||||
| #: repo.go:86 repo.go:156 | ||||
| #: repo.go:89 repo.go:166 | ||||
| #, fuzzy | ||||
| msgid "Error saving config" | ||||
| msgstr "Ошибка при кодировании конфигурации" | ||||
|  | ||||
| #: repo.go:111 | ||||
| #: repo.go:119 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "Удалить существующий репозиторий" | ||||
|  | ||||
| #: repo.go:118 | ||||
| #: repo.go:126 | ||||
| msgid "Name of the repo to be deleted" | ||||
| msgstr "Название репозитория  удалён" | ||||
|  | ||||
| #: repo.go:142 | ||||
| #: repo.go:152 | ||||
| msgid "Repo does not exist" | ||||
| msgstr "Репозитория не существует" | ||||
|  | ||||
| #: repo.go:150 | ||||
| #: repo.go:160 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "Ошибка при удалении каталога репозитория" | ||||
|  | ||||
| #: repo.go:167 | ||||
| #: repo.go:177 | ||||
| msgid "Error removing packages from database" | ||||
| msgstr "Ошибка при удалении пакетов из базы данных" | ||||
|  | ||||
| #: repo.go:179 | ||||
| #: repo.go:189 | ||||
| msgid "Pull all repositories that have changed" | ||||
| msgstr "Скачать все изменённые репозитории" | ||||
|  | ||||
| #: search.go:36 | ||||
| #: search.go:37 | ||||
| msgid "Search packages" | ||||
| msgstr "Поиск пакетов" | ||||
|  | ||||
| #: search.go:42 | ||||
| #: search.go:43 | ||||
| msgid "Search by name" | ||||
| msgstr "Искать по имени" | ||||
|  | ||||
| #: search.go:47 | ||||
| #: search.go:48 | ||||
| msgid "Search by description" | ||||
| msgstr "Искать по описанию" | ||||
|  | ||||
| #: search.go:52 | ||||
| #: search.go:53 | ||||
| msgid "Search by repository" | ||||
| msgstr "Искать по репозиторию" | ||||
|  | ||||
| #: search.go:57 | ||||
| #: search.go:58 | ||||
| msgid "Search by provides" | ||||
| msgstr "Иcкать по provides" | ||||
|  | ||||
| #: search.go:62 | ||||
| #: search.go:63 | ||||
| msgid "Format output using a Go template" | ||||
| msgstr "Формат выходных данных с использованием шаблона Go" | ||||
|  | ||||
| #: search.go:88 search.go:105 | ||||
| #: search.go:94 search.go:111 | ||||
| msgid "Error parsing format template" | ||||
| msgstr "Ошибка при разборе шаблона" | ||||
|  | ||||
| #: search.go:113 | ||||
| #: search.go:119 | ||||
| msgid "Error executing template" | ||||
| msgstr "Ошибка при выполнении шаблона" | ||||
|  | ||||
| #: upgrade.go:47 | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "Обновить все установленные пакеты" | ||||
| #, fuzzy | ||||
| #~ msgid "Unable to create config directory" | ||||
| #~ msgstr "Не удалось создать каталог конфигурации ALR" | ||||
|  | ||||
| #: upgrade.go:96 | ||||
| msgid "Error checking for updates" | ||||
| msgstr "Ошибка при проверке обновлений" | ||||
| #~ msgid "Unable to create repo cache directory" | ||||
| #~ msgstr "Не удалось создать каталог кэша репозитория" | ||||
|  | ||||
| #: upgrade.go:118 | ||||
| msgid "There is nothing to do." | ||||
| 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 "Upgrade all installed packages" | ||||
| #~ msgstr "Обновить все установленные пакеты" | ||||
|  | ||||
| #~ msgid "Error checking for updates" | ||||
| #~ msgstr "Ошибка при проверке обновлений" | ||||
|  | ||||
| #~ msgid "There is nothing to do." | ||||
| #~ msgstr "Здесь нечего делать." | ||||
|  | ||||
| #~ msgid "Error opening config file, using defaults" | ||||
| #~ msgstr "" | ||||
| @@ -572,9 +583,6 @@ msgstr "Здесь нечего делать." | ||||
| #~ msgid "Error parsing system language" | ||||
| #~ msgstr "Ошибка при парсинге языка системы" | ||||
|  | ||||
| #~ msgid "Error opening database" | ||||
| #~ msgstr "Ошибка при открытии базы данных" | ||||
|  | ||||
| #~ msgid "Executing version()" | ||||
| #~ msgstr "Исполнение версия()" | ||||
|  | ||||
|   | ||||
| @@ -19,13 +19,11 @@ | ||||
|  | ||||
| package types | ||||
|  | ||||
| import "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
|  | ||||
| type BuildOpts struct { | ||||
| 	Script      string | ||||
| 	Repository  string | ||||
| 	Packages    []string | ||||
| 	Manager     manager.Manager | ||||
| 	// Script      string | ||||
| 	// Repository  string | ||||
| 	// Packages    []string | ||||
| 	// Manager     manager.Manager | ||||
| 	Clean       bool | ||||
| 	Interactive bool | ||||
| } | ||||
|   | ||||
							
								
								
									
										68
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // 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 ( | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"os/user" | ||||
| 	"strconv" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| ) | ||||
|  | ||||
| func GetUidGidAlrUser() (int, int, error) { | ||||
| 	u, err := user.Lookup("alr") | ||||
| 	if err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
|  | ||||
| 	uid, err := strconv.Atoi(u.Uid) | ||||
| 	if err != nil { | ||||
| 		return 0, 0, err | ||||
| 	} | ||||
| 	gid, err := strconv.Atoi(u.Gid) | ||||
| 	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 nil | ||||
| } | ||||
|  | ||||
| func ExitIfNotRoot() { | ||||
| 	if os.Getuid() != 0 { | ||||
| 		slog.Error(gotext.Get("You need to be root")) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										63
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								main.go
									
									
									
									
									
								
							| @@ -21,10 +21,10 @@ package main | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strings" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| @@ -50,6 +50,24 @@ func VersionCmd() *cli.Command { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 GetApp() *cli.App { | ||||
| 	return &cli.App{ | ||||
| 		Name:  "alr", | ||||
| @@ -70,7 +88,7 @@ func GetApp() *cli.App { | ||||
| 		Commands: []*cli.Command{ | ||||
| 			InstallCmd(), | ||||
| 			RemoveCmd(), | ||||
| 			UpgradeCmd(), | ||||
| 			// UpgradeCmd(), | ||||
| 			InfoCmd(), | ||||
| 			ListCmd(), | ||||
| 			BuildCmd(), | ||||
| @@ -82,29 +100,40 @@ func GetApp() *cli.App { | ||||
| 			HelperCmd(), | ||||
| 			VersionCmd(), | ||||
| 			SearchCmd(), | ||||
| 			// TEST | ||||
| 			InternalBuildCmd(), | ||||
| 			InternalInstallCmd(), | ||||
| 			// InternalBuild2Cmd(), | ||||
| 		}, | ||||
| 		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) | ||||
| 			} | ||||
| 			/* | ||||
| 				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) | ||||
| 			} | ||||
| 				/* | ||||
| 					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 != "" { | ||||
| 				args := strings.Split(trimmed, " ") | ||||
| 				manager.Args = append(manager.Args, args...) | ||||
| 			} | ||||
|  | ||||
| 				if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { | ||||
| 					args := strings.Split(trimmed, " ") | ||||
| 					manager.Args = append(manager.Args, args...) | ||||
| 				} | ||||
|  | ||||
| 				return nil | ||||
| 			*/ | ||||
| 			return nil | ||||
| 		}, | ||||
| 		EnableBashCompletion: true, | ||||
| 		ExitErrHandler: func(c *cli.Context, err error) { | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										1185
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
							
						
						
									
										1185
									
								
								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") | ||||
| 			assert.NoError(t, err) | ||||
| 
 | ||||
| 			_, allVars, err := b.executeFirstPass(fl) | ||||
| 			_, allVars, err := b.scriptExecutor.ExecuteSecondPass(fl) | ||||
| 			assert.NoError(t, err) | ||||
| 
 | ||||
| 			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) | ||||
| } | ||||
							
								
								
									
										61
									
								
								pkg/build/installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								pkg/build/installer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| // 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( | ||||
| 	repos PackageFinder, | ||||
| 	mgr manager.Manager, | ||||
| ) *Installer { | ||||
| 	return &Installer{ | ||||
| 		repos: repos, | ||||
| 		mgr:   mgr, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type Installer struct { | ||||
| 	repos PackageFinder | ||||
| 	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 | ||||
| } | ||||
							
								
								
									
										68
									
								
								pkg/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								pkg/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // 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 ( | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||
| ) | ||||
|  | ||||
| func NewMainBuilder( | ||||
| 	cfg Config, | ||||
| 	repos PackageFinder, | ||||
| ) *Builder { | ||||
| 	slog.Info("", "uid", os.Geteuid(), "gid", os.Getegid()) | ||||
|  | ||||
| 	s, err := GetSafeScriptExecutor() | ||||
| 	if err != nil { | ||||
| 		slog.Info("i will panic") | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	mgr := manager.Detect() | ||||
|  | ||||
| 	installerExecutor, err := GetSafeInstaller() | ||||
| 	if err != nil { | ||||
| 		slog.Info("i will panic") | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	builder := &Builder{ | ||||
| 		scriptExecutor: s, | ||||
| 		cacheExecutor: &Cache{ | ||||
| 			cfg, | ||||
| 		}, | ||||
| 		scriptResolver: &ScriptResolver{ | ||||
| 			cfg, | ||||
| 		}, | ||||
| 		scriptViewerExecutor: &ScriptViewer{ | ||||
| 			config: cfg, | ||||
| 		}, | ||||
| 		checkerExecutor: &Checker{ | ||||
| 			mgr, | ||||
| 		}, | ||||
| 		installerExecutor: installerExecutor, | ||||
| 		sourceExecutor: &SourceDownloader{ | ||||
| 			cfg, | ||||
| 		}, | ||||
| 		repos: repos, | ||||
| 	} | ||||
|  | ||||
| 	return builder | ||||
| } | ||||
							
								
								
									
										263
									
								
								pkg/build/safe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								pkg/build/safe.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | ||||
| // 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" | ||||
| 	"net/rpc" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/hashicorp/go-plugin" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||
| ) | ||||
|  | ||||
| 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, error) { | ||||
| 	executable, err := os.Executable() | ||||
| 	if err != nil { | ||||
| 		return 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", | ||||
| 	} | ||||
| 	uid, gid, err := utils.GetUidGidAlrUser() | ||||
| 	if err != nil { | ||||
| 		return 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, | ||||
| 	}) | ||||
| 	rpcClient, err := client.Client() | ||||
| 	if err != nil { | ||||
| 		slog.Info("1") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	raw1, err := rpcClient.Dispense("script-executor") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return raw1.(ScriptExecutor), nil | ||||
| } | ||||
							
								
								
									
										132
									
								
								pkg/build/safe_installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								pkg/build/safe_installer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| // 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 ( | ||||
| 	"log/slog" | ||||
| 	"net/rpc" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"syscall" | ||||
|  | ||||
| 	"github.com/hashicorp/go-plugin" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||
| ) | ||||
|  | ||||
| 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 { | ||||
| 	slog.Warn("install", "paths", paths) | ||||
| 	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) { | ||||
| 	err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, nil) | ||||
| 	return nil, 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, error) { | ||||
| 	executable, err := os.Executable() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cmd := exec.Command(executable, "_internal-installer") | ||||
| 	cmd.Env = append(os.Environ(), | ||||
| 		"HOME=/var/cache/alr", | ||||
| 		"LOGNAME=alr", | ||||
| 		"USER=alr", | ||||
| 		"PATH=/usr/bin:/bin:/usr/local/bin", | ||||
| 		"ALR_LOG_LEVEL=DEBUG", | ||||
| 		"XDG_SESSION_CLASS=user", | ||||
| 	) | ||||
| 	uid, gid, err := utils.GetUidGidAlrUser() | ||||
| 	if err != nil { | ||||
| 		return 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", | ||||
| 		}, | ||||
| 		SyncStderr: os.Stderr, | ||||
| 	}) | ||||
| 	rpcClient, err := client.Client() | ||||
| 	if err != nil { | ||||
| 		slog.Info("1") | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	raw1, err := rpcClient.Dispense("installer") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return raw1.(InstallerExecutor), nil | ||||
| } | ||||
							
								
								
									
										434
									
								
								pkg/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								pkg/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,434 @@ | ||||
| // 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)),               // Ограничиваем открытие файлов | ||||
| 	) | ||||
| 	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" | ||||
|  | ||||
| 	"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/types" | ||||
| 	"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>.+)$`) | ||||
|  | ||||
| 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{ | ||||
| 		Name:    fmt.Sprintf("%s+alr-%s", vars.Name, opts.Repository), | ||||
| 		Name:    fmt.Sprintf("%s+alr-%s", vars.Name, input.Repository()), | ||||
| 		Arch:    cpu.Arch(), | ||||
| 		Version: vars.Version, | ||||
| 		Release: overrides.ReleasePlatformSpecific(vars.Release, info), | ||||
| 		Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()), | ||||
| 		Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Функция getPkgFormat возвращает формат пакета из менеджера пакетов, | ||||
| // или ALR_PKG_FORMAT, если он установлен. | ||||
| func getPkgFormat(mgr manager.Manager) string { | ||||
| func GetPkgFormat(mgr manager.Manager) string { | ||||
| 	pkgFormat := mgr.Format() | ||||
| 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | ||||
| 		pkgFormat = format | ||||
| @@ -272,25 +275,9 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error { | ||||
| 	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 возвращает имена всех предоставленных пакетов. | ||||
| /* | ||||
| func packageNames(pkgs []db.Package) []string { | ||||
| 	names := make([]string, len(pkgs)) | ||||
| 	for i, p := range pkgs { | ||||
| @@ -298,6 +285,7 @@ func packageNames(pkgs []db.Package) []string { | ||||
| 	} | ||||
| 	return names | ||||
| } | ||||
| */ | ||||
|  | ||||
| // Функция removeDuplicates убирает любые дубликаты из предоставленного среза. | ||||
| func removeDuplicates(slice []string) []string { | ||||
|   | ||||
| @@ -66,6 +66,7 @@ func (a *APTRpm) Install(opts *Opts, pkgs ...string) error { | ||||
| 	cmd := a.getCmd(opts, "apt-get", "install") | ||||
| 	cmd.Args = append(cmd.Args, pkgs...) | ||||
| 	setCmdEnv(cmd) | ||||
| 	cmd.Stdout = cmd.Stderr | ||||
| 	err := cmd.Run() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("apt-get: install: %w", err) | ||||
|   | ||||
| @@ -268,6 +268,7 @@ func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Ru | ||||
| 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||
| 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||
| 		interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | ||||
| 		interp.Dir(scriptDir), | ||||
| 	) | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										15
									
								
								repo.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								repo.go
									
									
									
									
									
								
							| @@ -31,6 +31,7 @@ import ( | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	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/utils" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||
| ) | ||||
|  | ||||
| @@ -54,6 +55,8 @@ func AddRepoCmd() *cli.Command { | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			utils.ExitIfNotRoot() | ||||
|  | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			name := c.String("name") | ||||
| @@ -87,6 +90,11 @@ func AddRepoCmd() *cli.Command { | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			if utils.DropCapsToAlrUser() != nil { | ||||
| 				slog.Error(gotext.Get("Can't drop privileges")) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			db := database.New(cfg) | ||||
| 			err = db.Init(ctx) | ||||
| 			if err != nil { | ||||
| @@ -119,6 +127,8 @@ func RemoveRepoCmd() *cli.Command { | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			utils.ExitIfNotRoot() | ||||
|  | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			name := c.String("name") | ||||
| @@ -179,6 +189,11 @@ func RefreshCmd() *cli.Command { | ||||
| 		Usage:   gotext.Get("Pull all repositories that have changed"), | ||||
| 		Aliases: []string{"ref"}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			if utils.DropCapsToAlrUser() != nil { | ||||
| 				slog.Error(gotext.Get("Can't drop privileges")) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			ctx := c.Context | ||||
| 			cfg := config.New() | ||||
| 			err := cfg.Load() | ||||
|   | ||||
| @@ -27,6 +27,7 @@ import ( | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||
| 	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/search" | ||||
| ) | ||||
|  | ||||
| @@ -63,6 +64,11 @@ func SearchCmd() *cli.Command { | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			if utils.DropCapsToAlrUser() != nil { | ||||
| 				slog.Error(gotext.Get("Can't drop privileges")) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			ctx := c.Context | ||||
| 			cfg := config.New() | ||||
| 			err := cfg.Load() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user